[
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.\n\nWe are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.\n\nExamples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.\n\nThis Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\nContributions are welcome and will be fully credited!\n\nWe accept contributions via Pull Requests on [Github](https://github.com/vuejs/pinia).\n\n## Pull Requests\n\nHere are some guidelines to make the process smoother:\n\n- **Add a test** - New features and bugfixes need tests. If you find it difficult to test, please tell us in the pull request and we will try to help you!\n- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.\n- **Run `npm test` locally** - This will allow you to go faster\n- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.\n- **Send coherent history** - Make sure your commits message means something\n- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.\n\n## Creating issues\n\n### Bug reports\n\nAlways try to provide as much information as possible. If you are reporting a bug, try to provide a repro on [this playground](https://play.pinia.vuejs.org) (or anything else) or a stacktrace at the very least. This will help us check the problem quicker.\n\n### Feature requests\n\nLay out the reasoning behind it and propose an API for it. Ideally, you should have a practical example to prove the utility of the feature you're requesting.\n\n## Contributing Docs\n\nAll the documentation files can be found in `packages/docs`. It contains the English markdown files while translation(s) are stored in their corresponding `<lang>` sub-folder(s):\n\n- [`zh`](https://github.com/vuejs/pinia/tree/v2/packages/docs/zh): Chinese translation.\n\nBesides that, the `.vitepress` sub-folder contains the config and theme, including the i18n information.\n\nContributing to the English docs is the same as contributing to the source code. You can create a pull request to our GitHub repo. However, if you would like to contribute to the translations, there are two options and some extra steps to follow:\n\n### Translate in a `<lang>` sub-folder and host it on our official repo\n\nIf you want to start translating the docs in a _new_ language:\n\n1. Create the corresponding `<lang>` sub-folder for your translation.\n2. Modify the i18n configuration in the `.vitepress` sub-folder.\n3. Translate the docs and run the doc site to self-test locally.\n4. Create a checkpoint for your language by running `pnpm run docs:translation:update <lang> [<commit>]`. A checkpoint is the hash and date of the latest commit when you do the translation. The checkpoint information is stored in the status file `packages/docs/.vitepress/translation-status.json`. _It's crucial for long-term maintenance since all the further translation sync-ups are based on their previous checkpoints._\n5. Commit all the changes and create a pull request to our GitHub repo.\n\n> [!TIP]\n> When you create the checkpoint, please remember to pass the second \"commit\" argument as `v2` since that's the base branch of Pinia now.\n\nWe will have a paragraph at the top of each translation page that shows the translation status. That way, users can quickly determine if the translation is up-to-date or lags behind the English version.\n\nSpeaking of the up-to-date translation, we also need good long-term maintenance for every language. If you want to _update_ an existing translation:\n\n1. See what translation you need to sync up with the original docs. There are two popular ways:\n   1. Via the [GitHub Compare](https://github.com/vuejs/pinia/compare/) page, only see the changes in `packages/docs/*` from the checkpoint hash to `v2` branch. You can find the checkpoint hash for your language via the translation status file `packages/docs/.vitepress/translation-status.json`. The compare page can be directly opened with the hash as part of the URL, e.g. https://github.com/vuejs/pinia/compare/c67a5c9...v2\n   2. Via a local command: `pnpm run docs:translation:compare <lang> v2`.\n2. Create your own branch and start the translation update, following the previous comparison.\n3. Create a checkpoint for your language by running `pnpm run docs:translation:update <lang> v2`.\n4. Commit all the changes and create a pull request to our GitHub repo.\n\n<!-- TODO: add an example once we have got one -->\n\n> [!TIP]\n> Before you create the new checkpoint, please remember fetch the latest v2 branch ahead.\n\n### Self-host the translation\n\nYou can also host the translation on your own. To create one, fork our GitHub repo and change the content and site config in `packages/docs`. To long-term maintain it, we _highly recommend_ a similar way that we do above for our officially hosted translations:\n\n- Ensure you maintain the _checkpoint_ properly. Also, ensure the _translation status_ is well-displayed on the top of each translation page.\n- Utilize the diff result between the latest official repository and your own checkpoint to guide your translation.\n\nTip: you can add the official repo as a remote to your forked repo. This way, you can still run `pnpm run docs:translation:update <lang> [<commit>]` and `npm run docs:translation:compare <lang> [<commit>]` to get the checkpoint and diff result:\n\n```bash\n# prepare the upstream remote\ngit remote add upstream git@github.com:vuejs/pinia.git\ngit fetch upstream v2\n\n# set the checkpoint\npnpm run docs:translation:update <lang> upstream/v2\n\n# get the diff result\npnpm run docs:translation:compare <lang> upstream/v2\n```\n\n<!-- TODO: add an example once we have got one -->\n\n### Translating Search text\n\nThe search box is powered by Algolia and you will need to translate the properties. Inspire yourself from `.vitepress/config/zh.ts` `zhSearch` variable.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: \"\\U0001F41E Bug report\"\ndescription: Report an issue with Pinia\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this bug report!\n  - type: input\n    id: reproduction\n    attributes:\n      label: Reproduction\n      description: \"If possible, provide a boiled down editable reproduction using [this playground](https://play.pinia.vuejs.org), a CodeSandbox or a GitHub repository based on [this template](https://github.com/piniajs/bug-report). A failing unit test is even better! Otherwise provide as much information as possible to reproduce the problem. There are other [examples of different environments](https://github.com/piniajs?q=example&type=source) that can be used as a bug reproduction. If no reproduction is provided and the information is not enough to reproduce the problem, we won't be able to give it a look **and the issue will be converted into a question and moved to discussions**.\"\n      placeholder: Reproduction\n    validations:\n      required: true\n  - type: textarea\n    id: steps\n    attributes:\n      label: Steps to reproduce the bug\n      description: |\n        1. Click on ...\n        2. Check log\n    validations:\n      required: true\n  - type: textarea\n    id: expected-behavior\n    attributes:\n      label: Expected behavior\n      description: A clear and concise description of what you expected to happen.\n    validations:\n      required: true\n  - type: textarea\n    id: actual-behavior\n    attributes:\n      label: Actual behavior\n      description: 'A clear and concise description of what actually happens.'\n    validations:\n      required: true\n  - type: textarea\n    id: other-info\n    attributes:\n      label: Additional information\n      description: Add any other context about the problem here.\n  - type: markdown\n    attributes:\n      value: |\n        ## Before creating an issue make sure that:\n        - This hasn't been [reported before](https://github.com/vuejs/pinia/issues).\n        - The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug **with no external dependencies (e.g. vuetify)**\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: 👨‍💻 Support\n    url: https://cal.com/posva/consultancy\n    about: Get direct help from the author of Pinia with your project\n  - name: 🔧 Vue 2 Official Support\n    url: https://cal.com/posva/consultancy\n    about: Is your project still on Vue 2? Get official support\n  - name: ❓ Questions\n    url: https://github.com/vuejs/pinia/discussions/new?category=Q-A\n    about: Ask a question or discuss about Pinia\n  - name: 💡 Ideas\n    url: https://github.com/vuejs/pinia/discussions/new?category=Ideas\n    about: Start a discussion to improve Pinia\n  - name: 🌟 GitHub Sponsors\n    url: https://github.com/sponsors/posva\n    about: Like this project? Please consider supporting the author.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: \"\\U0001F680 New feature proposal\"\ndescription: Suggest an idea for Pinia\nlabels: ['feature request']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for your interest in the project and taking the time to fill out this feature report!\n  - type: textarea\n    id: feature-description\n    attributes:\n      label: What problem is this solving\n      description: 'A clear and concise description of what the problem is. Ex. when using the function X we cannot do Y.'\n    validations:\n      required: true\n  - type: textarea\n    id: proposed-solution\n    attributes:\n      label: Proposed solution\n      description: 'A clear and concise description of what you want to happen with an API proposal when applicable'\n    validations:\n      required: true\n  - type: textarea\n    id: alternative\n    attributes:\n      label: Describe alternatives you've considered\n      description: A clear and concise description of any alternative solutions or features you've considered.\n  - type: markdown\n    attributes:\n      value: |\n        ## Before creating a feature request make sure that:\n        - This hasn't been [requested before](https://github.com/vuejs/pinia/issues).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\n\nIMPORTANT: use https://github.com/vuejs/pinia/issues/new or the issue will be closed.\n\n-->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nPlease make sure to include a test! If this is closing an\nexisting issue, reference that issue as well.\n-->\n"
  },
  {
    "path": ".github/commit-convention.md",
    "content": "## Git Commit Message Convention\n\n> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).\n\n#### TL;DR:\n\nMessages must be matched by the following regex:\n\n```text\n/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\\(.+\\))?: .{1,50}/\n```\n\n#### Examples\n\nAppears under \"Features\" header, `link` subheader:\n\n```\nfeat(link): add `force` option\n```\n\nAppears under \"Bug Fixes\" header, `view` subheader, with a link to issue #28:\n\n```\nfix(view): handle keep-alive with aborted navigations\n\nclose #28\n```\n\nAppears under \"Performance Improvements\" header, and under \"Breaking Changes\" with the breaking change explanation:\n\n```\nperf: improve guard extraction\n\nBREAKING CHANGE: The 'beforeRouteEnter' option has been removed.\n```\n\nThe following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the \"Reverts\" header.\n\n```\nrevert: feat(compiler): add 'comments' option\n\nThis reverts commit 667ecc1654a317a13331b17617d973392f415f02.\n```\n\n### Full Message Format\n\nA commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:\n\n```\n<type>(<scope>): <subject>\n<BLANK LINE>\n<body>\n<BLANK LINE>\n<footer>\n```\n\nThe **header** is mandatory and the **scope** of the header is optional.\n\n### Revert\n\nIf the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.\n\n### Type\n\nIf the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.\n\nOther prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.\n\n### Scope\n\nThe scope could be anything specifying the place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...\n\n### Subject\n\nThe subject contains a succinct description of the change:\n\n- use the imperative, present tense: \"change\" not \"changed\" nor \"changes\"\n- don't capitalize the first letter\n- no dot (.) at the end\n\n### Body\n\nJust as in the **subject**, use the imperative, present tense: \"change\" not \"changed\" nor \"changes\".\nThe body should include the motivation for the change and contrast this with previous behavior.\n\n### Footer\n\nThe footer should contain any information about **Breaking Changes** and is also the place to\nreference GitHub issues that this commit **Closes**.\n\n**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.\n"
  },
  {
    "path": ".github/funding.yml",
    "content": "github: posva\ncustom: https://www.paypal.me/posva\n"
  },
  {
    "path": ".github/settings.yml",
    "content": "labels:\n  - name: bug\n    color: ee0701\n  - name: contribution welcome\n    color: 0e8a16\n  - name: discussion\n    color: 4935ad\n  - name: docs\n    color: 8be281\n  - name: enhancement\n    color: a2eeef\n  - name: good first issue\n    color: 7057ff\n  - name: help wanted\n    color: 008672\n  - name: question\n    color: d876e3\n  - name: wontfix\n    color: ffffff\n  - name: WIP\n    color: ffffff\n  - name: need repro\n    color: c9581c\n  - name: feature request\n    color: fbca04\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\n\non:\n  push:\n    paths-ignore:\n      - 'packages/docs/**'\n      - 'packages/playground/**'\n  pull_request:\n    branches: [v2, v3]\n    paths-ignore:\n      - 'packages/docs/**'\n      - 'packages/playground/**'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v6\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v6\n        with:\n          node-version: lts/*\n          cache: pnpm\n\n      - run: pnpm install --frozen-lockfile\n      - run: pnpm run lint\n      - run: pnpm run test:types\n      - run: pnpm run -r dev:prepare\n      - run: pnpm run test:vitest\n      - run: pnpm run build\n      - run: pnpm run test:dts\n      - run: pnpm run size\n\n      - uses: codecov/codecov-action@v5\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/pkg.pr.new.yml",
    "content": "name: Publish Any Commit\n\non:\n  pull_request:\n    branches: [v2, v3]\n    paths-ignore:\n      - 'packages/docs/**'\n      - 'packages/playground/**'\n\n  push:\n    branches:\n      - '**'\n    tags:\n      - '!**'\n    paths-ignore:\n      - 'packages/docs/**'\n      - 'packages/playground/**'\n\njobs:\n  build:\n    if: github.repository == 'vuejs/pinia'\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v6\n        with:\n          node-version: lts/*\n          cache: pnpm\n\n      - name: Install\n        run: pnpm install --frozen-lockfile\n\n      - name: Build\n        run: pnpm build\n\n      - name: Release\n        run: pnpm dlx pkg-pr-new publish --compact --pnpm './packages/*'\n"
  },
  {
    "path": ".github/workflows/release-tag.yml",
    "content": "on:\n  push:\n    tags:\n      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10\n\nname: Create Release\n\njobs:\n  build:\n    name: Create Release\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n      - name: Create Release for Tag\n        id: release_tag\n        uses: yyx990803/release-tag@master\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          body: |\n            Please refer to [CHANGELOG.md](https://github.com/vuejs/pinia/blob/v3/packages/pinia/CHANGELOG.md) for details.\n"
  },
  {
    "path": ".github/workflows/update-sponsors.yml",
    "content": "name: Update sponsors\n\non:\n  schedule:\n    - cron: '0 0 * * *'\n  workflow_dispatch: {}\n\npermissions:\n  contents: write\n\njobs:\n  update:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Set up pnpm\n        uses: pnpm/action-setup@v4\n\n      - name: Set up Node\n        uses: actions/setup-node@v6\n        with:\n          node-version: lts/*\n          cache: pnpm\n\n      - name: Fetch sponsor data\n        run: |\n          set -euo pipefail\n          curl -fsSL https://sponsors.esm.is/sponsors-docs.json \\\n            -o packages/docs/.vitepress/theme/components/sponsors.json\n          curl -fsSL https://sponsors.esm.is/sponsors-docs-html \\\n            -o /tmp/sponsors-docs.html\n          python - <<'PY'\n          from pathlib import Path\n\n          readme_path = Path(\"README.md\")\n          html_path = Path(\"/tmp/sponsors-docs.html\")\n\n          start_marker = \"<!--sponsors start-->\"\n          end_marker = \"<!--sponsors end-->\"\n\n          content = readme_path.read_text()\n          start_index = content.find(start_marker)\n          end_index = content.find(end_marker)\n          if start_index == -1 or end_index == -1 or end_index < start_index:\n              raise SystemExit(\"Could not find sponsors markers in README.md\")\n\n          before = content[:start_index]\n          after = content[end_index + len(end_marker):]\n          html = html_path.read_text().strip()\n\n          new_content = f\"{before}{html}{after}\"\n          if new_content != content:\n              readme_path.write_text(new_content)\n          PY\n\n      # allows oxfmt to run on commit\n      - name: Install deps\n        run: |\n          pnpm install --frozen-lockfile\n\n      - name: Format sponsor files\n        run: |\n          pnpm exec oxfmt packages/docs/.vitepress/theme/components/sponsors.json README.md\n\n      - name: Check for changes\n        id: sponsors-after\n        run: |\n          if git diff --quiet -- packages/docs/.vitepress/theme/components/sponsors.json README.md; then\n            echo \"changed=false\" >> \"$GITHUB_OUTPUT\"\n          else\n            echo \"changed=true\" >> \"$GITHUB_OUTPUT\"\n          fi\n\n      - name: Commit\n        if: steps.sponsors-after.outputs.changed == 'true'\n        run: |\n          git config user.name \"github-actions[bot]\"\n          git config user.email \"github-actions[bot]@users.noreply.github.com\"\n          git add packages/docs/.vitepress/theme/components/sponsors.json README.md\n          git commit -m \"chore: update sponsors\" --no-verify\n          git push\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ncoverage\nnpm-debug.log\nyarn-error.log\n.nyc_output\ncoverage.lcov\ndist\n.DS_Store\ntemp\ntest-dts/tsconfig.tsbuildinfo\n.env\npackages/*/LICENSE\nexplorations\ndocs-api\npackages/docs/api\n.yalc\nyalc.lock\n.idea\n.vitepress/cache\ntsconfig.vitest-temp.json\n.osgrep\n"
  },
  {
    "path": ".npmrc",
    "content": "shamefully-hoist=true\nstrict-peer-dependencies=false\n"
  },
  {
    "path": ".oxfmtrc.json",
    "content": "{\n  \"$schema\": \"./node_modules/oxfmt/configuration_schema.json\",\n  \"semi\": false,\n  \"trailingComma\": \"es5\",\n  \"singleQuote\": true,\n  \"printWidth\": 80,\n  \"ignorePatterns\": [\n    \"__build__\",\n    \"dist\",\n    \"coverage\",\n    \"packages/docs/.vitepress/cache\",\n    \"temp\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"name\": \"vscode-jest-tests\",\n      \"request\": \"launch\",\n      \"console\": \"integratedTerminal\",\n      \"internalConsoleOptions\": \"neverOpen\",\n      \"disableOptimisticBPs\": true,\n      \"cwd\": \"${workspaceFolder}\",\n      \"runtimeExecutable\": \"yarn\",\n      \"args\": [\n        \"jest\",\n        \"--watch\",\n        \"--runInBand\",\n        \"--watchAll=false\",\n        \"--collectCoverage=false\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"[javascript]\": {\n    \"editor.formatOnSave\": true\n  },\n  \"[typescript]\": {\n    \"editor.formatOnSave\": true\n  },\n  \"typescript.tsdk\": \"node_modules/typescript/lib\",\n  \"jest.jestCommandLine\": \"yarn jest --watchAll\",\n  \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2019-present Eduardo San Martin Morote\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://pinia.vuejs.org\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <img width=\"180\" src=\"https://pinia.vuejs.org/logo.svg\" alt=\"Pinia logo\">\n  </a>\n</p>\n<br/>\n<p align=\"center\">\n  <a href=\"https://npmx.dev/package/pinia\"><img src=\"https://badgen.net/npm/v/pinia/^4\" alt=\"npm package\"></a>\n  <a href=\"https://github.com/vuejs/pinia/actions/workflows/ci.yml\"><img src=\"https://github.com/vuejs/pinia/actions/workflows/ci.yml/badge.svg\" alt=\"build status\"></a>\n  <a href=\"https://codecov.io/gh/vuejs/pinia\"><img src=\"https://codecov.io/gh/vuejs/pinia/branch/v4/graph/badge.svg?token=rU2xxQ6BGH\"/></a>\n</p>\n<br/>\n\n# Pinia\n\n> Intuitive, type safe and flexible Store for Vue\n\n- 💡 Intuitive\n- 🔑 Type Safe\n- ⚙️ Devtools support\n- 🔌 Extensible\n- 🏗 Modular by design\n- 📦 Extremely light\n- ⛰️ Nuxt Module\n\nThe latest version of pinia works with Vue 3. See the branch [v2](https://github.com/vuejs/pinia/tree/v2) for a version that works with Vue 2.\n\nPinia is the most similar English pronunciation of the word _pineapple_ in Spanish: _piña_. A pineapple is in reality a group of individual flowers that join together to create a multiple fruit. Similar to stores, each one is born individually, but they are all connected at the end. It's also a delicious tropical fruit indigenous to South America.\n\n## 👉 [Demo with Vue 3 on StackBlitz](https://stackblitz.com/github/piniajs/example-vue-3-vite)\n\n## 👉 [Demo with Nuxt 3 on StackBlitz](https://stackblitz.com/github/piniajs/example-nuxt-3)\n\n## Help me keep working on this project 💚\n\n- [Become a Sponsor on GitHub](https://github.com/sponsors/posva)\n- [One-time donation via PayPal](https://paypal.me/posva)\n\n<!--sponsors start-->\n\n<h4 align=\"center\">Gold Sponsors</h4>\n<p align=\"center\">\n    <a href=\"https://www.coderabbit.ai/?utm_source=vuerouter&utm_medium=sponsor\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://posva-sponsors.pages.dev/logos/coderabbitai-dark.svg\" media=\"(prefers-color-scheme: dark)\" height=\"72px\" alt=\"CodeRabbit\" />\n      <img src=\"https://posva-sponsors.pages.dev/logos/coderabbitai-light.svg\" height=\"72px\" alt=\"CodeRabbit\" />\n    </picture>\n  </a>\n</p>\n\n<h4 align=\"center\">Silver Sponsors</h4>\n<p align=\"center\">\n    <a href=\"https://www.vuemastery.com/\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://posva-sponsors.pages.dev/logos/vuemastery-dark.png\" media=\"(prefers-color-scheme: dark)\" height=\"42px\" alt=\"VueMastery\" />\n      <img src=\"https://posva-sponsors.pages.dev/logos/vuemastery-light.svg\" height=\"42px\" alt=\"VueMastery\" />\n    </picture>\n  </a>\n    <a href=\"https://www.controla.ai/?utm_source=posva\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://posva-sponsors.pages.dev/logos/controla-dark.png\" media=\"(prefers-color-scheme: dark)\" height=\"42px\" alt=\"Controla\" />\n      <img src=\"https://posva-sponsors.pages.dev/logos/controla-light.png\" height=\"42px\" alt=\"Controla\" />\n    </picture>\n  </a>\n    <a href=\"https://route4me.com\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://posva-sponsors.pages.dev/logos/route4me.png\" media=\"(prefers-color-scheme: dark)\" height=\"42px\" alt=\"Route Optimizer and Route Planner Software\" />\n      <img src=\"https://posva-sponsors.pages.dev/logos/route4me.png\" height=\"42px\" alt=\"Route Optimizer and Route Planner Software\" />\n    </picture>\n  </a>\n    <a href=\"https://jobs.sendcloud.com\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://posva-sponsors.pages.dev/logos/sendcloud-dark.svg\" media=\"(prefers-color-scheme: dark)\" height=\"42px\" alt=\"SendCloud\" />\n      <img src=\"https://posva-sponsors.pages.dev/logos/sendcloud-light.svg\" height=\"42px\" alt=\"SendCloud\" />\n    </picture>\n  </a>\n</p>\n\n<h4 align=\"center\">Bronze Sponsors</h4>\n<p align=\"center\">\n    <a href=\"https://stormier.ninja\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://avatars.githubusercontent.com/u/2486424\" media=\"(prefers-color-scheme: dark)\" height=\"26px\" alt=\"Stanislas Ormières\" />\n      <img src=\"https://avatars.githubusercontent.com/u/2486424\" height=\"26px\" alt=\"Stanislas Ormières\" />\n    </picture>\n  </a>\n    <a href=\"https://www.rtvision.com/\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://avatars.githubusercontent.com/u/8292810\" media=\"(prefers-color-scheme: dark)\" height=\"26px\" alt=\"RTVision\" />\n      <img src=\"https://avatars.githubusercontent.com/u/8292810\" height=\"26px\" alt=\"RTVision\" />\n    </picture>\n  </a>\n    <a href=\"https://storyblok.com\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <picture>\n      <source srcset=\"https://posva-sponsors.pages.dev/logos/storyblok.png\" media=\"(prefers-color-scheme: dark)\" height=\"26px\" alt=\"Storyblok\" />\n      <img src=\"https://posva-sponsors.pages.dev/logos/storyblok.png\" height=\"26px\" alt=\"Storyblok\" />\n    </picture>\n  </a>\n</p>\n\n<!--sponsors end-->\n<!--sponsors end-->\n\n---\n\n## FAQ\n\nA few notes about the project and possible questions:\n\n**Q**: _Is Pinia the successor of Vuex?_\n\n**A**: [Yes](https://vuejs.org/guide/scaling-up/state-management.html#pinia)\n\n**Q**: _What about dynamic modules?_\n\n**A**: Dynamic modules are not type safe, so instead [we allow creating different stores](https://pinia.vuejs.org/cookbook/composing-stores.html) that can be imported anywhere\n\n## Installation\n\n```bash\n# or pnpm or yarn\nnpm install pinia\n```\n\n## Usage\n\n### Install the plugin\n\nCreate a pinia (the root store) and pass it to app:\n\n```js\n// Vue 3\nimport { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\n\nconst pinia = createPinia()\nconst app = createApp(App)\n\napp.use(pinia)\napp.mount('#app')\n```\n\nFor more detailed instructions, including [Nuxt configuration](https://pinia.vuejs.org/ssr/nuxt.html), check the [Documentation](https://pinia.vuejs.org).\n\n### Create a Store\n\nYou can create as many stores as you want, and they should each exist in different files:\n\n```ts\nimport { defineStore } from 'pinia'\n\n// main is the name of the store. It is unique across your application\n// and will appear in devtools\nexport const useMainStore = defineStore('main', {\n  // a function that returns a fresh state\n  state: () => ({\n    counter: 0,\n    name: 'Eduardo',\n  }),\n  // optional getters\n  getters: {\n    // getters receive the state as first parameter\n    doubleCounter: (state) => state.counter * 2,\n    // use getters in other getters\n    doubleCounterPlusOne(): number {\n      return this.doubleCounter + 1\n    },\n  },\n  // optional actions\n  actions: {\n    reset() {\n      // `this` is the store instance\n      this.counter = 0\n    },\n  },\n})\n```\n\n`defineStore` returns a function that has to be called to get access to the store:\n\n```ts\nimport { useMainStore } from '@/stores/main'\nimport { storeToRefs } from 'pinia'\n\nexport default defineComponent({\n  setup() {\n    const main = useMainStore()\n\n    // extract specific store properties\n    const { counter, doubleCounter } = storeToRefs(main)\n\n    return {\n      // gives access to the whole store in the template\n      main,\n      // gives access only to specific state or getter\n      counter,\n      doubleCounter,\n    }\n  },\n})\n```\n\n## Documentation\n\nTo learn more about Pinia, check [its documentation](https://pinia.vuejs.org).\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nThis is the list of versions of Pinia which are\ncurrently being supported with security updates.\n\n| Version   | Supported                                             |\n| --------- | ----------------------------------------------------- |\n| 4.0.x     | :white_check_mark:                                    |\n| 3.0.x     | :white_check_mark:                                    |\n| 2.2.x     | :x: [LTS](https://github.com/vuejs/pinia/issues/3099) |\n| &lt;2.2.0 | :x:                                                   |\n\n## Reporting a Vulnerability\n\nTo report a vulnerability, please email the details to <posva13@gmail.com>.\n\nWhen a vulnerability is reported, it immediately becomes our top concern, with a full-time contributor dropping everything to work on it.\n\nWhile discovering new vulnerabilities is rare, we recommend always using the latest versions of Vue, Pinia, and its official companion libraries to ensure your application remains as secure as possible.\n\nThanks for helping to keep Pinia secure.\n"
  },
  {
    "path": "codecov.yml",
    "content": "coverage:\n  status:\n    patch: off\n    project:\n      default:\n        threshold: 2%\n"
  },
  {
    "path": "netlify.toml",
    "content": "[build]\ncommand = \"pnpm run docs:build\"\nignore = \"./scripts/docs-check.sh\"\npublish = \"packages/docs/.vitepress/dist\"\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@pinia/root\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vitest --coverage --ui\",\n    \"release\": \"node scripts/release.mjs\",\n    \"size\": \"pnpm run -r size\",\n    \"build\": \"pnpm run -C packages/pinia build && pnpm run -C packages/nuxt build && pnpm run -C packages/testing build\",\n    \"docs\": \"pnpm run --filter ./packages/docs -r docs\",\n    \"docs:api\": \"pnpm run --filter ./packages/docs -r docs:api\",\n    \"docs:translation:compare\": \"pnpm run --filter ./packages/docs -r docs:translation:compare\",\n    \"docs:translation:update\": \"pnpm run --filter ./packages/docs -r docs:translation:update\",\n    \"docs:translation:status\": \"pnpm run --filter ./packages/docs -r docs:translation:status\",\n    \"docs:build\": \"pnpm run docs:api && pnpm run --filter ./packages/docs -r docs:build\",\n    \"docs:preview\": \"pnpm run --filter ./packages/docs -r docs:preview\",\n    \"play\": \"pnpm run -r play\",\n    \"lint\": \"oxfmt --check\",\n    \"fmt\": \"oxfmt\",\n    \"lint:fix\": \"pnpm fmt\",\n    \"test\": \"pnpm run -r dev:prepare && pnpm run test:types && pnpm run test:vitest run && pnpm run -r test && pnpm run build && pnpm test:dts\",\n    \"test:vitest\": \"vitest --coverage\",\n    \"test:types\": \"tsc --build ./tsconfig.json\",\n    \"test:dts\": \"pnpm run -r test:dts\",\n    \"postinstall\": \"simple-git-hooks\"\n  },\n  \"devDependencies\": {\n    \"@posva/prompts\": \"^2.4.4\",\n    \"@rollup/plugin-alias\": \"^6.0.0\",\n    \"@rollup/plugin-commonjs\": \"^29.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.3\",\n    \"@rollup/plugin-replace\": \"^6.0.3\",\n    \"@rollup/plugin-terser\": \"^0.4.4\",\n    \"@types/lodash.kebabcase\": \"^4.1.9\",\n    \"@types/node\": \"^24.10.0\",\n    \"@vitest/coverage-v8\": \"^4.0.18\",\n    \"@vitest/ui\": \"^4.0.18\",\n    \"@vue/compiler-sfc\": \"~3.5.22\",\n    \"@vue/server-renderer\": \"~3.5.22\",\n    \"chalk\": \"^5.6.2\",\n    \"conventional-changelog-cli\": \"^2.2.2\",\n    \"execa\": \"^9.6.0\",\n    \"globby\": \"^15.0.0\",\n    \"happy-dom\": \"^20.4.0\",\n    \"lint-staged\": \"^16.2.7\",\n    \"lodash.kebabcase\": \"^4.1.1\",\n    \"minimist\": \"^1.2.8\",\n    \"oxfmt\": \"^0.40.0\",\n    \"p-series\": \"^3.0.0\",\n    \"pascalcase\": \"^2.0.0\",\n    \"rimraf\": \"^6.1.0\",\n    \"rollup\": \"^4.52.5\",\n    \"rollup-plugin-typescript2\": \"^0.36.0\",\n    \"semver\": \"^7.7.3\",\n    \"simple-git-hooks\": \"^2.13.1\",\n    \"typedoc\": \"^0.28.14\",\n    \"typedoc-plugin-markdown\": \"~4.9.0\",\n    \"typescript\": \"~5.9.3\",\n    \"vitest\": \"^4.0.18\",\n    \"vue\": \"~3.5.22\"\n  },\n  \"simple-git-hooks\": {\n    \"pre-commit\": \"pnpm lint-staged\",\n    \"commit-msg\": \"node scripts/verifyCommit.mjs\"\n  },\n  \"lint-staged\": {\n    \"*\": \"oxfmt --no-error-on-unmatched-pattern\"\n  },\n  \"packageManager\": \"pnpm@10.32.1\",\n  \"pnpm\": {\n    \"onlyBuiltDependencies\": [\n      \"esbuild\",\n      \"simple-git-hooks\",\n      \"vue-demi\"\n    ],\n    \"ignoredBuiltDependencies\": [\n      \"@parcel/watcher\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/docs/.gitignore",
    "content": ".vitepress/cache\n"
  },
  {
    "path": "packages/docs/.vitepress/config/en.ts",
    "content": "import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'\nimport typedocSidebar from '../../api/typedoc-sidebar.json'\n\nexport const META_URL = 'https://pinia.vuejs.org'\nexport const META_TITLE = 'Pinia 🍍'\nexport const META_DESCRIPTION =\n  'Intuitive, type safe, light and flexible Store for Vue'\n\nexport const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {\n  description: META_DESCRIPTION,\n  head: [\n    ['meta', { property: 'og:url', content: META_URL }],\n    ['meta', { property: 'og:description', content: META_DESCRIPTION }],\n    ['meta', { property: 'twitter:url', content: META_URL }],\n    ['meta', { property: 'twitter:title', content: META_TITLE }],\n    ['meta', { property: 'twitter:description', content: META_DESCRIPTION }],\n  ],\n\n  themeConfig: {\n    editLink: {\n      pattern: 'https://github.com/vuejs/pinia/edit/v3/packages/docs/:path',\n      text: 'Suggest changes to this page',\n    },\n\n    nav: [\n      // { text: 'Config', link: '/config/' },\n      // { text: 'Plugins', link: '/plugins/' },\n      {\n        text: 'Guide',\n        link: '/core-concepts/',\n        activeMatch: '^/core-concepts/',\n      },\n      { text: 'API', link: '/api/', activeMatch: '^/api/' },\n      { text: 'Cookbook', link: '/cookbook/', activeMatch: '^/cookbook/' },\n      {\n        text: 'Links',\n        items: [\n          {\n            text: 'Discussions',\n            link: 'https://github.com/vuejs/pinia/discussions',\n          },\n          {\n            text: 'Changelog',\n            link: 'https://github.com/vuejs/pinia/blob/v3/packages/pinia/CHANGELOG.md',\n          },\n          {\n            text: 'Vue.js Certification',\n            link: 'https://certificates.dev/vuejs/?friend=VUEROUTER&utm_source=pinia_vuejs&utm_medium=link&utm_campaign=pinia_vuejs_links&utm_content=navbar',\n          },\n        ],\n      },\n      {\n        text: 'v3.x',\n        items: [{ text: 'v2.x', link: 'https://v2.pinia.vuejs.org' }],\n      },\n    ],\n\n    sidebar: {\n      '/api/': [\n        {\n          text: 'API',\n          items: typedocSidebar,\n        },\n      ],\n      // catch-all fallback\n      '/': [\n        {\n          text: 'Introduction',\n          items: [\n            {\n              text: 'What is Pinia?',\n              link: '/introduction.html',\n            },\n            {\n              text: 'Getting Started',\n              link: '/getting-started.html',\n            },\n          ],\n        },\n        {\n          text: 'Core Concepts',\n          items: [\n            { text: 'Defining a Store', link: '/core-concepts/' },\n            { text: 'State', link: '/core-concepts/state.html' },\n            { text: 'Getters', link: '/core-concepts/getters.html' },\n            { text: 'Actions', link: '/core-concepts/actions.html' },\n            { text: 'Plugins', link: '/core-concepts/plugins.html' },\n            {\n              text: 'Stores outside of components',\n              link: '/core-concepts/outside-component-usage.html',\n            },\n          ],\n        },\n        {\n          text: 'Server-Side Rendering (SSR)',\n          items: [\n            {\n              text: 'Vue and Vite',\n              link: '/ssr/',\n            },\n            {\n              text: 'Nuxt',\n              link: '/ssr/nuxt.html',\n            },\n          ],\n        },\n        {\n          text: 'Cookbook',\n          collapsed: false,\n          items: [\n            {\n              text: 'Index',\n              link: '/cookbook/',\n            },\n            {\n              text: 'Migration from Vuex ≤4',\n              link: '/cookbook/migration-vuex.html',\n            },\n            {\n              text: 'Hot Module Replacement',\n              link: '/cookbook/hot-module-replacement.html',\n            },\n            {\n              text: 'Testing',\n              link: '/cookbook/testing.html',\n            },\n            {\n              text: 'Usage without setup()',\n              link: '/cookbook/options-api.html',\n            },\n            {\n              text: 'Composing Stores',\n              link: '/cookbook/composing-stores.html',\n            },\n            {\n              text: 'VSCode Snippets',\n              link: '/cookbook/vscode-snippets.html',\n            },\n            {\n              text: 'Migration from v2 to v3',\n              link: '/cookbook/migration-v2-v3.html',\n            },\n            {\n              text: 'Migration from v0/v1 to v2',\n              link: '/cookbook/migration-v1-v2.html',\n            },\n            {\n              text: 'Dealing with composables',\n              link: '/cookbook/composables.html',\n            },\n          ],\n        },\n      ],\n    },\n  },\n}\n"
  },
  {
    "path": "packages/docs/.vitepress/config/index.ts",
    "content": "import { defineConfig } from 'vitepress'\nimport { enConfig } from './en'\nimport { sharedConfig } from './shared'\nimport { zhConfig } from './zh'\n\nexport default defineConfig({\n  ...sharedConfig,\n\n  locales: {\n    root: { label: 'English', lang: 'en-US', link: '/', ...enConfig },\n    zh: { label: '简体中文', lang: 'zh-CN', link: '/zh/', ...zhConfig },\n    es: {\n      label: 'Español',\n      lang: 'es-ES',\n      link: 'https://es-pinia.vercel.app/',\n    },\n    ko: {\n      label: '한국어',\n      lang: 'ko-KR',\n      link: 'https://pinia.vuejs.kr/',\n    },\n    pt: {\n      label: 'Português',\n      lang: 'pt-PT',\n      link: 'https://pinia-docs-pt.netlify.app/',\n    },\n    uk: {\n      label: 'Українська',\n      lang: 'uk-UA',\n      link: 'https://pinia-ua.netlify.app',\n    },\n    ru: {\n      label: 'Русский',\n      lang: 'ru-RU',\n      link: 'https://pinia-ru.netlify.app',\n    },\n  },\n})\n"
  },
  {
    "path": "packages/docs/.vitepress/config/shared.ts",
    "content": "import { defineConfig, HeadConfig } from 'vitepress'\nimport { zhSearch } from './zh'\n\nexport const META_IMAGE = 'https://pinia.vuejs.org/social.png'\nexport const isProduction =\n  process.env.NETLIFY && process.env.CONTEXT === 'production'\n\nif (process.env.NETLIFY) {\n  console.log('Netlify build', process.env.CONTEXT)\n}\n\nconst productionHead: HeadConfig[] = []\n\nconst rControl = /[\\u0000-\\u001f]/g\nconst rSpecial = /[\\s~`!@#$%^&*()\\-_+=[\\]{}|\\\\;:\"'“”‘’<>,.?/]+/g\nconst rCombining = /[\\u0300-\\u036F]/g\n\n/**\n * Default slugification function\n */\nexport const slugify = (str: string): string =>\n  str\n    .normalize('NFKD')\n    // Remove accents\n    .replace(rCombining, '')\n    // Remove control characters\n    .replace(rControl, '')\n    // Replace special characters\n    .replace(rSpecial, '-')\n    // ensure it doesn't start with a number\n    .replace(/^(\\d)/, '_$1')\n\nexport const sharedConfig = defineConfig({\n  title: 'Pinia',\n  appearance: 'dark',\n\n  markdown: {\n    theme: {\n      dark: 'dracula-soft',\n      light: 'vitesse-light',\n    },\n\n    attrs: {\n      leftDelimiter: '%{',\n      rightDelimiter: '}%',\n    },\n\n    anchor: {\n      slugify,\n    },\n  },\n\n  head: [\n    ['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }],\n    ['link', { rel: 'icon', type: 'image/png', href: '/logo.png' }],\n\n    [\n      'meta',\n      { name: 'wwads-cn-verify', content: '5878a7ab84fb43402106c575658472fa' },\n    ],\n\n    [\n      'meta',\n      {\n        property: 'og:type',\n        content: 'website',\n      },\n    ],\n\n    [\n      'meta',\n      {\n        property: 'twitter:card',\n        content: 'summary_large_image',\n      },\n    ],\n    [\n      'meta',\n      {\n        property: 'twitter:image',\n        content: META_IMAGE,\n      },\n    ],\n\n    [\n      'script',\n      {\n        src: 'https://cdn.usefathom.com/script.js',\n        'data-site': 'KFPPRRIS',\n        'data-spa': 'auto',\n        defer: '',\n      },\n    ],\n\n    // Vue School Top banner\n    [\n      'script',\n      {\n        src: 'https://media.bitterbrains.com/main.js?from=pinia&type=top',\n        // @ts-expect-error: vitepress bug\n        async: true,\n        type: 'text/javascript',\n      },\n    ],\n\n    ...(isProduction ? productionHead : []),\n  ],\n\n  themeConfig: {\n    logo: '/logo.svg',\n    outline: [2, 3],\n\n    socialLinks: [\n      { icon: 'x', link: 'https://twitter.com/posva' },\n      {\n        icon: 'github',\n        link: 'https://github.com/vuejs/pinia',\n      },\n      {\n        icon: 'discord',\n        link: 'https://chat.vuejs.org',\n      },\n    ],\n\n    footer: {\n      copyright: 'Copyright © 2019-present Eduardo San Martin Morote',\n      message: 'Released under the MIT License.',\n    },\n\n    editLink: {\n      pattern: 'https://github.com/vuejs/pinia/edit/v3/packages/docs/:path',\n      text: 'Suggest changes',\n    },\n\n    search: {\n      provider: 'algolia',\n      options: {\n        appId: '69Y3N7LHI2',\n        apiKey: '45441f4b65a2f80329fd45c7cb371fea',\n        indexName: 'pinia',\n        locales: { ...zhSearch },\n      },\n    },\n\n    carbonAds: {\n      code: 'CEBICK3I',\n      // custom: 'CEBICK3M',\n      placement: 'routervuejsorg',\n    },\n  },\n})\n"
  },
  {
    "path": "packages/docs/.vitepress/config/zh.ts",
    "content": "import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'\n\nexport const META_URL = 'https://pinia.vuejs.org'\nexport const META_TITLE = 'Pinia 🍍'\nexport const META_DESCRIPTION = '值得你喜欢的 Vue Store'\n// TODO: translation of this\n// 'Intuitive, type safe, light and flexible Store for Vue'\n\nexport const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {\n  description: META_DESCRIPTION,\n  head: [\n    ['meta', { property: 'og:url', content: META_URL }],\n    ['meta', { property: 'og:description', content: META_DESCRIPTION }],\n    ['meta', { property: 'twitter:url', content: META_URL }],\n    ['meta', { property: 'twitter:title', content: META_TITLE }],\n    ['meta', { property: 'twitter:description', content: META_DESCRIPTION }],\n  ],\n\n  themeConfig: {\n    editLink: {\n      pattern: 'https://github.com/vuejs/pinia/edit/v3/packages/docs/:path',\n      text: '对本页提出修改建议',\n    },\n\n    outline: {\n      label: '本页内容',\n    },\n\n    docFooter: {\n      prev: '上一页',\n      next: '下一页',\n    },\n\n    nav: [\n      // { text: 'Config', link: '/config/' },\n      // { text: 'Plugins', link: '/plugins/' },\n      {\n        text: '指南',\n        link: '/zh/core-concepts/',\n        activeMatch: '^/zh/core-concepts/',\n      },\n      { text: 'API', link: '/zh/api/', activeMatch: '^/zh/api/' },\n      { text: '手册', link: '/zh/cookbook/', activeMatch: '^/zh/cookbook/' },\n      {\n        text: '相关链接',\n        items: [\n          {\n            text: '论坛',\n            link: 'https://github.com/vuejs/pinia/discussions',\n          },\n          {\n            text: '更新日志',\n            link: 'https://github.com/vuejs/pinia/blob/v3/packages/pinia/CHANGELOG.md',\n          },\n          {\n            text: 'Vue.js 认证',\n            link: 'https://certificates.dev/vuejs/?friend=VUEROUTER&utm_source=pinia_vuejs&utm_medium=link&utm_campaign=pinia_vuejs_links&utm_content=navbar',\n          },\n        ],\n      },\n      {\n        text: 'v3.x',\n        items: [{ text: 'v2.x', link: 'https://v2.pinia.vuejs.org' }],\n      },\n    ],\n    sidebar: {\n      '/zh/api/': [\n        {\n          text: 'packages',\n          items: [\n            { text: 'pinia', link: '/zh/api/modules/pinia.html' },\n            { text: '@pinia/nuxt', link: '/zh/api/modules/pinia_nuxt.html' },\n            {\n              text: '@pinia/testing',\n              link: '/zh/api/modules/pinia_testing.html',\n            },\n          ],\n        },\n      ],\n      '/zh/': [\n        {\n          text: '介绍',\n          items: [\n            {\n              text: 'Pinia 是什么？',\n              link: '/zh/introduction.html',\n            },\n            {\n              text: '开始',\n              link: '/zh/getting-started.html',\n            },\n          ],\n        },\n        {\n          text: '核心概念',\n          items: [\n            { text: '定义 Store', link: '/zh/core-concepts/' },\n            { text: 'State', link: '/zh/core-concepts/state.html' },\n            { text: 'Getter', link: '/zh/core-concepts/getters.html' },\n            { text: 'Action', link: '/zh/core-concepts/actions.html' },\n            { text: '插件', link: '/zh/core-concepts/plugins.html' },\n            {\n              text: '组件外的 Store',\n              link: '/zh/core-concepts/outside-component-usage.html',\n            },\n          ],\n        },\n        {\n          text: '服务端渲染 (SSR)',\n          items: [\n            {\n              text: 'Vue 与 Vite',\n              link: '/zh/ssr/',\n            },\n            {\n              text: 'Nuxt',\n              link: '/zh/ssr/nuxt.html',\n            },\n          ],\n        },\n        {\n          text: '手册',\n          collapsed: false,\n          items: [\n            {\n              text: '目录',\n              link: '/zh/cookbook/',\n            },\n            {\n              text: '从 Vuex ≤4 迁移',\n              link: '/zh/cookbook/migration-vuex.html',\n            },\n            {\n              text: '热更新',\n              link: '/zh/cookbook/hot-module-replacement.html',\n            },\n            {\n              text: '测试',\n              link: '/zh/cookbook/testing.html',\n            },\n            {\n              text: '不使用 setup() 的用法',\n              link: '/zh/cookbook/options-api.html',\n            },\n            {\n              text: '组合式 Stores',\n              link: '/zh/cookbook/composing-stores.html',\n            },\n            {\n              text: 'VSCode 代码片段',\n              link: '/zh/cookbook/vscode-snippets.html',\n            },\n            {\n              text: '从 v0/v1 迁移至 v2',\n              link: '/zh/cookbook/migration-v1-v2.html',\n            },\n            {\n              text: '处理组合式函数',\n              link: '/zh/cookbook/composables.html',\n            },\n          ],\n        },\n      ],\n    },\n  },\n}\n\nexport const zhSearch: DefaultTheme.AlgoliaSearchOptions['locales'] = {\n  zh: {\n    placeholder: '搜索文档',\n    translations: {\n      button: {\n        buttonText: '搜索文档',\n        buttonAriaLabel: '搜索文档',\n      },\n      modal: {\n        searchBox: {\n          resetButtonTitle: '清除查询条件',\n          resetButtonAriaLabel: '清除查询条件',\n          cancelButtonText: '取消',\n          cancelButtonAriaLabel: '取消',\n        },\n        startScreen: {\n          recentSearchesTitle: '搜索历史',\n          noRecentSearchesText: '没有搜索历史',\n          saveRecentSearchButtonTitle: '保存至搜索历史',\n          removeRecentSearchButtonTitle: '从搜索历史中移除',\n          favoriteSearchesTitle: '收藏',\n          removeFavoriteSearchButtonTitle: '从收藏中移除',\n        },\n        errorScreen: {\n          titleText: '无法获取结果',\n          helpText: '你可能需要检查你的网络连接',\n        },\n        footer: {\n          selectText: '选择',\n          navigateText: '切换',\n          closeText: '关闭',\n          searchByText: '搜索供应商',\n        },\n        noResultsScreen: {\n          noResultsText: '无法找到相关结果',\n          suggestedQueryText: '你可以尝试查询',\n          reportMissingResultsText: '你认为该查询应该有结果？',\n          reportMissingResultsLinkText: '点击反馈',\n        },\n      },\n    },\n  },\n}\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/AsideSponsors.vue",
    "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { VPDocAsideSponsors } from 'vitepress/theme'\nimport sponsors from './sponsors.json'\n\n// to avoid the never[] type in json\ninterface Sponsor {\n  alt: string\n  href: string\n  imgSrcDark: string\n  imgSrcLight: string\n}\n\nconst asideSponsors = computed(() => {\n  return [\n    ...(sponsors.platinum.length\n      ? [\n          {\n            items: sponsors.platinum.map((sponsor: Sponsor) => ({\n              name: sponsor.alt,\n              url: sponsor.href,\n              img: sponsor.imgSrcLight,\n            })),\n          },\n        ]\n      : []),\n    {\n      size: 'mini',\n      items: sponsors.gold\n        .map((sponsor: Sponsor) => ({\n          name: sponsor.alt,\n          url: sponsor.href,\n          img: sponsor.imgSrcLight,\n        }))\n        .concat({\n          name: 'Become a sponsor',\n          url: 'https://github.com/sponsors/posva',\n          img: '/your-logo-here.svg',\n        }),\n    },\n    {\n      size: 'xmini',\n      // TODO: use gold instead once I have some\n      items: sponsors.silver.map((sponsor: Sponsor) => ({\n        name: sponsor.alt,\n        url: sponsor.href,\n        img: sponsor.imgSrcLight,\n      })),\n    },\n  ]\n})\n</script>\n\n<template>\n  <VPDocAsideSponsors :data=\"asideSponsors\" />\n\n  <!-- <a\n    class=\"banner mp\"\n    href=\"https://masteringpinia.com?utm=pinia-sidebar\"\n    target=\"_blank\"\n  >\n    <img width=\"22\" height=\"22\" src=\"/mp-pinia-logo.svg\" />\n    <span>\n      <p class=\"extra-info\">Complete guide to</p>\n      <p class=\"heading\">Mastering Pinia</p>\n      <p class=\"extra-info\">written by its creator</p>\n    </span>\n  </a> -->\n\n  <a\n    class=\"banner cert\"\n    href=\"https://certificates.dev/vuejs/?friend=VUEROUTER&utm_source=pinia_vuejs&utm_medium=link&utm_campaign=pinia_vuejs_links&utm_content=sidebar\"\n    target=\"_blank\"\n  >\n    <img width=\"22\" height=\"22\" src=\"/vue-cert-logo.svg\" />\n    <span>\n      <p class=\"extra-info\">The official</p>\n      <p class=\"heading\">Vue.js Certification</p>\n      <p class=\"extra-info\">Get certified!</p>\n    </span>\n  </a>\n</template>\n\n<style scoped>\n.VPDocAsideSponsors {\n  margin-top: 8px !important;\n}\n\n:deep(.vp-sponsor-grid.mini .vp-sponsor-grid-image) {\n  max-width: 158px;\n  max-height: 48px;\n}\n:deep(.vp-sponsor-grid.xmini .vp-sponsor-grid-image) {\n  max-width: 80px;\n  max-height: 32px;\n}\n\n.banner {\n  margin: 0.25rem 0;\n  padding: 0.4rem 0;\n  border-radius: 14px;\n  position: relative;\n  font-size: 0.9rem;\n  font-weight: 700;\n  line-height: 1.1rem;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 100%;\n  gap: 1rem;\n  background-color: var(--vp-c-bg-alt);\n  border: 2px solid var(--vp-c-bg-alt);\n  transition: border-color 0.5s;\n}\n\n.banner:last-of-type {\n  margin-bottom: 1rem;\n}\n\n.banner:hover {\n  border: 2px solid var(--vp-c-brand-1);\n}\n\n.banner.cert:hover {\n  border: 2px solid var(--vp-c-green-1);\n}\n\n.banner img {\n  transition: transform 0.5s;\n  transform: scale(1.25);\n}\n.banner:hover img {\n  transform: scale(1.75);\n}\n\n.banner .extra-info {\n  color: var(--vp-c-text-1);\n  opacity: 0;\n  font-size: 0.7rem;\n  padding-left: 0.1rem;\n  transition: opacity 0.5s;\n}\n\n.banner .heading {\n  background-image: linear-gradient(\n    120deg,\n    var(--vp-c-brand-3) 16%,\n    var(--vp-c-brand-2),\n    var(--vp-c-brand-1)\n  );\n  background-clip: text;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n}\n\n.banner.cert .heading {\n  background-image: linear-gradient(\n    120deg,\n    var(--vp-c-green-3) 16%,\n    var(--vp-c-green-2),\n    var(--vp-c-green-1)\n  );\n}\n\n.banner:hover .extra-info {\n  opacity: 0.9;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/HomeSponsors.vue",
    "content": "<script setup lang=\"ts\">\nimport HomeSponsorsGroup from './HomeSponsorsGroup.vue'\nimport sponsors from './sponsors.json'\nimport { useData } from 'vitepress'\n\nconst { site } = useData()\nconst translations = {\n  en: 'Become a sponsor',\n  'en-US': 'Become a Sponsor!',\n  'zh-CN': '成为赞助者！',\n}\n</script>\n\n<template>\n  <div class=\"sponsors_outer\">\n    <div>\n      <HomeSponsorsGroup\n        v-if=\"sponsors.platinum.length\"\n        name=\"Platinum\"\n        size=\"96\"\n      />\n\n      <HomeSponsorsGroup v-if=\"sponsors.gold.length\" name=\"Gold\" size=\"38\" />\n\n      <HomeSponsorsGroup\n        v-if=\"sponsors.silver.length\"\n        name=\"Silver\"\n        size=\"24\"\n      />\n\n      <div class=\"cta\">\n        <a class=\"become-sponsor\" href=\"https://github.com/sponsors/posva\">{{\n          translations[site.lang] || translations.en\n        }}</a>\n      </div>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.sponsors_outer {\n  text-align: center;\n  padding: 35px 40px 45px;\n  background-color: var(--vp-c-bg-accent);\n  /* transition when toggling dark mode */\n  transition:\n    background-color 300ms ease-in-out,\n    color 300ms ease-in-out;\n}\n\n.cta {\n  margin-top: 2rem;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/HomeSponsorsGroup.vue",
    "content": "<template>\n  <h3>{{ name }} Sponsors</h3>\n\n  <p>\n    <a\n      v-for=\"sponsor in list\"\n      :key=\"sponsor.href\"\n      :href=\"sponsor.href\"\n      :title=\"sponsor.alt\"\n      target=\"_blank\"\n      rel=\"sponsored noopener\"\n      class=\"sponsor_wrapper\"\n      :class=\"\n        isDark && sponsor.imgSrcLight === sponsor.imgSrcDark && 'apply-bg'\n      \"\n    >\n      <img\n        :src=\"sponsor.imgSrc\"\n        :class=\"\n          isDark &&\n          sponsor.imgSrcLight === sponsor.imgSrcDark &&\n          'invert-colors'\n        \"\n        :alt=\"sponsor.alt\"\n        :style=\"{ height: size + 'px' }\"\n      />\n    </a>\n  </p>\n</template>\n\n<script setup lang=\"ts\">\nimport sponsors from './sponsors.json'\nimport { computed } from 'vue'\nimport { useData } from 'vitepress'\n\nconst props = withDefaults(\n  defineProps<{\n    name: 'Gold' | 'Platinum' | 'Silver' | 'Bronze'\n    size?: number | string\n  }>(),\n  {\n    size: 140,\n  }\n)\n\nconst { isDark } = useData()\n\nconst list = computed(() =>\n  sponsors[props.name.toLowerCase()].map((sponsor) => ({\n    ...sponsor,\n    imgSrc: isDark.value ? sponsor.imgSrcDark : sponsor.imgSrcLight,\n  }))\n)\n</script>\n\n<style scoped>\n.sponsor_wrapper {\n  padding: 5px;\n  margin: 0 3px;\n\n  border-radius: 5px;\n\n  display: inline-block;\n  text-decoration: none;\n  vertical-align: middle;\n\n  transition: background-color 300ms ease-in-out;\n}\n\np {\n  margin: 0;\n}\n\nh3 {\n  font-size: 1.35rem;\n  font-weight: 600;\n  margin: 0.75em 0;\n}\n\nimg {\n  transition: all 0.3s ease;\n  filter: grayscale(100%);\n  opacity: 0.66;\n}\n\nhtml:not(.light) img.invert-colors {\n  filter: invert(1) grayscale(100%);\n  background-color: transparent;\n}\n\n.sponsor_wrapper.apply-bg:hover {\n  background-color: var(--c-text);\n}\n\n.sponsor_wrapper:hover img {\n  filter: none !important;\n  opacity: 1;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/MadVueBanner.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref, onMounted } from 'vue'\n\nconst isVisible = ref(false)\nconst nameStorage = 'MADVUE-BANNER-MARCH-25'\nconst target = 'https://madvue.es/?utm_source=pinia&utm_content=top_banner'\n\nfunction closeBanner() {\n  // Hide the banner\n  isVisible.value = false\n  // Save action in the local storage\n  localStorage.setItem(nameStorage, String(true))\n  document.documentElement.classList.remove('has-banner')\n}\n\nonMounted(() => {\n  if (localStorage.getItem(nameStorage) != null) {\n    return\n  }\n\n  isVisible.value = true\n  document.documentElement.classList.add('has-banner')\n\n  // const keys = Object.keys(localStorage).filter(\n  //   (key) => key.includes('FREEWEEKEND25') && key.endsWith('_CLOSED')\n  // )\n\n  // if (\n  //   keys.length > 0 &&\n  //   keys.every((key) => localStorage.getItem(key) != null)\n  // ) {\n  //   isVisible.value = true\n  //   document.documentElement.classList.add('has-banner')\n  // }\n})\n</script>\n\n<template>\n  <div class=\"banner\" v-if=\"isVisible\">\n    <a target=\"_blank\" :href=\"target\">\n      <svg\n        class=\"logo\"\n        viewBox=\"0 0 2688 734\"\n        version=\"1.1\"\n        style=\"\n          fill-rule: evenodd;\n          clip-rule: evenodd;\n          stroke-linejoin: round;\n          stroke-miterlimit: 2;\n        \"\n      >\n        <g>\n          <g>\n            <g>\n              <path\n                d=\"M537.782,722.201l-115.56,-0l0,-367.774l-100,367.774l-115.56,-0l-91.102,-375.543l-0,375.543l-115.56,-0l-0,-721.094l123.329,-0l147.786,512.218l139.996,-512.218l126.671,-0l0,721.094Z\"\n                style=\"fill: #f97844; fill-rule: nonzero\"\n              ></path>\n            </g>\n          </g>\n        </g>\n        <g>\n          <g>\n            <g>\n              <path\n                d=\"M985.548,722.201l-111.111,-0l-0,-37.783c-1.498,32.596 -33.334,48.894 -95.552,48.894c-42.968,-0 -82.226,-14.085 -117.773,-42.231c-37.782,-30.361 -56.684,-66.276 -56.684,-107.769c-0,-26.671 7.422,-53.516 22.222,-80.556c14.822,-27.04 32.965,-48.698 54.449,-64.996c25.933,-19.249 60.373,-28.884 103.342,-28.884c37.022,-0 67.035,5.186 89.996,15.56l-0,-30.013c-0,-62.218 -24.089,-93.316 -72.223,-93.316c-20.746,-0 -36.306,5.555 -46.679,16.666c-7.401,8.138 -14.801,23.698 -22.223,46.658l-109.982,0c-0,-45.92 16.298,-85.178 48.871,-117.773c32.596,-32.596 71.854,-48.893 117.795,-48.893l28.885,-0c45.92,-0 85.178,16.297 117.773,48.893c32.596,32.595 48.894,71.853 48.894,117.773l-0,357.77Zm-111.111,-154.449c-0,-19.987 -7.965,-37.023 -23.894,-51.107c-15.928,-14.063 -33.897,-21.115 -53.884,-21.115c-20.009,-0 -39.453,7.964 -58.334,23.893c-18.901,15.929 -28.342,33.528 -28.342,52.778c0,19.27 10.005,35.177 30.013,47.786c17.774,11.849 36.654,17.773 56.663,17.773c19.987,0 37.956,-6.857 53.884,-20.551c15.929,-13.715 23.894,-30.186 23.894,-49.457Z\"\n                style=\"fill: #f97844; fill-rule: nonzero\"\n              ></path>\n            </g>\n          </g>\n        </g>\n        <g>\n          <g>\n            <g>\n              <path\n                d=\"M1417.74,-0l-1.107,722.201l-106.662,-0l-0,-32.227c-14.822,28.885 -45.183,43.338 -91.103,43.338c-45.92,-0 -85.178,-16.298 -117.773,-48.894c-32.596,-32.595 -48.893,-71.853 -48.893,-117.773l-0,-202.214c-0,-45.92 16.297,-85.178 48.893,-117.773c32.595,-32.596 71.853,-48.893 117.773,-48.893l28.885,-0c20.746,-0 39.258,10.373 55.555,31.12l-1.106,-227.778l115.538,-1.107Zm-115.538,359.983c-0,-42.21 -22.223,-63.325 -66.667,-63.325c-48.156,-0 -72.222,31.098 -72.222,93.338l-0,146.658c-0,62.218 22.591,93.337 67.773,93.337c18.511,0 34.809,-6.315 48.893,-18.902c14.063,-12.586 21.094,-28.146 21.094,-46.658l1.129,-204.448Z\"\n                style=\"fill: #f97844; fill-rule: nonzero\"\n              ></path>\n            </g>\n          </g>\n        </g>\n        <g>\n          <g>\n            <g>\n              <path\n                d=\"M1845.57,1.107l-127.778,722.2l-118.88,-1.106l-123.329,-721.094l115.538,-0l67.795,488.867l71.094,-488.867l115.56,-0Z\"\n                style=\"fill: #c4d141; fill-rule: nonzero\"\n              ></path>\n            </g>\n          </g>\n        </g>\n        <g>\n          <g>\n            <g>\n              <path\n                d=\"M2262.22,723.307l-115.56,-1.106l1.129,-27.778c-17.036,20.746 -48.893,31.12 -95.551,31.12c-45.942,-0 -83.334,-16.125 -112.218,-48.351c-28.906,-32.205 -43.338,-71.658 -43.338,-118.316l1.107,-368.88l108.876,-0l1.128,343.316c0,62.218 24.067,93.337 72.222,93.337c43.685,0 65.539,-21.115 65.539,-63.324l-1.107,-373.329l116.667,-0l1.106,533.311Z\"\n                style=\"fill: #c4d141; fill-rule: nonzero\"\n              ></path>\n            </g>\n          </g>\n        </g>\n        <g>\n          <g>\n            <g>\n              <path\n                d=\"M2687.76,566.645c-0,45.92 -16.298,85.178 -48.872,117.773c-32.595,32.596 -71.853,48.894 -117.773,48.894l-28.906,-0c-45.921,-0 -85.178,-16.298 -117.774,-48.894c-32.595,-32.595 -48.871,-71.853 -48.871,-117.773l-0,-202.214c-0,-45.92 16.276,-85.178 48.871,-117.773c32.596,-32.596 71.853,-48.893 117.774,-48.893l28.906,-0c45.92,-0 85.178,16.297 117.773,48.893c32.574,32.595 48.872,71.853 48.872,117.773l-0,153.321l-251.107,-0l0,18.902c0,34.809 7.791,59.244 23.351,73.329c15.538,14.062 41.102,21.115 76.649,21.115c20.009,0 33.702,-21.484 41.124,-64.453l109.983,-0Zm-111.111,-141.102l-0,-35.547c-0,-62.24 -24.067,-93.338 -72.201,-93.338c-45.204,-0 -67.795,31.098 -67.795,93.338l0,34.44l139.996,1.107Z\"\n                style=\"fill: #c4d141; fill-rule: nonzero\"\n              ></path>\n            </g>\n          </g>\n        </g>\n      </svg>\n    </a>\n\n    <div>\n      <div class=\"headline\">\n        <a target=\"_blank\" :href=\"target\" class=\"vt-tagline\"\n          >The Vue.js Event in Madrid</a\n        >\n        <span class=\"place\"> · Spain</span>\n        <span class=\"vt-date\"> · 29 May 2025</span>\n      </div>\n\n      <div class=\"claim\">\n        <span class=\"mv-text-primary\">Discounted</span> tickets available\n        <span class=\"mv-text-primary\">Get 10% off</span>\n      </div>\n    </div>\n    <a target=\"_blank\" class=\"action\" :href=\"target\">\n      More Info\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width=\"15\"\n        height=\"15\"\n        style=\"margin-left: 10px\"\n        viewBox=\"0 0 15 15\"\n      >\n        <path\n          fill=\"currentColor\"\n          d=\"M8.293 2.293a1 1 0 0 1 1.414 0l4.5 4.5a1 1 0 0 1 0 1.414l-4.5 4.5a1 1 0 0 1-1.414-1.414L11 8.5H1.5a1 1 0 0 1 0-2H11L8.293 3.707a1 1 0 0 1 0-1.414\"\n        />\n      </svg>\n    </a>\n    <div class=\"close-btn\" @click.stop.prevent=\"closeBanner\">\n      <span class=\"close\">&times;</span>\n    </div>\n  </div>\n</template>\n\n<style>\nhtml.has-banner {\n  --vp-layout-top-height: 72px;\n}\n</style>\n\n<style scoped>\n.banner {\n  position: fixed;\n  box-sizing: border-box;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 50;\n  height: var(--vp-layout-top-height);\n  font-weight: 600;\n  color: #fff;\n  background: #0f172a;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  gap: 5px;\n}\n\n.banner .logo {\n  height: 16px;\n}\n\n.banner .headline {\n  font-size: 14px;\n}\n\n.banner .claim {\n  display: none;\n}\n\n.banner-dismissed .banner {\n  display: none;\n}\n\n.mv-text-primary {\n  color: #c4d141;\n}\n\n.banner .action {\n  display: none;\n  background: #f97844;\n  color: #fff;\n  padding: 3px 8px;\n  font-size: 13px;\n  align-items: center;\n  border-radius: 8px;\n  text-decoration: none;\n}\n\n.banner .action svg {\n  display: none;\n}\n\n.action:hover {\n  text-decoration: none;\n  background: #c4d141;\n}\n\n.close {\n  font-size: 24px;\n  line-height: 24px;\n  height: 24px;\n}\n\n.banner .close-btn {\n  top: 50%;\n  transform: translateY(-50%);\n  height: 24px;\n  left: 16px;\n  z-index: 99;\n  position: absolute;\n  cursor: pointer;\n}\n\n.banner .place {\n  display: none;\n}\n\n@media (min-width: 768px) {\n  .banner {\n    gap: 20px;\n    text-align: center;\n    flex-direction: row;\n  }\n\n  .banner .place {\n    display: inline;\n  }\n\n  .banner .action {\n    display: flex;\n  }\n\n  .banner .action svg {\n    display: block;\n  }\n\n  .banner .logo {\n    height: 20px;\n  }\n\n  .banner .headline {\n    font-size: 15px;\n  }\n\n  .banner .claim {\n    font-size: 13px;\n    display: block;\n  }\n\n  .banner .action {\n    padding: 0 10px;\n    height: 32px;\n    font-size: 13px;\n  }\n}\n\n@media (min-width: 960px) {\n  .banner {\n    gap: 40px;\n  }\n\n  .banner .logo {\n    height: 32px;\n  }\n\n  .banner .headline {\n    font-size: 20px;\n  }\n\n  .banner .claim {\n    font-size: 18px;\n    margin-top: 4px;\n  }\n\n  .banner .action {\n    padding: 0 14px;\n    height: 40px;\n    font-size: 16px;\n  }\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/MasteringPiniaLink.vue",
    "content": "<script setup lang=\"ts\">\nimport { useData } from 'vitepress'\nimport { computed, ref } from 'vue'\n\n// TODO: split into 2 components\n\nconst { site } = useData()\nconst translations = {\n  videoLink: {\n    'en-US':\n      'Master this and much more with the official video course by the author of Pinia',\n    'zh-CN': '通过 Pinia 作者的官方视频课程掌握更多内容',\n  },\n  videoEmbed: {\n    'en-US': 'Master this and much more with a free video from Mastering Pinia',\n    'zh-CN': '通过 Mastering Pinia 的免费视频掌握更多内容',\n  },\n  watchMoreA: {\n    'en-US': 'Watch more on ',\n    'zh-CN': '在 ',\n  },\n  watchMoreB: {\n    'en-US': '',\n    'zh-CN': ' 上观看更多内容',\n  },\n}\nconst props = defineProps<{ href: string; title: string; mpLink?: string }>()\nconst isVideo = computed(() => props.href.startsWith('https://play.gumlet.io/'))\nconst isVideoOpen = ref(false)\n</script>\n\n<template>\n  <div class=\"mp\">\n    <template v-if=\"isVideo\">\n      <div class=\"video-embed\" v-if=\"isVideoOpen\">\n        <div style=\"padding: 56.25% 0 0 0; position: relative\">\n          <iframe\n            loading=\"lazy\"\n            title=\"Gumlet video player\"\n            false\n            :src=\"\n              href +\n              '?preload=false&autoplay=true&loop=false&disable_player_controls=false'\n            \"\n            style=\"\n              border: none;\n              position: absolute;\n              top: 0;\n              left: 0;\n              height: 100%;\n              width: 100%;\n            \"\n            allow=\"\n              accelerometer;\n              gyroscope;\n              autoplay;\n              encrypted-media;\n              picture-in-picture;\n              fullscreen;\n            \"\n            frameborder=\"0\"\n            allowfullscreen\n          >\n          </iframe>\n        </div>\n\n        <div class=\"watch-more\">\n          {{ translations.watchMoreA[site.lang] }}\n          <a\n            :href=\"mpLink || 'https://masteringpinia.com'\"\n            target=\"_blank\"\n            rel=\"noopener\"\n          >\n            Mastering Pinia\n            <img src=\"/mp-pinia-logo.svg\" alt=\"mastering pinia logo\" />\n          </a>\n          {{ translations.watchMoreB[site.lang] }}\n        </div>\n      </div>\n\n      <button class=\"cta\" :title v-else @click=\"isVideoOpen = true\">\n        <div class=\"text\">\n          <slot>{{ translations.videoEmbed[site.lang] }}</slot>\n        </div>\n      </button>\n    </template>\n    <a\n      v-else\n      :href=\"href\"\n      target=\"_blank\"\n      rel=\"noopener\"\n      :title=\"title\"\n      class=\"no-icon cta\"\n    >\n      <div class=\"text\">\n        <slot>{{ translations.videoLink[site.lang] }}</slot>\n      </div>\n    </a>\n  </div>\n</template>\n\n<style scoped>\n.mp {\n  margin-top: 20px;\n}\n.mp:has(.cta) {\n  position: relative;\n  transition: border-color 0.5s;\n  padding: 1em 1.25em;\n  background-color: var(--vp-code-block-bg);\n  border-radius: 1em;\n  border: 2px solid var(--vp-c-bg-alt);\n}\n.mp:has(.cta):hover {\n  border: 2px solid var(--vp-c-brand-1);\n}\n.cta:hover {\n  color: var(--vp-c-brand-1);\n  text-decoration: underline;\n}\n\n.watch-more {\n  text-align: end;\n  font-size: 0.8em;\n  background-color: var(--vp-code-block-bg);\n  border-radius: 0 0 1em 1em;\n  padding-right: 1em;\n}\n\n.watch-more img {\n  height: 1em;\n  display: inline-block;\n  /* vertical-align: middle; */\n}\n\n.cta {\n  font-size: inherit;\n  color: var(--c-text);\n  position: relative;\n  display: flex;\n  gap: 1em;\n  grid-template-columns: 1fr auto;\n  align-items: center;\n  justify-content: space-between;\n  padding-left: 36px;\n}\n.cta .content {\n  flex-grow: 1;\n}\n.cta:before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 30px;\n  height: 30px;\n  top: calc(50% - 15px);\n  left: -4px;\n  border-radius: 50%;\n  background-color: var(--vp-c-brand-1);\n}\nhtml.dark .cta:after {\n  --play-icon-color: #000;\n}\n.cta:after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  top: calc(50% - 5px);\n  left: 8px;\n  border-top: 5px solid transparent;\n  border-bottom: 5px solid transparent;\n  border-left: 9px solid var(--play-icon-color, #fff);\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/PiniaLogo.vue",
    "content": "<template>\n  <svg\n    id=\"pinia-logo\"\n    viewBox=\"0 0 408 520\"\n    fill=\"none\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    style=\"z-index: 1; width: 100%\"\n    ref=\"svgEl\"\n  >\n    <g class=\"leaves\">\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M178.604 253.684C210.877 222.936 201.029 184.907 171.8 138.825C142.571 92.7423 83.7343 76.3478 72.7579 86.8057C61.7814 97.2637 59.3484 172.034 88.577 218.116C117.806 264.198 146.331 284.432 178.604 253.684Z\"\n        fill=\"url(#paint0_linear)\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M206.508 257.94C230.384 295.582 262.303 281.819 301.811 244.176C341.32 206.534 357.702 134.428 349.582 121.625C341.462 108.822 279.752 109.464 240.243 147.107C200.734 184.749 182.633 220.298 206.508 257.94Z\"\n        fill=\"url(#paint1_linear)\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M193.454 237.231C233.099 242.099 248.689 205.474 255.958 146.272C263.227 87.0698 233.262 24.497 219.778 22.8414C206.295 21.1858 160.995 74.5172 153.726 133.719C146.457 192.922 153.81 232.364 193.454 237.231Z\"\n        fill=\"url(#paint2_linear)\"\n      />\n    </g>\n\n    <g class=\"body\">\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M192.63 519.038C275.748 519.038 343.156 494.893 343.156 385.393C343.156 275.893 275.748 186.038 192.63 186.038C109.511 186.038 42.156 275.893 42.156 385.393C42.156 494.893 109.511 519.038 192.63 519.038Z\"\n        fill=\"url(#paint3_linear)\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M310.811 401.034C308.621 398.93 305.139 398.999 303.034 401.189L229.034 478.189C226.93 480.379 226.999 483.861 229.189 485.966C231.379 488.07 234.861 488.001 236.966 485.811L310.966 408.811C313.07 406.621 313.001 403.139 310.811 401.034Z\"\n        fill=\"#ECB732\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M239.233 409.993C237.02 412.073 236.912 415.554 238.993 417.767L285.993 467.767C288.073 469.98 291.554 470.088 293.767 468.007C295.98 465.927 296.088 462.446 294.007 460.233L247.007 410.233C244.927 408.02 241.446 407.912 239.233 409.993Z\"\n        fill=\"#ECB732\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M225.889 225.111C228.037 227.259 228.037 230.741 225.889 232.889L176.889 281.889C174.741 284.037 171.259 284.037 169.111 281.889C166.963 279.741 166.963 276.259 169.111 274.111L218.111 225.111C220.259 222.963 223.741 222.963 225.889 225.111Z\"\n        fill=\"#FFC73B\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M215.889 281.889C218.037 279.741 218.037 276.259 215.889 274.111L166.889 225.111C164.741 222.963 161.259 222.963 159.111 225.111C156.963 227.259 156.963 230.741 159.111 232.889L208.111 281.889C210.259 284.037 213.741 284.037 215.889 281.889Z\"\n        fill=\"#FFC73B\"\n      />\n\n      <g class=\"eye-left\">\n        <path\n          d=\"M111.34 359.471C123.125 360.918 133.225 357.648 133.898 352.166C134.571 346.684 125.563 341.067 113.777 339.62C101.992 338.173 91.8918 341.444 91.2187 346.925C90.5456 352.407 99.5541 358.024 111.34 359.471Z\"\n          fill=\"#EAADCC\"\n        />\n        <template v-if=\"!blinking || blinking === 'open'\">\n          <path\n            d=\"M150.023 321.156C149.513 335.783 137.241 347.226 122.615 346.715C107.988 346.205 96.545 333.933 97.0557 319.307C97.5665 304.68 109.838 293.237 124.464 293.748C139.091 294.258 150.534 306.53 150.023 321.156Z\"\n            fill=\"white\"\n          />\n          <g clip-path=\"url(#eye-left-mask)\">\n            <g class=\"eyeball\">\n              <path\n                d=\"M141.046 320.343C140.719 329.726 132.847 337.067 123.463 336.739C114.08 336.411 106.739 328.539 107.067 319.156C107.395 309.773 115.267 302.432 124.65 302.76C134.033 303.087 141.374 310.959 141.046 320.343Z\"\n                fill=\"black\"\n              />\n              <path\n                d=\"M125.161 316.786C125.026 320.65 121.784 323.672 117.921 323.537C114.057 323.403 111.034 320.161 111.169 316.297C111.304 312.434 114.546 309.411 118.409 309.546C122.273 309.681 125.296 312.922 125.161 316.786Z\"\n                fill=\"white\"\n              />\n            </g>\n          </g>\n        </template>\n        <path\n          fill-rule=\"evenodd\"\n          v-else\n          clip-rule=\"evenodd\"\n          d=\"M98.4768 310.811C97.1028 311.737 96.7396 313.602 97.6656 314.976C102.329 321.896 109.474 325.785 118.663 326.835C127.921 327.893 136.204 325.529 143.318 319.748C144.603 318.703 144.799 316.813 143.754 315.527C142.709 314.242 140.819 314.046 139.534 315.091C133.705 319.828 127.041 321.754 119.344 320.874C111.579 319.987 106.157 316.839 102.641 311.623C101.715 310.249 99.8508 309.885 98.4768 310.811Z\"\n          fill=\"#755400\"\n        />\n      </g>\n\n      <g class=\"eye-right\">\n        <path\n          d=\"M263.558 365.546C275.433 365.546 285.058 361.069 285.058 355.546C285.058 350.023 275.433 345.546 263.558 345.546C251.684 345.546 242.058 350.023 242.058 355.546C242.058 361.069 251.684 365.546 263.558 365.546Z\"\n          fill=\"#EAADCC\"\n        />\n\n        <template v-if=\"!blinking || blinking === 'open'\">\n          <path\n            d=\"M279.944 325.693C279.433 340.32 267.162 351.763 252.536 351.252C237.909 350.742 226.466 338.47 226.977 323.844C227.487 309.217 239.759 297.774 254.385 298.285C269.012 298.795 280.455 311.067 279.944 325.693Z\"\n            fill=\"white\"\n          />\n          <g clip-path=\"url(#eye-right-mask)\">\n            <g class=\"eyeball\">\n              <path\n                d=\"M270.967 324.879C270.64 334.263 262.767 341.604 253.384 341.276C244.001 340.948 236.66 333.076 236.988 323.693C237.316 314.31 245.188 306.969 254.571 307.297C263.954 307.624 271.295 315.496 270.967 324.879Z\"\n                fill=\"black\"\n              />\n              <path\n                d=\"M255.082 321.323C254.947 325.187 251.705 328.209 247.842 328.074C243.978 327.939 240.955 324.698 241.09 320.834C241.225 316.971 244.467 313.948 248.33 314.083C252.194 314.218 255.217 317.459 255.082 321.323Z\"\n                fill=\"white\"\n              />\n            </g>\n          </g>\n        </template>\n        <path\n          fill-rule=\"evenodd\"\n          v-else\n          clip-rule=\"evenodd\"\n          d=\"M231.477 319.811C230.103 320.737 229.74 322.602 230.666 323.976C235.329 330.896 242.474 334.785 251.663 335.835C260.921 336.893 269.204 334.529 276.318 328.748C277.603 327.703 277.799 325.813 276.754 324.527C275.709 323.242 273.819 323.046 272.534 324.091C266.705 328.828 260.041 330.754 252.344 329.874C244.579 328.987 239.157 325.839 235.641 320.623C234.715 319.249 232.851 318.885 231.477 319.811Z\"\n          fill=\"#755400\"\n        />\n      </g>\n\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M70.1889 401.034C72.379 398.93 75.8608 398.999 77.9656 401.189L151.966 478.189C154.07 480.379 154.001 483.861 151.811 485.966C149.621 488.07 146.139 488.001 144.034 485.811L70.0344 408.811C67.9296 406.621 67.9988 403.139 70.1889 401.034Z\"\n        fill=\"#ECB732\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M141.767 409.993C143.98 412.073 144.088 415.554 142.007 417.767L95.0074 467.767C92.927 469.98 89.4462 470.088 87.233 468.007C85.0197 465.927 84.9121 462.446 86.9925 460.233L133.993 410.233C136.073 408.02 139.554 407.912 141.767 409.993Z\"\n        fill=\"#ECB732\"\n      />\n      <g class=\"mouth\">\n        <path\n          class=\"smile\"\n          fill-rule=\"evenodd\"\n          clip-rule=\"evenodd\"\n          d=\"M163.323 337.658C161.949 338.584 161.586 340.448 162.512 341.822C167.176 348.743 174.321 352.632 183.51 353.682C192.767 354.74 201.051 352.375 208.164 346.594C209.45 345.549 209.645 343.66 208.6 342.374C207.555 341.088 205.666 340.893 204.38 341.938C198.552 346.675 191.887 348.6 184.191 347.721C176.425 346.834 171.003 343.686 167.488 338.469C166.562 337.095 164.697 336.732 163.323 337.658Z\"\n          fill=\"black\"\n        />\n        <path\n          class=\"open\"\n          d=\"M213.046 343.089C213.046 356.089 199.012 367.537 186.862 367.537C174.712 367.537 164.177 356.078 164.177 343.078C164.177 335.899 175.857 332.075 188.008 332.075C200.158 332.075 213.046 335.909 213.046 343.089Z\"\n          fill=\"#E77777\"\n        />\n      </g>\n    </g>\n\n    <defs>\n      <linearGradient\n        id=\"paint0_linear\"\n        x1=\"68.5172\"\n        y1=\"90.0774\"\n        x2=\"85.0979\"\n        y2=\"170.543\"\n        gradientUnits=\"userSpaceOnUse\"\n      >\n        <stop stop-color=\"#52CE63\" />\n        <stop offset=\"1\" stop-color=\"#51A256\" />\n      </linearGradient>\n      <linearGradient\n        id=\"paint1_linear\"\n        x1=\"359.841\"\n        y1=\"134.702\"\n        x2=\"279.366\"\n        y2=\"151.265\"\n        gradientUnits=\"userSpaceOnUse\"\n      >\n        <stop stop-color=\"#52CE63\" />\n        <stop offset=\"1\" stop-color=\"#51A256\" />\n      </linearGradient>\n      <linearGradient\n        id=\"paint2_linear\"\n        x1=\"219.235\"\n        y1=\"22.7747\"\n        x2=\"203.754\"\n        y2=\"148.86\"\n        gradientUnits=\"userSpaceOnUse\"\n      >\n        <stop stop-color=\"#8AE99C\" />\n        <stop offset=\"1\" stop-color=\"#52CE63\" />\n      </linearGradient>\n      <linearGradient\n        id=\"paint3_linear\"\n        x1=\"196.803\"\n        y1=\"244.222\"\n        x2=\"171.815\"\n        y2=\"518.625\"\n        gradientUnits=\"userSpaceOnUse\"\n      >\n        <stop stop-color=\"#FFE56C\" />\n        <stop offset=\"1\" stop-color=\"#FFC63A\" />\n      </linearGradient>\n\n      <clipPath id=\"eye-right-mask\">\n        <circle cy=\"325px\" cx=\"254\" r=\"27\" />\n      </clipPath>\n      <clipPath id=\"eye-left-mask\">\n        <circle cy=\"320\" cx=\"124\" r=\"27\" />\n      </clipPath>\n    </defs>\n  </svg>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'\nimport { useSpring } from 'vue-use-spring'\nimport {\n  useMouse,\n  useEventListener,\n  useDebounceFn,\n  useIdle,\n  whenever,\n} from '@vueuse/core'\n\nconst { x: mouseX, y: mouseY } = useMouse()\nconst mousePos = useSpring(\n  reactive({\n    x: mouseX,\n    y: mouseY,\n  }),\n  {\n    mass: 1,\n    tension: 120,\n    friction: 34,\n    precision: 1,\n  }\n)\n\nconst { idle } = useIdle(3000)\n\n// make it like it's looking at the user when they are idle\nwhenever(idle, () => {\n  const l = leftEyeCenter.value\n  const r = rightEyeCenter.value\n  mousePos.x = l.x + (r.x - l.x) / 2\n  mousePos.y = r.y\n})\n\nconst svgEl = ref<SVGElement>()\nconst leftEyeCenter = ref({ x: 0, y: 0 })\nconst rightEyeCenter = ref({ x: 0, y: 0 })\n\nfunction computedEyesCenter() {\n  const svg = svgEl.value\n  if (svg) {\n    const leftEye = svg.querySelector<SVGElement>('.eye-left .eyeball')!\n    const leftEyeRect = leftEye.getBoundingClientRect()\n    leftEyeCenter.value = {\n      x: leftEyeRect.x + leftEyeRect.width / 2,\n      y: leftEyeRect.y + leftEyeRect.height / 2,\n    }\n\n    const rightEye = svg.querySelector<SVGElement>('.eye-right .eyeball')!\n    const rightEyeRect = rightEye.getBoundingClientRect()\n    rightEyeCenter.value = {\n      x: rightEyeRect.x + rightEyeRect.width / 2,\n      y: rightEyeRect.y + rightEyeRect.height / 2,\n    }\n  }\n}\n\nuseEventListener('resize', useDebounceFn(computedEyesCenter, 750))\n\nconst leftEyeHorizontalDistance = computed(() => {\n  return Math.min(1, Math.max(-1, (mousePos.x - leftEyeCenter.value.x) / 150))\n})\nconst rightEyeHorizontalDistance = computed(() => {\n  return Math.min(1, Math.max(-1, (mousePos.x - rightEyeCenter.value.x) / 150))\n})\nconst eyeVerticalDistance = computed(() => {\n  return Math.min(1, Math.max(-1, (mousePos.y - leftEyeCenter.value.y) / 100))\n})\n\nconst blinking = ref<'open' | 'closed'>('open')\n\nconst blinkTimer = 100\n// use a randomized blink interval for a more natural look\nconst minBlinkInterval = 2000\nconst maxBlinkInterval = 10000\n\nonMounted(() => {\n  nextTick(() => {\n    computedEyesCenter()\n    idle.value = true\n  })\n\n  let timerId = setInterval(\n    () => {\n      let blinkState = 0\n      function blinkHandler() {\n        blinkState++\n\n        if (blinkState % 2) {\n          blinking.value = 'closed'\n          setTimeout(blinkHandler, blinkTimer * 1.7)\n        } else if (blinkState < 4) {\n          blinking.value = 'open'\n          setTimeout(blinkHandler, blinkTimer)\n        } else {\n          blinking.value = 'open'\n        }\n      }\n      setTimeout(blinkHandler, 0)\n    },\n    (maxBlinkInterval - minBlinkInterval) / 2\n  )\n\n  onUnmounted(() => {\n    clearInterval(timerId)\n  })\n})\n</script>\n\n<style scoped>\n.eye-left .eyeball {\n  transform: translate(\n    calc(12px * v-bind(leftEyeHorizontalDistance)),\n    calc(10px * v-bind(eyeVerticalDistance))\n  );\n}\n\n.eye-right .eyeball {\n  transform: translate(\n    calc(12px * v-bind(rightEyeHorizontalDistance)),\n    calc(10px * v-bind(eyeVerticalDistance))\n  );\n}\n\n#pinia-logo .mouth .open {\n  visibility: hidden;\n}\n#pinia-logo:hover .mouth .open {\n  visibility: unset;\n}\n#pinia-logo:hover .mouth .smile {\n  visibility: hidden;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/RuleKitLink.vue",
    "content": "<script setup lang=\"ts\">\nimport { useData } from 'vitepress'\n\nconst { site } = useData()\n\nconst translations = {\n  'en-US': 'Vibe code Vue apps with confidence',\n  'zh-CN': '自信地编写 Vue 应用的 Vibe 代码',\n}\n</script>\n\n<template>\n  <div class=\"rulekit\">\n    <a\n      href=\"https://rulekit.dev?from=pinia\"\n      target=\"_blank\"\n      rel=\"sponsored noopener\"\n      aria-label=\"Visit RuleKit - curated rules for Cursor, Claude Code, and more (opens in new tab)\"\n      class=\"rulekit-link\"\n    >\n      <div class=\"rulekit-content\">\n        <div class=\"rulekit-left\">\n          <span class=\"rulekit-sparkles\" aria-hidden=\"true\">✨</span>\n          <span class=\"rulekit-text\">\n            <slot>{{ translations[site.lang] || translations['en-US'] }}</slot>\n          </span>\n        </div>\n        <div class=\"rulekit-right\">\n          <svg\n            width=\"1024\"\n            height=\"1024\"\n            viewBox=\"0 0 1024 1024\"\n            fill=\"none\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            role=\"img\"\n            alt=\"RuleKit logo\"\n            class=\"rulekit-logo\"\n          >\n            <path\n              d=\"M335 694L353.947 697.225V737L335 733.775V694Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              d=\"M355 538.234V673L335 669.766V536.078L355 538.234Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              d=\"M259.255 329.5L313.424 338.585V414.78L374.576 425.512V350.39L422.5 359.457L422.529 391.816L399.251 388.374V443.756L375.649 440.537V460.314L313.424 449.576V430.878L287.675 426.585L286.602 367.561L259.255 364V329.5Z\"\n              fill=\"currentColor\"\n            />\n            <path\n              d=\"M398.897 113.5L647.031 158.597V199L676.034 205.5V276L836.085 304.623L896.239 362.605V498.968L873.681 516.147V778.5L914.5 792.095L755.522 914.5L180.84 802.832L130.354 738.409V472.125L103.5 457.092V336.835L164.728 270.264L291.48 205.5L321.557 211V176.85L350.56 165.5V139L398.897 113.5ZM379.562 148.933V184L350.56 182.218L349.485 259.527L379.562 263.822V184L582.581 218.725V300.328L622.325 307V224.094L598.693 220.873V188.661L379.562 148.933ZM433.271 218.725V234.831L561.097 257.379V240.2L433.271 218.725ZM291.48 234.831L223.807 272.412L740.484 364.752L802.786 325.024L673.885 302.476L622.325 330L561.097 319V282L433.271 259.527L389.23 285.296L321.557 275V240.2L291.48 234.831ZM180.84 288.517L128.206 343.277V440.537L233.475 461.387L232.401 360.457L259.255 364V446L284.103 449.576V476.42L311.889 480.714V750L373.117 762L374.191 691.165V493.599L399.251 497V472.125L422.5 476.42L422.529 391.816L449.383 396.964V500L543.911 516V414.143L571.839 418.438V388.374L624.474 396.964V423.807L652.402 428V534.5L778.08 551.5V443.756L731 392.244L180.84 288.517ZM831.789 336.835L757.671 383.005L796.341 423.807L871.533 374.416L831.789 336.835ZM807.083 445.281V529.032L871.533 486.083V404.48L807.083 445.281ZM571.839 449.576V521.516L587 524.5V592L606.213 595.603V527.5L624.474 531V458.5L571.839 449.576ZM155.06 472.125V729.5L191.5 778.5L713 880.5L754.448 853.298V579.497L635.215 560.17V601L682.479 609.562V649.29L663.144 647V682.575L682.479 684.723L683.5 814.5L652.402 808.201V839L543.911 819.5V788.874L511.685 783.505V654.5L532.095 658.5V623.52L511.685 620.299V578.424L557.875 585.94V545.138L422.5 521.516L402.12 538.234V654.658V766.326L373.117 762V791.021L311.889 778.5V750L285.035 744.851V512.926L264.5 493.599L155.06 472.125ZM778.08 579.497L780.228 833.971L847.901 781.358V538.234L778.08 579.497ZM561.097 627.5V664.5L543.911 661V788.874L565.394 793.169L567 669L584.729 672V632.5L561.097 627.5ZM611.583 639.626V676.5L628.77 679V802.832L652.402 808.201V682.575L635.215 680.428V643L611.583 639.626Z\"\n              fill=\"currentColor\"\n            />\n          </svg>\n          <span class=\"rulekit-title\">RuleKit</span>\n        </div>\n      </div>\n    </a>\n  </div>\n</template>\n\n<style scoped>\n.rulekit {\n  margin: 1rem 0;\n}\n\n.rulekit-title {\n  font-family:\n    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n    'Liberation Mono', 'Courier New', monospace;\n}\n\n.rulekit-link {\n  display: block;\n  padding: 1rem 1.5rem;\n  border-radius: 12px;\n  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\n  border: 1px solid #dee2e6;\n  text-decoration: none;\n  color: #212529;\n  transition: all 0.3s ease;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n  position: relative;\n  overflow: hidden;\n}\n\n.rulekit-link:visited {\n  color: #212529;\n}\n\n.rulekit-link:focus {\n  outline: 2px solid #0066cc;\n  outline-offset: 2px;\n}\n\n.rulekit-link:focus:not(:focus-visible) {\n  outline: none;\n}\n\n.rulekit-link::before {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: -100%;\n  width: 100%;\n  height: 100%;\n  background: linear-gradient(\n    90deg,\n    transparent,\n    rgba(255, 255, 255, 0.4),\n    transparent\n  );\n  transition: left 0.6s ease;\n}\n\n.rulekit-link:hover::before {\n  left: 100%;\n}\n\n.rulekit-link:hover {\n  text-decoration: none !important;\n  transform: translateY(-2px);\n  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n  background: linear-gradient(135deg, #f1f3f4 0%, #e2e6ea 100%);\n}\n\n.rulekit-content {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 1rem;\n  position: relative;\n  z-index: 2;\n}\n\n.rulekit-left {\n  display: flex;\n  align-items: center;\n  gap: 1rem;\n}\n\n.rulekit-right {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n\n.rulekit-sparkles {\n  font-size: 1.5rem;\n}\n\n.rulekit-logo {\n  width: 2rem;\n  height: 2rem;\n  flex-shrink: 0;\n  color: #495057;\n}\n\n.rulekit-text {\n  font-weight: 500;\n  font-family:\n    'Spectral', ui-serif, system-ui, sans-serif, 'Apple Color Emoji',\n    'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';\n  font-size: 0.95rem;\n  line-height: 1.4;\n}\n\n/* Dark mode styles */\n.dark .rulekit-link {\n  background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);\n  border-color: #404040;\n  color: #e5e5e5;\n}\n\n.dark .rulekit-link::before {\n  background: linear-gradient(\n    90deg,\n    transparent,\n    rgba(255, 255, 255, 0.15),\n    transparent\n  );\n}\n\n.dark .rulekit-link:hover {\n  text-decoration: none !important;\n  background: linear-gradient(135deg, #262626 0%, #363636 100%);\n  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n}\n\n.dark .rulekit-logo {\n  color: #ffffff;\n}\n\n.dark .rulekit-text {\n  color: #e5e5e5;\n}\n\n.dark .rulekit-link:visited {\n  color: #e5e5e5;\n}\n\n.dark .rulekit-link:focus {\n  outline-color: #66b3ff;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/VueMasteryBanner.vue",
    "content": "<template>\n  <div class=\"vuemastery-banner-wrapper\" role=\"banner\" v-if=\"isVisible\">\n    <div\n      :class=\"{ 'show-flash': showFlash }\"\n      class=\"vuemastery-background-dim\"\n      ref=\"vuemastery-banner-flash\"\n    ></div>\n    <a id=\"vm-banner\" href=\"https://www.vuemastery.com/holiday\" target=\"_blank\">\n      <img\n        id=\"vm-logo-full\"\n        src=\"/vuemastery/vuemastery-white.svg\"\n        alt=\"vuemastery\"\n      />\n      <img\n        id=\"vm-logo-small\"\n        src=\"https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2Fvue-mastery-logo-small.png?alt=media&token=941fcc3a-2b6f-40e9-b4c8-56b3890da108\"\n        alt=\"vuemastery\"\n      />\n      <div class=\"vm-banner-wrapper\">\n        <div class=\"vm-banner-content\">\n          <h1 class=\"vm-banner-title\">Learn Vue with Evan You</h1>\n          <p class=\"vm-banner-sub\">Get 60% off a year of Vue courses</p>\n        </div>\n        <button id=\"vm-banner-cta\">Unlock your discount</button>\n      </div>\n      <button id=\"vm-banner-close\" @click.prevent=\"closeBanner\">\n        <span class=\"close\">&times;</span>\n      </button>\n    </a>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted } from 'vue'\n\nconst isVisible = ref(false)\nconst showFlash = ref(false)\nconst nameStorage = 'VUEMASTERY-BANNER-DECEMBER-2023'\n\nconst closeBanner = () => {\n  // Hide the banner\n  isVisible.value = false\n  // Save action in the local storage\n  localStorage.setItem(nameStorage, String(true))\n  document.documentElement.classList.remove('vuemastery-menu-fixed')\n}\n\nonMounted(() => {\n  isVisible.value = !localStorage.getItem(nameStorage)\n  if (isVisible.value) {\n    document.documentElement.classList.add('vuemastery-menu-fixed')\n    setTimeout(() => {\n      showFlash.value = true\n    }, 2000)\n  }\n})\n</script>\n<style scoped>\n.vuemastery-banner-wrapper {\n  position: fixed;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  z-index: 61;\n  width: 100%;\n  height: 100%;\n  max-height: 70px;\n  background: linear-gradient(45deg, #0a2b4e, #835ec2);\n  background-size: 110%;\n  background-position: 50% 50%;\n  overflow: hidden;\n  padding: 12px;\n  margin: 0;\n  transition: background-size 0.25s cubic-bezier(0.39, 0.575, 0.565, 1);\n}\n\n.vuemastery-banner-wrapper:hover {\n  background-size: 100%;\n}\n\n.vuemastery-banner-wrapper:before {\n  content: '';\n  background: url(/vuemastery/background-bubbles-vuemastery.svg) left center\n    no-repeat;\n  background-size: cover;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  transition: all 0.3s ease-out 0.1s;\n  transform: scale(1.1);\n  width: 100%;\n  height: 100%;\n}\n.vuemastery-banner-wrapper:after {\n  content: '';\n  background: url(/vuemastery/lock-vuemastery.svg) right center no-repeat;\n  background-size: auto 100%;\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n  pointer-events: none;\n}\n\n.vuemastery-banner-wrapper:hover:after {\n  background-image: url(/vuemastery/unlock-vuemastery.svg);\n}\n\n#vm-banner {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  text-decoration: none;\n  color: white;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  overflow: hidden;\n}\n\n#vm-logo-full {\n  position: absolute;\n  left: 15px;\n  width: 120px;\n}\n\n#vm-logo-small {\n  display: none;\n}\n\n.vm-banner-wrapper {\n  display: flex;\n  align-items: center;\n}\n\n.vm-banner-content {\n  display: flex;\n}\n\n.vm-banner-title {\n  margin: 0;\n  padding: 0;\n  font-weight: bold;\n  font-size: 24px;\n  text-align: center;\n  background: linear-gradient(145deg, #c3ffac, #86ec87, #38a56a);\n  background-clip: text;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n}\n\n.vm-banner-sub {\n  margin: 0 2em;\n  padding: 0;\n  font-size: 16px;\n  text-align: center;\n  color: #fff;\n}\n\n#vm-banner-cta {\n  position: relative;\n  margin-left: 10px;\n  padding: 10px 24px;\n  background: linear-gradient(to top right, #41b782, #86d169);\n  border: none;\n  border-radius: 30px;\n  color: #fff;\n  font-size: 12px;\n  font-weight: bold;\n  text-decoration: none;\n  text-transform: uppercase;\n}\n\n#vm-banner-cta:hover {\n  background: linear-gradient(to bottom right, #41b782, #86d169);\n}\n\n#vm-banner-close {\n  position: absolute;\n  right: 12px;\n  color: #fff;\n  font-size: 20px;\n  font-weight: bold;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n#vm-banner-close > .close {\n  font-size: 20px;\n  font-weight: 600;\n}\n\n@media (max-width: 1200px) {\n  #vm-banner-cta {\n    display: none;\n  }\n\n  .vm-banner-content {\n    flex-direction: column;\n  }\n\n  .vm-banner-sub {\n    margin: 0 1em;\n  }\n}\n\n@media (max-width: 850px) {\n  .vuemastery-banner-wrapper:after {\n    background: none;\n  }\n}\n@media (max-width: 767px) {\n  #vm-logo-full {\n    left: 10px;\n    width: 100px;\n  }\n}\n@media (max-width: 767px) {\n  #vm-logo-full {\n    display: none;\n  }\n  #vm-logo-small {\n    position: absolute;\n    display: block;\n    left: 10px;\n    width: 40px;\n  }\n  .vm-banner-title {\n    font-size: 14px;\n  }\n  .vm-banner-sub {\n    font-size: 12px;\n    margin: 0;\n  }\n}\n\n.vuemastery-background-dim {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  left: 0;\n}\n.vuemastery-background-dim:after {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: -100%;\n  width: 100%;\n  height: 100%;\n  background: linear-gradient(\n    90deg,\n    transparent,\n    rgba(255, 255, 255, 0.4),\n    transparent\n  );\n  transition: 0.5s;\n  transition-delay: 0.5s;\n}\n.vuemastery-background-dim.show-flash:after {\n  left: 100%;\n}\n</style>\n\n<style>\nhtml.vuemastery-menu-fixed {\n  --vp-layout-top-height: 70px;\n}\nhtml.vuemastery-menu-fixed .VPNav,\nhtml.vuemastery-menu-fixed .VPSidebar {\n  top: 70px;\n}\nhtml.vuemastery-menu-fixed {\n  scroll-padding-top: 134px;\n  overflow: auto;\n}\nhtml.vuemastery-menu-fixed {\n  margin-top: 72px;\n}\n@media (max-width: 960px) {\n  html.vuemastery-menu-fixed .VPNav,\n  html.vuemastery-menu-fixed .VPSidebar {\n    top: 0;\n  }\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/VueMasteryHomeLink.vue",
    "content": "<script setup lang=\"ts\"></script>\n\n<template>\n  <div class=\"container\">\n    <div class=\"inside\">\n      <a\n        href=\"https://www.vuemastery.com/pinia?coupon=PINIA-DOCS&via=eduardo\"\n        target=\"_blank\"\n      >\n        <span class=\"logo-wrapper\">\n          <img\n            alt=\"Vue Mastery Logo\"\n            width=\"25px\"\n            src=\"https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2Fvue-mastery-logo-small.png?alt=media&token=941fcc3a-2b6f-40e9-b4c8-56b3890da108\"\n          />\n        </span>\n        <span class=\"description\">\n          Get the <span class=\"highlight\">Pinia Cheat Sheet</span> from Vue\n          Mastery\n        </span>\n      </a>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.container {\n  text-align: center;\n  margin-top: 30px;\n}\n.inside {\n  width: 960px;\n  border-bottom: 1px solid var(--c-divider);\n  padding-bottom: 50px;\n  margin: 0 auto;\n}\na {\n  background-color: var(--c-bg-accent);\n  border-radius: 8px;\n  padding: 8px 16px 8px 8px;\n}\n.description {\n  line-height: 20px;\n  color: var(--c-text);\n  margin-left: 10px;\n  transition: color 0.5s;\n}\na:hover {\n  text-decoration: none !important;\n}\na:hover .highlight {\n  text-decoration: underline;\n}\n.highlight {\n  color: var(--c-brand);\n}\n@media (max-width: 960px) {\n  .inside {\n    width: 100%;\n  }\n}\n@media (max-width: 420px) {\n  a {\n    display: block;\n    margin-left: 10px;\n    margin-right: 10px;\n  }\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/VueMasteryLogoLink.vue",
    "content": "<script setup lang=\"ts\">\nconst props = defineProps<{\n  for: string\n}>()\n\nconst links = {\n  'pinia-cheat-sheet':\n    'https://www.vuemastery.com/pinia?coupon=PINIA-DOCS&via=eduardo',\n}\n\nconst link = links[props.for]\nconst forPiniaCheatSheet = props.for === 'pinia-cheat-sheet'\n</script>\n\n<template>\n  <a :href=\"link\" target=\"_blank\">\n    <span class=\"logo-wrapper\">\n      <img\n        alt=\"Vue Mastery Logo\"\n        width=\"25px\"\n        src=\"https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2Fvue-mastery-logo-small.png?alt=media&token=941fcc3a-2b6f-40e9-b4c8-56b3890da108\"\n      />\n    </span>\n    <span v-if=\"forPiniaCheatSheet\" class=\"description\">\n      Get the <span class=\"highlight\">Pinia Cheat Sheet</span> from Vue Mastery\n    </span>\n  </a>\n</template>\n\n<style scoped>\na {\n  background-color: var(--c-bg-accent);\n  border-radius: 8px;\n  padding: 8px 16px 8px 8px;\n  display: flex;\n  align-items: center;\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n.description {\n  flex: 1;\n  line-height: 20px;\n  color: var(--c-text);\n  margin: 0 0 0 12px;\n  transition: color 0.5s;\n}\na:hover {\n  text-decoration: none !important;\n}\na:hover .highlight {\n  text-decoration: underline;\n}\n.highlight {\n  color: var(--c-brand);\n}\n.logo-wrapper {\n  position: relative;\n  width: 48px;\n  height: 48px;\n  border-radius: 50%;\n  background-color: white;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  margin-left: 5px;\n}\n.logo-wrapper img {\n  width: 25px;\n  object-fit: contain;\n}\n@media (max-width: 576px) {\n  .description {\n    font-size: 12px;\n    line-height: 18px;\n  }\n  .logo-wrapper {\n    position: relative;\n    width: 32px;\n    height: 32px;\n  }\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/VueSchoolLink.vue",
    "content": "<template>\n  <div class=\"vueschool\">\n    <a\n      :href=\"`${href}?friend=vuerouter`\"\n      target=\"_blank\"\n      rel=\"sponsored noopener\"\n      :title=\"title\"\n      class=\"no-icon\"\n    >\n      <slot>{{ translations[site.lang] }}</slot>\n    </a>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useData } from 'vitepress'\n\nconst { site } = useData()\nconst translations = {\n  'en-US': 'Watch a free video lesson on Vue School',\n  'zh-CN': '在 Vue School 上观看免费视频课程',\n}\ndefineProps<{ href: string; title: string }>()\n</script>\n\n<style scoped>\n.vueschool {\n  margin-top: 20px;\n  background-color: var(--vp-code-block-bg);\n  padding: 1em 1.25em;\n  border-radius: 2px;\n  position: relative;\n  display: flex;\n}\n.vueschool a {\n  color: var(--c-text);\n  position: relative;\n  padding-left: 36px;\n}\n.vueschool a:before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 30px;\n  height: 30px;\n  top: calc(50% - 15px);\n  left: -4px;\n  border-radius: 50%;\n  background-color: #73abfe;\n}\n.vueschool a:after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  top: calc(50% - 5px);\n  left: 8px;\n  border-top: 5px solid transparent;\n  border-bottom: 5px solid transparent;\n  border-left: 8px solid #fff;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/VuejsdeConfBanner.vue",
    "content": "<template>\n  <div class=\"banner banner-vuejsconf\" v-if=\"isVisible\">\n    <a\n      href=\"https://conf.vuejs.de/tickets/?voucher=COMMUNITY&utm_source=vuejs&utm_medium=referral&utm_campaign=banner-placement&utm_content=banner\"\n      target=\"_blank\"\n    >\n      <picture>\n        <source\n          media=\"(min-width:1260px)\"\n          srcset=\"\n            /vuejsde-conf/vuejsdeconf_banner_large.png,\n            /vuejsde-conf/vuejsdeconf_banner_large_2x.png 2x\n          \"\n        />\n        <source\n          media=\"(min-width:970px)\"\n          srcset=\"\n            /vuejsde-conf/vuejsdeconf_banner_medium.png,\n            /vuejsde-conf/vuejsdeconf_banner_medium_2x.png 2x\n          \"\n        />\n        <source\n          media=\"(min-width:576px)\"\n          srcset=\"\n            /vuejsde-conf/vuejsdeconf_banner_small.png,\n            /vuejsde-conf/vuejsdeconf_banner_small_2x.png 2x\n          \"\n        />\n        <source\n          media=\"(min-width:320px)\"\n          srcset=\"\n            /vuejsde-conf/vuejsdeconf_banner_smallest.png,\n            /vuejsde-conf/vuejsdeconf_banner_smallest_2x.png 2x\n          \"\n          alt=\"\"\n        />\n        <img src=\"/vuejsde-conf/vuejsdeconf_banner_smallest_2x.png\" alt=\"\" />\n      </picture>\n    </a>\n    <div class=\"close-btn\" @click.stop.prevent=\"closeBanner\">\n      <span class=\"close\">&times;</span>\n    </div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted } from 'vue'\n\nconst isVisible = ref(false)\n\nconst nameStorage = 'VUEJSDECONF-BANNER-SEPTEMBER-2024'\n\nconst resetLayoutTopHeight = () => {\n  document.documentElement.classList.add('banner-dismissed')\n}\n\nconst closeBanner = () => {\n  // Hide the banner\n  isVisible.value = false\n  // Save action in the local storage\n  localStorage.setItem(nameStorage, String(true))\n  resetLayoutTopHeight()\n}\n\nonMounted(() => {\n  isVisible.value = !localStorage.getItem(nameStorage)\n\n  if (!isVisible.value) {\n    resetLayoutTopHeight()\n  }\n})\n</script>\n<style>\nhtml:not(.banner-dismissed) {\n  --vp-layout-top-height: 72px;\n}\n</style>\n<style scoped>\n.banner {\n  position: fixed;\n  z-index: var(--vp-z-index-layout-top);\n  box-sizing: border-box;\n  top: 0;\n  left: 0;\n  right: 0;\n  height: var(--vp-layout-top-height);\n  line-height: 0;\n  text-align: center;\n  font-size: 12px;\n  font-weight: 600;\n  color: #000;\n}\n\n.banner-dismissed .banner {\n  display: none;\n}\n\na {\n  text-decoration: underline;\n}\n\n.close {\n  font-size: 24px;\n}\n\n.banner-vuejsconf {\n  background: linear-gradient(90deg, #fff 50%, #6f97c4 50%);\n}\n\n.banner-vuejsconf a {\n  display: inline-block;\n  margin: 0 auto;\n}\n\n.banner-vuejsconf .close-btn {\n  top: 0;\n  left: 0;\n  z-index: 99;\n  position: absolute;\n  border-radius: 50%;\n  padding: 10px;\n  cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/components/sponsors.json",
    "content": "{\n  \"platinum\": [],\n  \"gold\": [\n    {\n      \"alt\": \"CodeRabbit\",\n      \"href\": \"https://www.coderabbit.ai/?utm_source=vuerouter&utm_medium=sponsor\",\n      \"imgSrcDark\": \"https://posva-sponsors.pages.dev/logos/coderabbitai-dark.svg\",\n      \"imgSrcLight\": \"https://posva-sponsors.pages.dev/logos/coderabbitai-light.svg\"\n    }\n  ],\n  \"silver\": [\n    {\n      \"alt\": \"VueMastery\",\n      \"href\": \"https://www.vuemastery.com/\",\n      \"imgSrcDark\": \"https://posva-sponsors.pages.dev/logos/vuemastery-dark.png\",\n      \"imgSrcLight\": \"https://posva-sponsors.pages.dev/logos/vuemastery-light.svg\"\n    },\n    {\n      \"alt\": \"Controla\",\n      \"href\": \"https://www.controla.ai/?utm_source=posva\",\n      \"imgSrcDark\": \"https://posva-sponsors.pages.dev/logos/controla-dark.png\",\n      \"imgSrcLight\": \"https://posva-sponsors.pages.dev/logos/controla-light.png\"\n    },\n    {\n      \"alt\": \"Route Optimizer and Route Planner Software\",\n      \"href\": \"https://route4me.com\",\n      \"imgSrcDark\": \"https://posva-sponsors.pages.dev/logos/route4me.png\",\n      \"imgSrcLight\": \"https://posva-sponsors.pages.dev/logos/route4me.png\"\n    },\n    {\n      \"alt\": \"SendCloud\",\n      \"href\": \"https://jobs.sendcloud.com\",\n      \"imgSrcDark\": \"https://posva-sponsors.pages.dev/logos/sendcloud-dark.svg\",\n      \"imgSrcLight\": \"https://posva-sponsors.pages.dev/logos/sendcloud-light.svg\"\n    }\n  ],\n  \"bronze\": [\n    {\n      \"alt\": \"Stanislas Ormières\",\n      \"href\": \"https://stormier.ninja\",\n      \"imgSrcDark\": \"https://avatars.githubusercontent.com/u/2486424\",\n      \"imgSrcLight\": \"https://avatars.githubusercontent.com/u/2486424\"\n    },\n    {\n      \"alt\": \"RTVision\",\n      \"href\": \"https://www.rtvision.com/\",\n      \"imgSrcDark\": \"https://avatars.githubusercontent.com/u/8292810\",\n      \"imgSrcLight\": \"https://avatars.githubusercontent.com/u/8292810\"\n    },\n    {\n      \"alt\": \"Storyblok\",\n      \"href\": \"https://storyblok.com\",\n      \"imgSrcDark\": \"https://posva-sponsors.pages.dev/logos/storyblok.png\",\n      \"imgSrcLight\": \"https://posva-sponsors.pages.dev/logos/storyblok.png\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/index.ts",
    "content": "import { h } from 'vue'\nimport { type Theme } from 'vitepress'\nimport DefaultTheme from 'vitepress/theme'\nimport AsideSponsors from './components/AsideSponsors.vue'\n// import AsideSponsors from './components/AsideSponsors.vue'\nimport TranslationStatus from 'vitepress-translation-helper/ui/TranslationStatus.vue'\n// import HomeSponsors from './components/HomeSponsors.vue'\nimport PiniaLogo from './components/PiniaLogo.vue'\nimport './styles/vars.css'\nimport './styles/playground-links.css'\nimport VueSchoolLink from './components/VueSchoolLink.vue'\nimport VueMasteryLogoLink from './components/VueMasteryLogoLink.vue'\nimport MasteringPiniaLink from './components/MasteringPiniaLink.vue'\nimport RuleKitLink from './components/RuleKitLink.vue'\nimport status from '../translation-status.json'\n\nconst i18nLabels = {\n  zh: '该翻译已同步到了 ${date} 的版本，其对应的 commit hash 是 <code>${hash}</code>。',\n}\n\nconst theme: Theme = {\n  ...DefaultTheme,\n  Layout() {\n    return h(DefaultTheme.Layout, null, {\n      'home-hero-image': () => h('div', { class: 'image-src' }, h(PiniaLogo)),\n      // 'home-features-after': () => h(HomeSponsors),\n      'aside-ads-before': () => h(AsideSponsors),\n      // 'layout-top': () => h(VuejsdeConfBanner),\n      'doc-before': () => h(TranslationStatus, { status, i18nLabels }),\n    })\n  },\n\n  enhanceApp({ app }) {\n    app.component('VueSchoolLink', VueSchoolLink)\n    app.component('VueMasteryLogoLink', VueMasteryLogoLink)\n    app.component('MasteringPiniaLink', MasteringPiniaLink)\n    app.component('RuleKitLink', RuleKitLink)\n  },\n}\n\nexport default theme\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/styles/home-links.css",
    "content": "/* Style to get the cheat sheet link in the home page */\n\na.cta {\n  text-align: center;\n  border-radius: 8px;\n}\n\na.cta:hover {\n  border-color: var(--vp-c-brand);\n  background-color: var(--c-bg-accent);\n}\n\na.cta.vue-mastery::before {\n  content: '';\n  display: inline-block;\n  width: 25px;\n  height: 25px;\n  background-image: url('https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2Fvue-mastery-logo-small.png?alt=media&token=941fcc3a-2b6f-40e9-b4c8-56b3890da108');\n  background-size: 25px;\n  background-repeat: no-repeat;\n  background-position: bottom;\n  margin-right: 0.5em;\n}\n\na.cta.mastering-pinia {\n  height: 100%;\n  line-height: 100%;\n  display: flex;\n  justify-content: center;\n  white-space: pre;\n  min-height: 41px;\n  position: relative;\n  background-color: var(--c-black);\n}\na.cta.mastering-pinia::before {\n  content: '';\n  width: 156px;\n  height: 100%;\n  display: inline-block;\n  background-image: url('/mp-logo.svg');\n  background-size: 156px;\n  background-repeat: no-repeat;\n  vertical-align: bottom;\n  line-height: normal;\n  transform: translateY(6px);\n}\na.cta.mastering-pinia:hover::after {\n  animation: none;\n}\na.cta.mastering-pinia::after {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  /* background-color: var(--vp-button-brand-border); */\n  border: 1px solid var(--vp-button-brand-border);\n  border-radius: 20px;\n  animation: ping 3s cubic-bezier(0, 0, 0.2, 1) infinite;\n  z-index: -1;\n}\n\n@keyframes ping {\n  15%,\n  to {\n    transform: scale(1.25, 2);\n    opacity: 0;\n  }\n}\n\na.cta.vueschool {\n  position: relative;\n  color: currentColor;\n  padding-left: 38px !important;\n}\na.cta.vueschool::before {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 20px;\n  height: 20px;\n  top: calc(50% - 10px);\n  left: 12px;\n  border-radius: 50%;\n  border: 1px solid currentColor;\n}\n\na.cta.vueschool::after {\n  content: '';\n  position: absolute;\n  display: block;\n  width: 0;\n  height: 0;\n  top: calc(50% - 4px);\n  left: 20px;\n  border-top: 4px solid transparent;\n  border-bottom: 4px solid transparent;\n  border-left: 7px solid currentColor;\n}\n\na.cta.rulekit {\n  font-family:\n    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n    'Liberation Mono', 'Courier New', monospace;\n}\n\na.cta.rulekit::before {\n  content: '';\n  display: inline-block;\n  width: 16px;\n  height: 16px;\n  background-image: url('/rulekit-logo.svg');\n  background-size: 16px;\n  background-repeat: no-repeat;\n  background-position: center;\n  margin-right: 0.5em;\n  vertical-align: middle;\n  filter: brightness(0); /* Make it black by default */\n}\n\nhtml.dark a.cta.rulekit::before {\n  filter: brightness(0) invert(1); /* Make it white in dark mode */\n}\n\n@media (max-width: 420px) {\n  a.cta.cta.vue-mastery {\n    max-width: 320px;\n    line-height: 1.5em;\n    white-space: normal;\n    display: block;\n    padding-bottom: 10px;\n    margin-left: 8px;\n    margin-right: 8px;\n  }\n}\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/styles/playground-links.css",
    "content": ".vp-doc a[href^='https://play.pinia.vuejs.org']:before {\n  content: '▶';\n  width: 20px;\n  height: 20px;\n  display: inline-block;\n  border-radius: 10px;\n  vertical-align: middle;\n  position: relative;\n  top: -2px;\n  border: 2px solid;\n  margin-right: 8px;\n  margin-left: 4px;\n  line-height: 15px;\n  padding-left: 4.5px;\n  font-size: 11px;\n  font-family: system-ui, BlinkMacSystemFont, sans-serif;\n}\n"
  },
  {
    "path": "packages/docs/.vitepress/theme/styles/vars.css",
    "content": "/**\n * Colors\n * -------------------------------------------------------------------------- */\n\n:root {\n  --c-yellow-1: #ffd859;\n  --c-yellow-2: #f7d336;\n  --c-yellow-3: #dec96e;\n  --c-yellow-soft-1: #ecb732;\n  --c-yellow-soft-2: #c99513;\n\n  --c-teal: #086367;\n  --c-teal-light: #33898d;\n\n  --c-white-dark: #f8f8f8;\n  --c-black-darker: #0d121b;\n  --c-black: #111827;\n  --c-black-light: #161f32;\n  --c-black-lighter: #262a44;\n\n  --c-green-1: #52ce63;\n  --c-green-2: #8ae99c;\n  --c-green-3: #51a256;\n  --c-green-soft: #316334;\n\n  /* light theme is a bit different */\n  --vp-c-brand-1: var(--vp-c-green-1);\n  --vp-c-brand-2: var(--vp-c-green-2);\n  --vp-c-brand-3: var(--vp-c-green-3);\n  --vp-c-brand-soft: var(--vp-c-green-soft);\n\n  --c-text-dark-1: #d9e6eb;\n  --c-text-dark-2: #c4dde6;\n  --c-text-dark-3: #abc4cc;\n  --c-text-light-1: #2c3e50;\n  --c-text-light-2: #476582;\n  --c-text-light-3: #90a4b7;\n\n  --vp-c-brand-dark: var(--c-green-soft);\n  --vp-c-brand-darker: var(--c-green-soft);\n  --vp-c-brand-dimm: rgba(100, 108, 255, 0.08);\n  --vp-c-brand-text: var(--c-text-light-1);\n  --c-bg-accent: var(--c-white-dark);\n  --code-bg-color: var(--c-white-dark);\n  --code-inline-bg-color: var(--c-white-dark);\n  --code-font-family:\n    'dm', source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;\n  --code-font-size: 16px;\n\n  --vp-code-block-bg: var(--vp-c-bg-alt);\n  --vp-code-line-highlight-color: rgba(0, 0, 0, 0.075);\n  --vp-code-color: var(--vp-text-color);\n}\n\nhtml.dark:root {\n  /* --c-black: #ffffff;\n  --c-white: #000000; */\n  /* --c-divider-light: rgba(60, 60, 67, 0.12);\n  --c-divider-dark: rgba(84, 84, 88, 0.48); */\n  /* --c-brand-light: var(--c-yellow-light); */\n\n  --vp-c-brand-1: var(--c-yellow-1);\n  --vp-c-brand-2: var(--c-yellow-2);\n  --vp-c-brand-3: var(--c-yellow-3);\n\n  --vp-c-bg-alpha-with-backdrop: rgba(20, 25, 36, 0.7);\n  --vp-c-bg-alpha-without-backdrop: rgba(20, 25, 36, 0.9);\n\n  --vp-code-line-highlight-color: rgba(0, 0, 0, 0.5);\n\n  --vp-c-text-1: var(--c-text-dark-1);\n  --vp-c-brand-text: var(--c-text-light-1);\n  --c-text-light: var(--c-text-dark-2);\n  --c-text-lighter: var(--c-text-dark-3);\n  --c-divider: var(--c-divider-dark);\n  --c-bg-accent: var(--c-black-light);\n  /* --vp-code-inline-bg: var(--vp-c-black-light); */\n\n  --vp-c-bg: var(--c-black);\n  --vp-c-bg-soft: var(--c-black-light);\n  --vp-c-bg-soft-up: var(--c-black-lighter);\n  --vp-c-bg-mute: var(--c-black-light);\n  --vp-c-bg-soft-mute: var(--c-black-lighter);\n  --vp-c-bg-alt: #0d121b;\n  --vp-c-bg-elv: var(--vp-c-bg-soft);\n  --vp-c-bg-elv-mute: var(--vp-c-bg-soft-mute);\n  --vp-c-mute: var(--vp-c-bg-mute);\n  --vp-c-mute-dark: var(--c-black-lighter);\n  --vp-c-mute-darker: var(--c-black-darker);\n\n  --vp-home-hero-name-background: -webkit-linear-gradient(\n    78deg,\n    var(--c-yellow-2) 30%,\n    var(--c-green-2)\n  );\n}\n\nhtml.dark .DocSearch {\n  --docsearch-hit-active-color: var(--c-text-light-1);\n}\n\n/**\n * Component: Button\n * -------------------------------------------------------------------------- */\n\n:root {\n  --vp-button-brand-border: var(--c-yellow-soft-1);\n  --vp-button-brand-text: var(--c-black);\n  --vp-button-brand-bg: var(--c-yellow-1);\n  --vp-button-brand-hover-border: var(--c-yellow-2);\n  --vp-button-brand-hover-text: var(--c-black-darker);\n  --vp-button-brand-hover-bg: var(--c-yellow-2);\n  --vp-button-brand-active-border: var(--c-yellow-soft-1);\n  --vp-button-brand-active-text: var(--c-black-darker);\n  --vp-button-brand-active-bg: var(--vp-button-brand-bg);\n}\n\n/**\n * Component: Home\n * -------------------------------------------------------------------------- */\n\n:root {\n  --vp-home-hero-name-color: transparent;\n  --vp-home-hero-name-background: linear-gradient(\n    292deg,\n    var(--c-green-1) 50%,\n    var(--c-green-2)\n  );\n  --vp-home-hero-image-background-image: linear-gradient(\n    15deg,\n    var(--c-yellow-2) 65%,\n    var(--c-green-1) 30%\n  );\n  --vp-home-hero-image-filter: blur(40px);\n}\n\n.VPHero .VPImage.image-src {\n  max-height: 192px;\n}\n\n@media (min-width: 640px) {\n  :root {\n    --vp-home-hero-image-filter: blur(56px);\n  }\n  .VPHero .VPImage.image-src {\n    max-height: 256px;\n  }\n}\n\n@media (min-width: 960px) {\n  :root {\n    --vp-home-hero-image-filter: blur(72px);\n  }\n  .VPHero .VPImage.image-src {\n    max-height: 320px;\n  }\n}\n\n.become-sponsor {\n  font-size: 0.9em;\n  font-weight: 700;\n  width: auto;\n  text-align: center;\n  background-color: transparent;\n  padding: 0.75em 2em;\n  border-radius: 2em;\n  transition: all 0.15s ease;\n  box-sizing: border-box;\n  border: 2px solid var(--c-yellow-2);\n}\n\n.become-sponsor.become-sponsor:hover {\n  background-color: var(--c-yellow-1);\n  text-decoration: none;\n  border-color: var(--c-yellow-1);\n  color: var(--c-text-light-1);\n}\n\n.vp-doc a {\n  text-decoration: none;\n}\n.vp-doc a:hover {\n  text-decoration: underline;\n}\n\n.sponsors-top .become-sponsor {\n  font-size: 0.75em;\n  padding: 0.2em;\n  width: auto;\n  max-width: 150px;\n}\n\nkbd {\n  display: inline-block;\n  padding: 3px 5px;\n  font-size: 0.65em;\n  color: var(--vp-c-text-1);\n  vertical-align: middle;\n  background-color: var(--vp-c-bg-mute);\n  border: solid 1px var(--vp-c-bg-soft-mute);\n  border-radius: 6px;\n  box-shadow: inset 0 -1px 0 var(--vp-c-bg-soft-mute);\n  line-height: 0.95em;\n}\n"
  },
  {
    "path": "packages/docs/.vitepress/translation-status.json",
    "content": "{\n  \"zh\": {\n    \"hash\": \"02a476d\",\n    \"date\": \"2024-05-20\"\n  }\n}\n"
  },
  {
    "path": "packages/docs/cookbook/composables.md",
    "content": "# Dealing with Composables\n\n[Composables](https://vuejs.org/guide/reusability/composables.html#composables) are functions that leverage Vue Composition API to encapsulate and reuse stateful logic. Whether you write your own, you use [external libraries](https://vueuse.org/) or do both, you can fully use the power of Composables in your pinia stores.\n\n## Option Stores\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/using-composables-in-option-stores\"\n  title=\"Using Composables in Option Stores\"\n/>\n\nWhen defining an option store, you can call a composable inside of the `state` property:\n\n```ts\nexport const useAuthStore = defineStore('auth', {\n  state: () => ({\n    user: useLocalStorage('pinia/auth/login', 'bob'),\n  }),\n})\n```\n\nKeep in mind that **you can only return writable state** (e.g. a `ref()`). Here are some examples of composables that you can use:\n\n<RuleKitLink />\n\n- [useLocalStorage](https://vueuse.org/core/useLocalStorage/)\n- [useAsyncState](https://vueuse.org/core/useAsyncState/)\n\nHere are some examples of composables that cannot be used in an option stores (but can be used with setup stores):\n\n- [useMediaControls](https://vueuse.org/core/useMediaControls/): exposes functions\n- [useMemoryInfo](https://vueuse.org/core/useMemory/): exposes readonly data\n- [useEyeDropper](https://vueuse.org/core/useEyeDropper/): exposes readonly data and functions\n\n## Setup Stores\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/using-composables-in-setup-stores\"\n  title=\"Using Composables in Setup Stores\"\n/>\n\nOn the other hand, when defining a setup store, you can use almost any composable since every property gets discerned into state, action, or getter:\n\n```ts\nimport { defineStore } from 'pinia'\nimport { useMediaControls } from '@vueuse/core'\n\nexport const useVideoPlayer = defineStore('video', () => {\n  // we won't expose (return) this element directly\n  const videoElement = ref<HTMLVideoElement>()\n  const src = ref('/data/video.mp4')\n  const { playing, volume, currentTime, togglePictureInPicture } =\n    useMediaControls(videoElement, { src })\n\n  function loadVideo(element: HTMLVideoElement, src: string) {\n    videoElement.value = element\n    src.value = src\n  }\n\n  return {\n    src,\n    playing,\n    volume,\n    currentTime,\n\n    loadVideo,\n    togglePictureInPicture,\n  }\n})\n```\n\n:::warning\nDifferently from regular state, `ref<HTMLVideoElement>()` contains a non-serializable reference to the DOM element. This is why we don't return it directly. Since it's client-only state, we know it won't be set on the server and will **always** start as `undefined` on the client.\n:::\n\n## SSR\n\nWhen dealing with [Server Side Rendering](../ssr/index.md), you need to take care of some extra steps in order to use composables within your stores.\n\nIn [Option Stores](#option-stores), you need to define a `hydrate()` function. This function is called when the store is instantiated on the client (the browser) when there is an initial state available at the time the store is created. The reason we need to define this function is because in such scenario, `state()` is not called.\n\n```ts\nimport { defineStore } from 'pinia'\nimport { useLocalStorage } from '@vueuse/core'\n\nexport const useAuthStore = defineStore('auth', {\n  state: () => ({\n    user: useLocalStorage('pinia/auth/login', 'bob'),\n  }),\n\n  hydrate(state, initialState) {\n    // in this case we can completely ignore the initial state since we\n    // want to read the value from the browser\n    state.user = useLocalStorage('pinia/auth/login', 'bob')\n  },\n})\n```\n\nIn [Setup Stores](#setup-stores), you need to use a helper named `skipHydrate()` on any state property that shouldn't be picked up from the initial state. Differently from option stores, setup stores cannot just _skip calling `state()`_, so we mark properties that cannot be hydrated with `skipHydrate()`. Note that this only applies to state properties:\n\n```ts\nimport { defineStore, skipHydrate } from 'pinia'\nimport { useEyeDropper, useLocalStorage } from '@vueuse/core'\n\nexport const useColorStore = defineStore('colors', () => {\n  const { isSupported, open, sRGBHex } = useEyeDropper()\n  const lastColor = useLocalStorage('lastColor', sRGBHex)\n  // ...\n  return {\n    lastColor: skipHydrate(lastColor), // Ref<string>\n    open, // Function\n    isSupported, // boolean (not even reactive)\n  }\n})\n```\n"
  },
  {
    "path": "packages/docs/cookbook/composing-stores.md",
    "content": "# Composing Stores\n\n<RuleKitLink />\n\nComposing stores is about having stores that use each other, and this is supported in Pinia. There is one rule to follow:\n\nIf **two or more stores use each other**, they cannot create an infinite loop through _getters_ or _actions_. They cannot **both** directly read each other's state in their setup function:\n\n```js\nconst useX = defineStore('x', () => {\n  const y = useY()\n\n  // ❌ This is not possible because y also tries to read x.name\n  y.name\n\n  function doSomething() {\n    // ✅ Read y properties in computed or actions\n    const yName = y.name\n    // ...\n  }\n\n  return {\n    name: ref('I am X'),\n  }\n})\n\nconst useY = defineStore('y', () => {\n  const x = useX()\n\n  // ❌ This is not possible because x also tries to read y.name\n  x.name\n\n  function doSomething() {\n    // ✅ Read x properties in computed or actions\n    const xName = x.name\n    // ...\n  }\n\n  return {\n    name: ref('I am Y'),\n  }\n})\n```\n\n## Nested Stores\n\nNote that if one store uses another store, you can directly import and call the `useStore()` function within _actions_ and _getters_. Then you can interact with the store just like you would from within a Vue component. See [Shared Getters](#Shared-Getters) and [Shared Actions](#Shared-Actions).\n\nWhen it comes to _setup stores_, you can simply use one of the stores **at the top** of the store function:\n\n```ts\nimport { defineStore } from 'pinia'\nimport { useUserStore } from './user'\nimport { apiPurchase } from './api'\n\nexport const useCartStore = defineStore('cart', () => {\n  const user = useUserStore()\n  const list = ref([])\n\n  const summary = computed(() => {\n    return `Hi ${user.name}, you have ${list.value.length} items in your cart. It costs ${price.value}.`\n  })\n\n  function purchase() {\n    return apiPurchase(user.id, list.value)\n  }\n\n  return { list, summary, purchase }\n})\n```\n\n## Shared Getters\n\nYou can simply call `useUserStore()` inside a _getter_:\n\n```js\nimport { defineStore } from 'pinia'\nimport { useUserStore } from './user'\n\nexport const useCartStore = defineStore('cart', {\n  getters: {\n    summary(state) {\n      const user = useUserStore()\n\n      return `Hi ${user.name}, you have ${state.list.length} items in your cart. It costs ${state.price}.`\n    },\n  },\n})\n```\n\n## Shared Actions\n\nThe same applies to _actions_:\n\n```js\nimport { defineStore } from 'pinia'\nimport { useUserStore } from './user'\nimport { apiOrderCart } from './api'\n\nexport const useCartStore = defineStore('cart', {\n  actions: {\n    async orderCart() {\n      const user = useUserStore()\n\n      try {\n        await apiOrderCart(user.token, this.items)\n        // another action\n        this.emptyCart()\n      } catch (err) {\n        displayError(err)\n      }\n    },\n  },\n})\n```\n\nSince actions can be asynchronous, make sure **all of your `useStore()` calls appear before any `await`**. Otherwise, this could lead to using the wrong pinia instance _in SSR apps_:\n\n```js{7-8,11-13}\nimport { defineStore } from 'pinia'\nimport { useUserStore } from './user'\nimport { apiOrderCart } from './api'\n\nexport const useCartStore = defineStore('cart', {\n  actions: {\n    async orderCart() {\n      // ✅ call at the top of the action before any `await`\n      const user = useUserStore()\n\n      try {\n        await apiOrderCart(user.token, this.items)\n        // ❌ called after an `await` statement\n        const otherStore = useOtherStore()\n        // another action\n        this.emptyCart()\n      } catch (err) {\n        displayError(err)\n      }\n    },\n  },\n})\n```\n"
  },
  {
    "path": "packages/docs/cookbook/hot-module-replacement.md",
    "content": "# HMR (Hot Module Replacement)\n\n<RuleKitLink />\n\nPinia supports Hot Module replacement so you can edit your stores and interact with them directly in your app without reloading the page, allowing you to keep the existing state, add, or even remove state, actions, and getters.\n\nAt the moment, only [Vite](https://vitejs.dev/guide/api-hmr.html#hmr-api) is officially supported but any bundler implementing the `import.meta.hot` spec should work (e.g. [webpack](https://webpack.js.org/api/module-variables/#importmetawebpackhot) seems to use `import.meta.webpackHot` instead of `import.meta.hot`).\nYou need to add this snippet of code next to any store declaration. Let's say you have three stores: `auth.js`, `cart.js`, and `chat.js`, you will have to add (and adapt) this after the creation of the _store definition_:\n\n```js\n// auth.js\nimport { defineStore, acceptHMRUpdate } from 'pinia'\n\nexport const useAuth = defineStore('auth', {\n  // options...\n})\n\n// make sure to pass the right store definition, `useAuth` in this case.\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot))\n}\n```\n"
  },
  {
    "path": "packages/docs/cookbook/index.md",
    "content": "# Cookbook\n\n<RuleKitLink />\n\n- [Migrating from Vuex ≤4](./migration-vuex.md): A migration guide for converting Vuex ≤4 projects.\n- [HMR](./hot-module-replacement.md): How to activate hot module replacement and improve the developer experience.\n- [Testing Stores (WIP)](./testing.md): How to unit test Stores and mock them in component unit tests.\n- [Composing Stores](./composing-stores.md): How to cross use multiple stores. e.g. using the user store in the cart store.\n- [Options API](./options-api.md): How to use Pinia without the composition API, outside of `setup()`.\n- [Migrating from 0.0.7](./migration-0-0-7.md): A migration guide with more examples than the changelog.\n"
  },
  {
    "path": "packages/docs/cookbook/migration-0-0-7.md",
    "content": "# Migrating from 0.0.7\n\nThe versions after `0.0.7`: `0.1.0`, and `0.2.0`, came with a few big breaking changes. This guide helps you migrate whether you use Vue 2 or Vue 3. The whole changelog can be found in the repository:\n\n- [For Pinia <= 1 for Vue 2](https://github.com/vuejs/pinia/blob/v1/CHANGELOG.md)\n- [For Pinia >= 2 for Vue 3](https://github.com/vuejs/pinia/blob/v3/packages/pinia/CHANGELOG.md)\n\nIf you have questions or issues regarding the migration, feel free to [open a discussion](https://github.com/vuejs/pinia/discussions/categories/q-a) to ask for help.\n\n## No more `store.state`\n\nYou no longer access the store state via a `state` property, you can directly access any state property.\n\nGiven a store defined with:\n\n```js\nconst useStore({\n  id: 'main',\n  state: () => ({ count: 0 })\n})\n```\n\nDo\n\n```diff\n const store = useStore()\n\n-store.state.count++\n+store.count.++\n```\n\nYou can still access the whole store state with `$state` when needed:\n\n```diff\n-store.state = newState\n+store.$state = newState\n```\n\n## Rename of store properties\n\nAll store properties (`id`, `patch`, `reset`, etc) are now prefixed with `$` to allow properties defined on the store with the same names. Tip: you can refactor your whole codebase with F2 (or right-click + Refactor) on each of the store's properties\n\n```diff\n const store = useStore()\n-store.patch({ count: 0 })\n+store.$patch({ count: 0 })\n\n-store.reset()\n+store.$reset()\n\n-store.id\n+store.$id\n```\n\n## The Pinia instance\n\nIt's now necessary to create a pinia instance and install it:\n\nIf you are using Vue 2 (Pinia <= 1):\n\n```js\nimport Vue from 'vue'\nimport { createPinia, PiniaVuePlugin } from 'pinia'\n\nconst pinia = createPinia()\nVue.use(PiniaVuePlugin)\nnew Vue({\n  el: '#app',\n  pinia,\n  // ...\n})\n```\n\nIf you are using Vue 3 (Pinia >= 2):\n\n```js\nimport { createApp } from 'vue'\nimport { createPinia, PiniaVuePlugin } from 'pinia'\nimport App from './App.vue'\n\nconst pinia = createPinia()\ncreateApp(App).use(pinia).mount('#app')\n```\n\nThe `pinia` instance is what holds the state and should **be unique per application**. Check the SSR section of the docs for more details.\n\n## SSR changes\n\nThe SSR plugin `PiniaSsr` is no longer necessary and has been removed.\nWith the introduction of pinia instances, `getRootState()` is no longer necessary and should be replaced with `pinia.state.value`:\n\nIf you are using Vue 2 (Pinia <= 1):\n\n```diff\n// entry-server.js\n-import { getRootState, PiniaSsr } from 'pinia',\n+import { createPinia, PiniaVuePlugin } from 'pinia',\n\n\n-// install plugin to automatically use correct context in setup and onServerPrefetch\n-Vue.use(PiniaSsr);\n+Vue.use(PiniaVuePlugin)\n\n export default context => {\n+  const pinia = createPinia()\n   const app = new Vue({\n     // other options\n+    pinia\n   })\n\n   context.rendered = () => {\n     // pass state to context\n-    context.piniaState = getRootState(context.req)\n+    context.piniaState = pinia.state.value\n   };\n\n-   return { app }\n+   return { app, pinia }\n }\n```\n\n`setActiveReq()` and `getActiveReq()` have been replaced with `setActivePinia()` and `getActivePinia()` respectively. `setActivePinia()` can only be passed a `pinia` instance created with `createPinia()`. **Note that most of the time you won't directly use these functions**.\n"
  },
  {
    "path": "packages/docs/cookbook/migration-v1-v2.md",
    "content": "# Migrating from 0.x (v1) to v2\n\n<RuleKitLink />\n\nStarting at version `2.0.0-rc.4`, pinia supports both Vue 2 and Vue 3! This means, all new updates will be applied to this version 2 so both Vue 2 and Vue 3 users can benefit from it. If you are using Vue 3, this doesn't change anything for you as you were already using the rc and you can check [the CHANGELOG](https://github.com/vuejs/pinia/blob/v2/packages/pinia/CHANGELOG.md) for a detailed explanation of everything that changed. Otherwise, **this guide is for you**!\n\n## Deprecations\n\nLet's take a look at all the changes you need to apply to your code. First, make sure you are already running the latest 0.x version to see any deprecations:\n\n```shell\nnpm i 'pinia@^0.x.x'\n# or with yarn\nyarn add 'pinia@^0.x.x'\n```\n\nIf you are using ESLint, consider using [this plugin](https://github.com/gund/eslint-plugin-deprecation) to find all deprecated usages. Otherwise, you should be able to see them as they appear crossed. These are the APIs that were deprecated that were removed:\n\n- `createStore()` becomes `defineStore()`\n- In subscriptions, `storeName` becomes `storeId`\n- `PiniaPlugin` was renamed `PiniaVuePlugin` (Pinia plugin for Vue 2)\n- `$subscribe()` no longer accepts a _boolean_ as second parameter, pass an object with `detached: true` instead.\n- Pinia plugins no longer directly receive the `id` of the store. Use `store.$id` instead.\n\n## Breaking changes\n\nAfter removing these, you can upgrade to v2 with:\n\n```shell\nnpm i 'pinia@^2.x.x'\n# or with yarn\nyarn add 'pinia@^2.x.x'\n```\n\nAnd start updating your code.\n\n### Generic Store type\n\nAdded in [2.0.0-rc.0](https://github.com/vuejs/pinia/blob/v2/packages/pinia/CHANGELOG.md#200-rc0-2021-07-28)\n\nReplace any usage of the type `GenericStore` with `StoreGeneric`. This is the new generic store type that should accept any kind of store. If you were writing functions using the type `Store` without passing its generics (e.g. `Store<Id, State, Getters, Actions>`), you should also use `StoreGeneric` as the `Store` type without generics creates an empty store type.\n\n```ts\nfunction takeAnyStore(store: Store) {} // [!code --]\nfunction takeAnyStore(store: StoreGeneric) {} // [!code ++]\n\nfunction takeAnyStore(store: GenericStore) {} // [!code --]\nfunction takeAnyStore(store: StoreGeneric) {} // [!code ++]\n```\n\n## `DefineStoreOptions` for plugins\n\nIf you were writing plugins, using TypeScript, and extending the type `DefineStoreOptions` to add custom options, you should rename it to `DefineStoreOptionsBase`. This type will apply to both setup and options stores.\n\n```ts\ndeclare module 'pinia' {\n  export interface DefineStoreOptions<S, Store> { // [!code --]\n  export interface DefineStoreOptionsBase<S, Store> { // [!code ++]\n    debounce?: {\n      [k in keyof StoreActions<Store>]?: number\n    }\n  }\n}\n```\n\n## `PiniaStorePlugin` was renamed\n\nThe type `PiniaStorePlugin` was renamed to `PiniaPlugin`.\n\n```ts\nimport { PiniaStorePlugin } from 'pinia' // [!code --]\nimport { PiniaPlugin } from 'pinia' // [!code ++]\n\nconst piniaPlugin: PiniaStorePlugin = () => { // [!code --]\nconst piniaPlugin: PiniaPlugin = () => { // [!code ++]\n  // ...\n}\n```\n\n**Note this change can only be done after upgrading to the latest version of Pinia without deprecations**.\n\n## `@vue/composition-api` version\n\nSince pinia now relies on `effectScope()`, you must use at least the version `1.1.0` of `@vue/composition-api`:\n\n```shell\nnpm i @vue/composition-api@latest\n# or with yarn\nyarn add @vue/composition-api@latest\n```\n\n## webpack 4 support\n\nIf you are using webpack 4 (Vue CLI uses webpack 4), you might encounter an error like this:\n\n```\nERROR  Failed to compile with 18 errors\n\n error  in ./node_modules/pinia/dist/pinia.mjs\n\nCan't import the named export 'computed' from non EcmaScript module (only default export is available)\n```\n\nThis is due to the modernization of dist files to support native ESM modules in Node.js. Files are now using the extension `.mjs` and `.cjs` to let Node benefit from this. To fix this issue you have two possibilities:\n\n- If you are using Vue CLI 4.x, upgrade your dependencies. This should include the fix below.\n  - If upgrading is not possible for you, add this to your `vue.config.js`:\n\n    ```js\n    // vue.config.js\n    module.exports = {\n      configureWebpack: {\n        module: {\n          rules: [\n            {\n              test: /\\.mjs$/,\n              include: /node_modules/,\n              type: 'javascript/auto',\n            },\n          ],\n        },\n      },\n    }\n    ```\n\n- If you are manually handling webpack, you will have to let it know how to handle `.mjs` files:\n\n  ```js\n  // webpack.config.js\n  module.exports = {\n    module: {\n      rules: [\n        {\n          test: /\\.mjs$/,\n          include: /node_modules/,\n          type: 'javascript/auto',\n        },\n      ],\n    },\n  }\n  ```\n\n## Devtools\n\nPinia v2 no longer hijacks Vue Devtools v5, it requires Vue Devtools v6. Find the download link on the [Vue Devtools documentation](https://devtools.vuejs.org/guide/installation.html#chrome) for the **beta channel** of the extension.\n\n## Nuxt\n\nIf you are using Nuxt, pinia has now its own dedicated Nuxt package 🎉. Install it with:\n\n```bash\nnpm i @pinia/nuxt\n# or with yarn\nyarn add @pinia/nuxt\n```\n\nAlso make sure to **update your `@nuxtjs/composition-api` package**.\n\nThen adapt your `nuxt.config.js` and your `tsconfig.json` if you are using TypeScript:\n\n```js\n// nuxt.config.js\nmodule.exports {\n  buildModules: [\n    '@nuxtjs/composition-api/module',\n    'pinia/nuxt', // [!code --]\n    '@pinia/nuxt', // [!code ++]\n  ],\n}\n```\n\n```json\n// tsconfig.json\n{\n  \"types\": [\n    // ...\n    \"pinia/nuxt/types\" // [!code --]\n    \"@pinia/nuxt\" // [!code ++]\n  ]\n}\n```\n\nIt is also recommended to give [the dedicated Nuxt section](../ssr/nuxt.md) a read.\n"
  },
  {
    "path": "packages/docs/cookbook/migration-v2-v3.md",
    "content": "# Migrating from v2 to v3\n\n<RuleKitLink />\n\nPinia v3 is a _boring_ major release with no new features. It drops deprecated APIs and updates major dependencies. It only supports Vue 3. If you are using Vue 2, you can keep using v2. If you need help, [book help with Pinia's author](https://cal.com/posva/consultancy).\n\nFor most users, the migration should require **no change**. This guide is here to help you in case you encounter any issues.\n\n## Deprecations\n\n### `defineStore({ id })`\n\nThe `defineStore()` signature that accepts an object with an `id` property is deprecated. You should use the `id` parameter instead:\n\n```ts\ndefineStore({ // [!code --]\n  id: 'storeName', // [!code --]\ndefineStore('storeName', { // [!code ++]\n  // ...\n})\n```\n\n### `PiniaStorePlugin`\n\nThis deprecated type alias has been removed in favor of `PiniaPlugin`.\n\n## New versions\n\n- Only Vue 3 is supported.\n- TypeScript 5 or newer is required.\n- The devtools API has been upgraded to [v7](https://devtools.vuejs.org).\n\n## Nuxt\n\nThe Nuxt module has been updated to support Nuxt 3. If you are using Nuxt 2 or Nuxt bridge, you can keep using the old version of Pinia.\n"
  },
  {
    "path": "packages/docs/cookbook/migration-vuex.md",
    "content": "# Migrating from Vuex ≤4\n\nAlthough the structure of Vuex and Pinia stores is different, a lot of the logic can be reused. This guide serves to help you through the process and point out some common gotchas that can appear.\n\n## Preparation\n\nFirst, follow the [Getting Started guide](../getting-started.md) to install Pinia.\n\n## Restructuring Modules to Stores\n\nVuex has the concept of a single store with multiple _modules_. These modules can optionally be namespaced and even nested within each other.\n\nThe easiest way to transition that concept to be used with Pinia is that each module you used previously is now a _store_. Each store requires an `id` which is similar to a namespace in Vuex. This means that each store is namespaced by design. Nested modules can also each become their own store. Stores that depend on each other will simply import the other store.\n\nHow you choose to restructure your Vuex modules into Pinia stores is entirely up to you, but here is one suggestion:\n\n<RuleKitLink />\n\n```bash\n# Vuex example (assuming namespaced modules)\nsrc\n└── store\n    ├── index.js           # Initializes Vuex, imports modules\n    └── modules\n        ├── module1.js     # 'module1' namespace\n        └── nested\n            ├── index.js   # 'nested' namespace, imports module2 & module3\n            ├── module2.js # 'nested/module2' namespace\n            └── module3.js # 'nested/module3' namespace\n\n# Pinia equivalent, note ids match previous namespaces\nsrc\n└── stores\n    ├── index.js          # (Optional) Initializes Pinia, does not import stores\n    ├── module1.js        # 'module1' id\n    ├── nested-module2.js # 'nestedModule2' id\n    ├── nested-module3.js # 'nestedModule3' id\n    └── nested.js         # 'nested' id\n```\n\nThis creates a flat structure for stores but also preserves the previous namespacing with equivalent `id`s. If you had some state/getters/actions/mutations in the root of the store (in the `store/index.js` file of Vuex) you may wish to create another store called something like `root` which holds all that information.\n\nThe directory for Pinia is generally called `stores` instead of `store`. This is to emphasize that Pinia uses multiple stores, instead of a single store in Vuex.\n\nFor large projects you may wish to do this conversion module by module rather than converting everything at once. You can actually mix Pinia and Vuex together during the migration so this approach can also work and is another reason for naming the Pinia directory `stores` instead.\n\n## Converting a Single Module\n\nHere is a complete example of the before and after of converting a Vuex module to a Pinia store, see below for a step-by-step guide. The Pinia example uses an option store as the structure is most similar to Vuex:\n\n```ts\n// Vuex module in the 'auth/user' namespace\nimport { Module } from 'vuex'\nimport { api } from '@/api'\nimport { RootState } from '@/types' // if using a Vuex type definition\n\ninterface State {\n  firstName: string\n  lastName: string\n  userId: number | null\n}\n\nconst storeModule: Module<State, RootState> = {\n  namespaced: true,\n  state: {\n    firstName: '',\n    lastName: '',\n    userId: null,\n  },\n  getters: {\n    firstName: (state) => state.firstName,\n    fullName: (state) => `${state.firstName} ${state.lastName}`,\n    loggedIn: (state) => state.userId !== null,\n    // combine with some state from other modules\n    fullUserDetails: (state, getters, rootState, rootGetters) => {\n      return {\n        ...state,\n        fullName: getters.fullName,\n        // read the state from another module named `auth`\n        ...rootState.auth.preferences,\n        // read a getter from a namespaced module called `email` nested under `auth`\n        ...rootGetters['auth/email'].details,\n      }\n    },\n  },\n  actions: {\n    async loadUser({ state, commit }, id: number) {\n      if (state.userId !== null) throw new Error('Already logged in')\n      const res = await api.user.load(id)\n      commit('updateUser', res)\n    },\n  },\n  mutations: {\n    updateUser(state, payload) {\n      state.firstName = payload.firstName\n      state.lastName = payload.lastName\n      state.userId = payload.userId\n    },\n    clearUser(state) {\n      state.firstName = ''\n      state.lastName = ''\n      state.userId = null\n    },\n  },\n}\n\nexport default storeModule\n```\n\n```ts\n// Pinia Store\nimport { defineStore } from 'pinia'\nimport { useAuthPreferencesStore } from './auth-preferences'\nimport { useAuthEmailStore } from './auth-email'\nimport vuexStore from '@/store' // for gradual conversion, see fullUserDetails\n\ninterface State {\n  firstName: string\n  lastName: string\n  userId: number | null\n}\n\nexport const useAuthUserStore = defineStore('authUser', {\n  // convert to a function\n  state: (): State => ({\n    firstName: '',\n    lastName: '',\n    userId: null,\n  }),\n  getters: {\n    // firstName getter removed, no longer needed\n    fullName: (state) => `${state.firstName} ${state.lastName}`,\n    loggedIn: (state) => state.userId !== null,\n    // must define return type because of using `this`\n    fullUserDetails(state): FullUserDetails {\n      // import from other stores\n      const authPreferencesStore = useAuthPreferencesStore()\n      const authEmailStore = useAuthEmailStore()\n      return {\n        ...state,\n        // other getters now on `this`\n        fullName: this.fullName,\n        ...authPreferencesStore.$state,\n        ...authEmailStore.details,\n      }\n\n      // alternative if other modules are still in Vuex\n      // return {\n      //   ...state,\n      //   fullName: this.fullName,\n      //   ...vuexStore.state.auth.preferences,\n      //   ...vuexStore.getters['auth/email'].details\n      // }\n    },\n  },\n  actions: {\n    // no context as first argument, use `this` instead\n    async loadUser(id: number) {\n      if (this.userId !== null) throw new Error('Already logged in')\n      const res = await api.user.load(id)\n      this.updateUser(res)\n    },\n    // mutations can now become actions, instead of `state` as first argument use `this`\n    updateUser(payload) {\n      this.firstName = payload.firstName\n      this.lastName = payload.lastName\n      this.userId = payload.userId\n    },\n    // easily reset state using `$reset`\n    clearUser() {\n      this.$reset()\n    },\n  },\n})\n```\n\nLet's break the above down into steps:\n\n1. Add a required `id` for the store, you may wish to keep this the same as the namespace before. It is also recommended to make sure the `id` is in _camelCase_ as it makes it easier to use with `mapStores()`.\n2. Convert `state` to a function if it was not one already\n3. Convert `getters`\n   1. Remove any getters that return state under the same name (eg. `firstName: (state) => state.firstName`), these are not necessary as you can access any state directly from the store instance\n   2. If you need to access other getters, they are on `this` instead of using the second argument. Remember that if you are using `this` then you will have to use a regular function instead of an arrow function. Also note that you will need to specify a return type because of TS limitations, see [here](../core-concepts/getters.md#accessing-other-getters) for more details\n   3. If using `rootState` or `rootGetters` arguments, replace them by importing the other store directly, or if they still exist in Vuex then access them directly from Vuex\n4. Convert `actions`\n   1. Remove the first `context` argument from each action. Everything should be accessible from `this` instead\n   2. If using other stores either import them directly or access them on Vuex, the same as for getters\n5. Convert `mutations`\n   1. Mutations do not exist any more. These can be converted to `actions` instead, or you can just assign directly to the store within your components (eg. `userStore.firstName = 'First'`)\n   2. If converting to actions, remove the first `state` argument and replace any assignments with `this` instead\n   3. A common mutation is to reset the state back to its initial state. This is built in functionality with the store's `$reset` method. Note that this functionality only exists for option stores.\n\nAs you can see most of your code can be reused. Type safety should also help you identify what needs to be changed if anything is missed.\n\n## Usage Inside Components\n\nNow that your Vuex module has been converted to a Pinia store, any component or other file that uses that module needs to be updated too.\n\nIf you were using `map` helpers from Vuex before, it's worth looking at the [Usage without setup() guide](./options-api.md) as most of those helpers can be reused.\n\nIf you were using `useStore` then instead import the new store directly and access the state on it. For example:\n\n```ts\n// Vuex\nimport { defineComponent, computed } from 'vue'\nimport { useStore } from 'vuex'\n\nexport default defineComponent({\n  setup() {\n    const store = useStore()\n\n    const firstName = computed(() => store.state.auth.user.firstName)\n    const fullName = computed(() => store.getters['auth/user/fullName'])\n\n    return {\n      firstName,\n      fullName,\n    }\n  },\n})\n```\n\n```ts\n// Pinia\nimport { defineComponent, computed } from 'vue'\nimport { useAuthUserStore } from '@/stores/auth-user'\n\nexport default defineComponent({\n  setup() {\n    const authUserStore = useAuthUserStore()\n\n    const firstName = computed(() => authUserStore.firstName)\n    const fullName = computed(() => authUserStore.fullName)\n\n    return {\n      // you can also access the whole store in your component by returning it\n      authUserStore,\n      firstName,\n      fullName,\n    }\n  },\n})\n```\n\n## Usage Outside Components\n\nUpdating usage outside of components should be simple as long as you're careful to _not use a store outside of functions_. Here is an example of using the store in a Vue Router navigation guard:\n\n```ts\n// Vuex\nimport vuexStore from '@/store'\n\nrouter.beforeEach((to, from, next) => {\n  if (vuexStore.getters['auth/user/loggedIn']) next()\n  else next('/login')\n})\n```\n\n```ts\n// Pinia\nimport { useAuthUserStore } from '@/stores/auth-user'\n\nrouter.beforeEach((to, from, next) => {\n  // Must be used within the function!\n  const authUserStore = useAuthUserStore()\n  if (authUserStore.loggedIn) next()\n  else next('/login')\n})\n```\n\nMore details can be found [here](../core-concepts/outside-component-usage.md).\n\n## Advanced Vuex Usage\n\nIn the case your Vuex store using some of the more advanced features it offers, here is some guidance on how to accomplish the same in Pinia. Some of these points are already covered in [this comparison summary](../introduction.md#Comparison-with-Vuex-3-x-4-x).\n\n### Dynamic Modules\n\nThere is no need to dynamically register modules in Pinia. Stores are dynamic by design and are only registered when they are needed. If a store is never used, it will never be \"registered\".\n\n### Hot Module Replacement\n\nHMR is also supported but will need to be replaced, see the [HMR Guide](./hot-module-replacement.md).\n\n### Plugins\n\nIf you use a public Vuex plugin then check if there is a Pinia alternative. If not you will need to write your own or evaluate whether the plugin is still necessary.\n\nIf you have written a plugin of your own, then it can likely be updated to work with Pinia. See the [Plugin Guide](../core-concepts/plugins.md).\n"
  },
  {
    "path": "packages/docs/cookbook/options-api.md",
    "content": "# Usage without `setup()`\n\nPinia can be used even if you are not using the composition API (if you are using Vue <2.7, you still need to install the `@vue/composition-api` plugin though). While we recommend you give the Composition API a try and learn it, it might not be the time for you and your team yet, you might be in the process of migrating an application, or any other reason. There are a few functions:\n\n- [mapStores](#giving-access-to-the-whole-store)\n- [mapState](../core-concepts/state.md#usage-with-the-options-api)\n- [mapWritableState](../core-concepts/state.md#modifiable-state)\n- ⚠️ [mapGetters](../core-concepts/getters.md#without-setup) (just for migration convenience, use `mapState()` instead)\n- [mapActions](../core-concepts/actions.md#without-setup)\n\n<RuleKitLink />\n\n## Giving access to the whole store\n\nIf you need to access pretty much everything from the store, it might be too much to map every single property of the store... Instead you can get access to the whole store with `mapStores()`:\n\n```js\nimport { mapStores } from 'pinia'\n\n// given two stores with the following ids\nconst useUserStore = defineStore('user', {\n  // ...\n})\nconst useCartStore = defineStore('cart', {\n  // ...\n})\n\nexport default {\n  computed: {\n    // note we are not passing an array, just one store after the other\n    // each store will be accessible as its id + 'Store'\n    ...mapStores(useCartStore, useUserStore),\n  },\n\n  methods: {\n    async buyStuff() {\n      // use them anywhere!\n      if (this.userStore.isAuthenticated()) {\n        await this.cartStore.buy()\n        this.$router.push('/purchased')\n      }\n    },\n  },\n}\n```\n\nBy default, Pinia will add the `\"Store\"` suffix to the `id` of each store. You can customize this behavior by calling the `setMapStoreSuffix()`:\n\n```js\nimport { createPinia, setMapStoreSuffix } from 'pinia'\n\n// completely remove the suffix: this.user, this.cart\nsetMapStoreSuffix('')\n// this.user_store, this.cart_store (it's okay, I won't judge you)\nsetMapStoreSuffix('_store')\nexport const pinia = createPinia()\n```\n\n## TypeScript\n\nBy default, all map helpers support autocompletion and you don't need to do anything. If you call `setMapStoreSuffix()` to change the `\"Store\"` suffix, you will need to also add it somewhere in a TS file or your `global.d.ts` file. The most convenient place would be the same place where you call `setMapStoreSuffix()`:\n\n```ts\nimport { createPinia, setMapStoreSuffix } from 'pinia'\n\nsetMapStoreSuffix('') // completely remove the suffix\nexport const pinia = createPinia()\n\ndeclare module 'pinia' {\n  export interface MapStoresCustomization {\n    // set it to the same value as above\n    suffix: ''\n  }\n}\n```\n\n:::warning\nIf you are using a TypeScript declaration file (like `global.d.ts`), make sure to `import 'pinia'` at the top of it to expose all existing types.\n:::\n"
  },
  {
    "path": "packages/docs/cookbook/testing.md",
    "content": "# Testing stores\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/65f9a9c10bfab01f414c25dc\"\n  title=\"Watch a free video of Mastering Pinia about testing stores\"\n/>\n\nStores will, by design, be used at many places and can make testing much harder than it should be. Fortunately, this doesn't have to be the case. We need to take care of three things when testing stores:\n\n- The `pinia` instance: Stores cannot work without it\n- `actions`: most of the time, they contain the most complex logic of our stores. Wouldn't it be nice if they were mocked by default?\n- Plugins: If you rely on plugins, you will have to install them for tests too\n\nDepending on what or how you are testing, we need to take care of these three things differently.\n\n<RuleKitLink />\n\n## Unit testing a store\n\nTo unit test a store, the most important part is creating a `pinia` instance:\n\n```js\n// stores/counter.spec.ts\nimport { setActivePinia, createPinia } from 'pinia'\nimport { useCounterStore } from '../src/stores/counter'\n\ndescribe('Counter Store', () => {\n  beforeEach(() => {\n    // creates a fresh pinia and makes it active\n    // so it's automatically picked up by any useStore() call\n    // without having to pass it to it: `useStore(pinia)`\n    setActivePinia(createPinia())\n  })\n\n  it('increments', () => {\n    const counter = useCounterStore()\n    expect(counter.n).toBe(0)\n    counter.increment()\n    expect(counter.n).toBe(1)\n  })\n\n  it('increments by amount', () => {\n    const counter = useCounterStore()\n    counter.increment(10)\n    expect(counter.n).toBe(10)\n  })\n})\n```\n\nIf you have any store plugins, there is one important thing to know: **plugins won't be used until `pinia` is installed in an App**. This can be solved by creating an empty App or a fake one:\n\n```js\nimport { setActivePinia, createPinia } from 'pinia'\nimport { createApp } from 'vue'\nimport { somePlugin } from '../src/stores/plugin'\n\n// same code as above...\n\n// you don't need to create one app per test\nconst app = createApp({})\nbeforeEach(() => {\n  const pinia = createPinia().use(somePlugin)\n  app.use(pinia)\n  setActivePinia(pinia)\n})\n```\n\n## Unit testing components\n\n<!-- NOTE: too long maybe but good value -->\n<!-- <MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/6630f540c418f8419b73b2b2?t1=1715867840&t2=1715867570609?preload=false&autoplay=false&loop=false&disable_player_controls=false\"\n  title=\"Watch a free video of Mastering Pinia about testing stores\"\n/> -->\n\nThis can be achieved with `createTestingPinia()`, which returns a pinia instance designed to help unit tests components.\n\nStart by installing `@pinia/testing`:\n\n```shell\nnpm i -D @pinia/testing\n```\n\nAnd make sure to create a testing pinia in your tests when mounting a component:\n\n```js\nimport { mount } from '@vue/test-utils'\nimport { createTestingPinia } from '@pinia/testing'\n// import any store you want to interact with in tests\nimport { useSomeStore } from '@/stores/myStore'\n\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [createTestingPinia()],\n  },\n})\n\nconst store = useSomeStore() // uses the testing pinia!\n\n// state can be directly manipulated\nstore.name = 'my new name'\n// can also be done through patch\nstore.$patch({ name: 'new name' })\nexpect(store.name).toBe('new name')\n\n// actions are stubbed by default, meaning they don't execute their code by default.\n// See below to customize this behavior.\nstore.someAction()\n\nexpect(store.someAction).toHaveBeenCalledTimes(1)\nexpect(store.someAction).toHaveBeenLastCalledWith()\n```\n\n### Initial State\n\nYou can set the initial state of **all of your stores** when creating a testing pinia by passing an `initialState` object. This object will be used by the testing pinia to _patch_ stores when they are created. Let's say you want to initialize the state of this store:\n\n```ts\nimport { defineStore } from 'pinia'\n\nconst useCounterStore = defineStore('counter', {\n  state: () => ({ n: 0 }),\n  // ...\n})\n```\n\nSince the store is named _\"counter\"_, you need to add a matching object to `initialState`:\n\n```ts\n// somewhere in your test\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        initialState: {\n          counter: { n: 20 }, // start the counter at 20 instead of 0\n        },\n      }),\n    ],\n  },\n})\n\nconst store = useSomeStore() // uses the testing pinia!\nstore.n // 20\n```\n\n### Customizing behavior of actions\n\n`createTestingPinia` stubs out all store actions unless told otherwise. This allows you to test your components and stores separately.\n\nIf you want to revert this behavior and normally execute your actions during tests, specify `stubActions: false` when calling `createTestingPinia`:\n\n```js\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [createTestingPinia({ stubActions: false })],\n  },\n})\n\nconst store = useSomeStore()\n\n// Now this call WILL execute the implementation defined by the store\nstore.someAction()\n\n// ...but it's still wrapped with a spy, so you can inspect calls\nexpect(store.someAction).toHaveBeenCalledTimes(1)\n```\n\n### Selective action stubbing\n\nSometimes you may want to stub only specific actions while allowing others to execute normally. You can achieve this by passing an array of action names to the `stubActions` option:\n\n```js\n// Only stub the 'increment' and 'reset' actions\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        stubActions: ['increment', 'reset'],\n      }),\n    ],\n  },\n})\n\nconst store = useSomeStore()\n\n// These actions will be stubbed (not executed)\nstore.increment() // stubbed\nstore.reset() // stubbed\n\n// Other actions will execute normally but still be spied\nstore.fetchData() // executed normally\nexpect(store.fetchData).toHaveBeenCalledTimes(1)\n```\n\nFor more complex scenarios, you can pass a function that receives the action name and store instance, and returns whether the action should be stubbed:\n\n```js\n// Stub actions based on custom logic\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        stubActions: (actionName, store) => {\n          // Stub all actions that start with 'set'\n          if (actionName.startsWith('set')) return true\n\n          // Stub actions based on initial store state\n          if (store.isPremium) return false\n\n          return true\n        },\n      }),\n    ],\n  },\n})\n\nconst store = useSomeStore()\n\n// Actions starting with 'set' are stubbed\nstore.setValue(42) // stubbed\n\n// Other actions may execute based on the initial store state\nstore.fetchData() // executed or stubbed based on initial store.isPremium\n```\n\n::: tip\n\n- An empty array `[]` means no actions will be stubbed (same as `false`)\n- The function is evaluated once at store setup time, receiving the store instance in its initial state\n\n:::\n\nYou can also manually mock specific actions after creating the store:\n\n```ts\nconst store = useSomeStore()\nvi.spyOn(store, 'increment').mockImplementation(() => {})\n// or if using testing pinia with stubbed actions\nstore.increment.mockImplementation(() => {})\n```\n\n### Mocking the returned value of an action\n\nActions are automatically spied but type-wise, they are still the regular actions. In order to get the correct type, we must implement a custom type-wrapper that applies the `Mock` type to each action. **This type depends on the testing framework you are using**. Here is an example with Vitest:\n\n```ts\nimport type { Mock } from 'vitest'\nimport type { UnwrapRef } from 'vue'\nimport type { Store, StoreDefinition } from 'pinia'\n\nfunction mockedStore<TStoreDef extends () => unknown>(\n  useStore: TStoreDef\n): TStoreDef extends StoreDefinition<\n  infer Id,\n  infer State,\n  infer Getters,\n  infer Actions\n>\n  ? Store<\n      Id,\n      State,\n      Record<string, never>,\n      {\n        [K in keyof Actions]: Actions[K] extends (...args: any[]) => any\n          ? // 👇 depends on your testing framework\n            Mock<Actions[K]>\n          : Actions[K]\n      }\n    > & {\n      [K in keyof Getters]: UnwrapRef<Getters[K]>\n    }\n  : ReturnType<TStoreDef> {\n  return useStore() as any\n}\n```\n\nThis can be used in tests to get a correctly typed store:\n\n```ts\nimport { mockedStore } from './mockedStore'\nimport { useSomeStore } from '@/stores/myStore'\n\nconst store = mockedStore(useSomeStore)\n// typed!\nstore.someAction.mockResolvedValue('some value')\n```\n\nIf you are interested in learning more tricks like this, you should check out the Testing lessons on [Mastering Pinia](https://masteringpinia.com/lessons/exercise-mocking-stores-introduction).\n\n### Specifying the createSpy function\n\nWhen using Jest, or vitest with `globals: true`, `createTestingPinia` automatically stubs actions using the spy function based on the existing test framework (`jest.fn` or `vitest.fn`). If you are not using `globals: true` or using a different framework, you'll need to provide a [createSpy](../api/@pinia/testing/interfaces/TestingOptions.html#createSpy-) option:\n\n::: code-group\n\n```ts [vitest]\n// NOTE: not needed with `globals: true`\nimport { vi } from 'vitest'\n\ncreateTestingPinia({\n  createSpy: vi.fn,\n})\n```\n\n```ts [sinon]\nimport sinon from 'sinon'\n\ncreateTestingPinia({\n  createSpy: sinon.spy,\n})\n```\n\n:::\n\nYou can find more examples in [the tests of the testing package](https://github.com/vuejs/pinia/blob/v3/packages/testing/src/testing.spec.ts).\n\n### Mocking getters\n\nBy default, any getter will be computed like regular usage but you can manually force a value by setting the getter to anything you want:\n\n```ts\nimport { defineStore } from 'pinia'\nimport { createTestingPinia } from '@pinia/testing'\n\nconst useCounterStore = defineStore('counter', {\n  state: () => ({ n: 1 }),\n  getters: {\n    double: (state) => state.n * 2,\n  },\n})\n\nconst pinia = createTestingPinia()\nconst counter = useCounterStore(pinia)\n\ncounter.double = 3 // 🪄 getters are writable only in tests\n\n// set to undefined to reset the default behavior\n// @ts-expect-error: usually it's a number\ncounter.double = undefined\ncounter.double // 2 (=1 x 2)\n```\n\n### Pinia Plugins\n\nIf you have any pinia plugins, make sure to pass them when calling `createTestingPinia()` so they are properly applied. **Do not add them with `testingPinia.use(MyPlugin)`** like you would do with a regular pinia:\n\n```js\nimport { createTestingPinia } from '@pinia/testing'\nimport { somePlugin } from '../src/stores/plugin'\n\n// inside some test\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        stubActions: false,\n        plugins: [somePlugin],\n      }),\n    ],\n  },\n})\n```\n\n## E2E tests\n\nWhen it comes to Pinia, you don't need to change anything for E2E tests, that's the whole point of these tests! You could maybe test HTTP requests, but that's way beyond the scope of this guide 😄.\n"
  },
  {
    "path": "packages/docs/cookbook/vscode-snippets.md",
    "content": "# VS Code Snippets\n\n<RuleKitLink />\n\nThese are some snippets that I use in VS Code to make my life easier.\n\nManage user snippets with <kbd>⇧ Shift</kbd>+<kbd>⌘ Command</kbd>+<kbd>P</kbd> / <kbd>⇧ Shift</kbd>+<kbd>⌃ Control</kbd>+<kbd>P</kbd> and then `Snippets: Configure User Snippets`.\n\n```json\n{\n  \"Pinia Options Store Boilerplate\": {\n    \"scope\": \"javascript,typescript\",\n    \"prefix\": \"pinia-options\",\n    \"body\": [\n      \"import { defineStore, acceptHMRUpdate } from 'pinia'\",\n      \"\",\n      \"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', {\",\n      \" state: () => ({\",\n      \"   $0\",\n      \" }),\",\n      \" getters: {},\",\n      \" actions: {},\",\n      \"})\",\n      \"\",\n      \"if (import.meta.hot) {\",\n      \" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))\",\n      \"}\",\n      \"\"\n    ],\n    \"description\": \"Bootstrap the code needed for a Vue.js Pinia Options Store file\"\n  },\n  \"Pinia Setup Store Boilerplate\": {\n    \"scope\": \"javascript,typescript\",\n    \"prefix\": \"pinia-setup\",\n    \"body\": [\n      \"import { defineStore, acceptHMRUpdate } from 'pinia'\",\n      \"\",\n      \"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', () => {\",\n      \" $0\",\n      \" return {}\",\n      \"})\",\n      \"\",\n      \"if (import.meta.hot) {\",\n      \" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))\",\n      \"}\",\n      \"\"\n    ],\n    \"description\": \"Bootstrap the code needed for a Vue.js Pinia Setup Store file\"\n  }\n}\n```\n"
  },
  {
    "path": "packages/docs/core-concepts/actions.md",
    "content": "# Actions\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/synchronous-and-asynchronous-actions-in-pinia\"\n  title=\"Learn all about actions in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/the-3-pillars-of-pinia-actions\"\n  title=\"Learn all about actions in Pinia\"\n/>\n\nActions are the equivalent of [methods](https://vuejs.org/api/options-state.html#methods) in components. They can be defined with the `actions` property in `defineStore()` and **they are perfect to define business logic**:\n\n```js\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  actions: {\n    // since we rely on `this`, we cannot use an arrow function\n    increment() {\n      this.count++\n    },\n    randomizeCounter() {\n      this.count = Math.round(100 * Math.random())\n    },\n  },\n})\n```\n\n<RuleKitLink />\n\nLike [getters](./getters.md), actions get access to the _whole store instance_ through `this` with **full typing (and autocompletion ✨) support**. **Unlike getters, `actions` can be asynchronous**, you can `await` inside of actions any API call or even other actions! Here is an example using [Mande](https://github.com/posva/mande). Note the library you use doesn't matter as long as you get a `Promise`. You could even use the native `fetch` function (browser only):\n\n```js\nimport { mande } from 'mande'\n\nconst api = mande('/api/users')\n\nexport const useUsers = defineStore('users', {\n  state: () => ({\n    userData: null,\n    // ...\n  }),\n\n  actions: {\n    async registerUser(login, password) {\n      try {\n        this.userData = await api.post({ login, password })\n        showTooltip(`Welcome back ${this.userData.name}!`)\n      } catch (error) {\n        showTooltip(error)\n        // let the form component display the error\n        return error\n      }\n    },\n  },\n})\n```\n\nYou are also completely free to set whatever arguments you want and return anything. When calling actions, everything will be automatically inferred!\n\nActions are invoked like regular functions and methods:\n\n```vue\n<script setup>\nconst store = useCounterStore()\n// call the action as a method of the store\nstore.randomizeCounter()\n</script>\n\n<template>\n  <!-- Even on the template -->\n  <button @click=\"store.randomizeCounter()\">Randomize</button>\n</template>\n```\n\n## Accessing other stores actions\n\nTo consume another store, you can directly _use it_ inside of the _action_:\n\n```js\nimport { useAuthStore } from './auth-store'\n\nexport const useSettingsStore = defineStore('settings', {\n  state: () => ({\n    preferences: null,\n    // ...\n  }),\n  actions: {\n    async fetchUserPreferences() {\n      const auth = useAuthStore()\n      if (auth.isAuthenticated) {\n        this.preferences = await fetchPreferences()\n      } else {\n        throw new Error('User must be authenticated')\n      }\n    },\n  },\n})\n```\n\n## Usage with the Options API\n\n<VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-pinia-actions-in-the-options-api\"\n  title=\"Access Pinia Getters via the Options API\"\n/>\n\nFor the following examples, you can assume the following store was created:\n\n```js\n// Example File Path:\n// ./src/stores/counter.js\n\nimport { defineStore } from 'pinia'\n\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n```\n\n### With `setup()`\n\nWhile Composition API is not for everyone, the `setup()` hook can make Pinia easier to work with while using the Options API. No extra map helper functions needed!\n\n```vue\n<script>\nimport { useCounterStore } from '../stores/counter'\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore()\n\n    return { counterStore }\n  },\n  methods: {\n    incrementAndPrint() {\n      this.counterStore.increment()\n      console.log('New Count:', this.counterStore.count)\n    },\n  },\n})\n</script>\n```\n\n### Without `setup()`\n\nIf you would prefer not to use Composition API at all, you can use the `mapActions()` helper to map actions properties as methods in your component:\n\n```js\nimport { mapActions } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  methods: {\n    // gives access to this.increment() inside the component\n    // same as calling from store.increment()\n    ...mapActions(useCounterStore, ['increment']),\n    // same as above but registers it as this.myOwnName()\n    ...mapActions(useCounterStore, { myOwnName: 'increment' }),\n  },\n}\n```\n\n## Subscribing to actions\n\nIt is possible to observe actions and their outcome with `store.$onAction()`. The callback passed to it is executed before the action itself. `after` handles promises and allows you to execute a function after the action resolves. In a similar way, `onError` allows you to execute a function if the action throws or rejects. These are useful for tracking errors at runtime, similar to [this tip in the Vue docs](https://vuejs.org/guide/best-practices/production-deployment#tracking-runtime-errors).\n\nHere is an example that logs before running actions and after they resolve/reject.\n\n```js\nconst unsubscribe = someStore.$onAction(\n  ({\n    name, // name of the action\n    store, // store instance, same as `someStore`\n    args, // array of parameters passed to the action\n    after, // hook after the action returns or resolves\n    onError, // hook if the action throws or rejects\n  }) => {\n    // a shared variable for this specific action call\n    const startTime = Date.now()\n    // this will trigger before an action on `store` is executed\n    console.log(`Start \"${name}\" with params [${args.join(', ')}].`)\n\n    // this will trigger if the action succeeds and after it has fully run.\n    // it waits for any returned promise\n    after((result) => {\n      console.log(\n        `Finished \"${name}\" after ${\n          Date.now() - startTime\n        }ms.\\nResult: ${result}.`\n      )\n    })\n\n    // this will trigger if the action throws or returns a promise that rejects\n    onError((error) => {\n      console.warn(\n        `Failed \"${name}\" after ${Date.now() - startTime}ms.\\nError: ${error}.`\n      )\n    })\n  }\n)\n\n// manually remove the listener\nunsubscribe()\n```\n\nBy default, _action subscriptions_ are bound to the component where they are added (if the store is inside a component's `setup()`). Meaning, they will be automatically removed when the component is unmounted. If you also want to keep them after the component is unmounted, pass `true` as the second argument to _detach_ the _action subscription_ from the current component:\n\n```vue\n<script setup>\nconst someStore = useSomeStore()\n\n// this subscription will be kept even after the component is unmounted\nsomeStore.$onAction(callback, true)\n</script>\n```\n"
  },
  {
    "path": "packages/docs/core-concepts/getters.md",
    "content": "# Getters\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/getters-in-pinia\"\n  title=\"Learn all about getters in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/the-3-pillars-of-pinia-getters\"\n  title=\"Learn all about getters in Pinia\"\n/>\n\nGetters are exactly the equivalent of [computed values](https://vuejs.org/guide/essentials/computed.html) for the state of a Store. They can be defined with the `getters` property in `defineStore()`. They receive the `state` as the first parameter **to encourage** the usage of arrow function:\n\n```js\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    doubleCount: (state) => state.count * 2,\n  },\n})\n```\n\n<RuleKitLink />\n\nMost of the time, getters will only rely on the state. However, they might need to use other getters. Because of this, we can get access to the _whole store instance_ through `this` when defining a regular function **but it is necessary to define the type of the return type (in TypeScript)**. This is due to a known limitation in TypeScript and **doesn't affect getters defined with an arrow function nor getters not using `this`**:\n\n```ts\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    // automatically infers the return type as a number\n    doubleCount(state) {\n      return state.count * 2\n    },\n    // the return type **must** be explicitly set\n    doublePlusOne(): number {\n      // autocompletion and typings for the whole store ✨\n      return this.doubleCount + 1\n    },\n  },\n})\n```\n\nThen you can access the getter directly on the store instance:\n\n```vue\n<script setup>\nimport { useCounterStore } from './counterStore'\n\nconst store = useCounterStore()\n</script>\n\n<template>\n  <p>Double count is {{ store.doubleCount }}</p>\n</template>\n```\n\n## Accessing other getters\n\nAs with computed properties, you can combine multiple getters. Access any other getter via `this`. In this scenario, **you will need to specify a return type** for the getter.\n\n::: code-group\n\n```ts [counterStore.ts]\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    doubleCount(state) {\n      return state.count * 2\n    },\n    doubleCountPlusOne(): number {\n      return this.doubleCount + 1\n    },\n  },\n})\n```\n\n```js [counterStore.js]\n// You can use JSDoc (https://jsdoc.app/tags-returns.html) in JavaScript\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    // type is automatically inferred because we are not using `this`\n    doubleCount: (state) => state.count * 2,\n    // here we need to add the type ourselves (using JSDoc in JS). We can also\n    // use this to document the getter\n    /**\n     * Returns the count value times two plus one.\n     *\n     * @returns {number}\n     */\n    doubleCountPlusOne() {\n      // autocompletion ✨\n      return this.doubleCount + 1\n    },\n  },\n})\n```\n\n:::\n\n## Passing arguments to getters\n\n_Getters_ are just _computed_ properties behind the scenes, so it's not possible to pass any parameters to them. However, you can return a function from the _getter_ to accept any arguments:\n\n```js\nexport const useStore = defineStore('main', {\n  getters: {\n    getUserById: (state) => {\n      return (userId) => state.users.find((user) => user.id === userId)\n    },\n  },\n})\n```\n\nand use in component:\n\n```vue\n<script setup>\nimport { storeToRefs } from 'pinia'\nimport { useUserListStore } from './store'\n\nconst userList = useUserListStore()\nconst { getUserById } = storeToRefs(userList)\n// note you will have to use `getUserById.value` to access\n// the function within the <script setup>\n</script>\n\n<template>\n  <p>User 2: {{ getUserById(2) }}</p>\n</template>\n```\n\nNote that when doing this, **getters are not cached anymore**. They are simply functions you invoke. You can, however, cache some results inside of the getter itself, which is uncommon but should prove more performant:\n\n```js\nexport const useStore = defineStore('main', {\n  getters: {\n    getActiveUserById(state) {\n      const activeUsers = state.users.filter((user) => user.active)\n      return (userId) => activeUsers.find((user) => user.id === userId)\n    },\n  },\n})\n```\n\n## Accessing other stores getters\n\nTo use another store's getters, you can directly _use it_ inside of the _getter_:\n\n```js\nimport { useOtherStore } from './other-store'\n\nexport const useStore = defineStore('main', {\n  state: () => ({\n    // ...\n  }),\n  getters: {\n    otherGetter(state) {\n      const otherStore = useOtherStore()\n      return state.localData + otherStore.data\n    },\n  },\n})\n```\n\n## Usage with `setup()`\n\nYou can directly access any getter as a property of the store (exactly like state properties):\n\n```vue\n<script setup>\nconst store = useCounterStore()\n\nstore.count = 3\nstore.doubleCount // 6\n</script>\n```\n\n## Usage with the Options API\n\n<VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-pinia-getters-in-the-options-api\"\n  title=\"Access Pinia Getters via the Options API\"\n/>\n\nFor the following examples, you can assume the following store was created:\n\n```js\n// Example File Path:\n// ./src/stores/counter.js\n\nimport { defineStore } from 'pinia'\n\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    doubleCount(state) {\n      return state.count * 2\n    },\n  },\n})\n```\n\n### With `setup()`\n\nWhile Composition API is not for everyone, the `setup()` hook can make using Pinia easier to work with in the Options API. No extra map helper functions needed!\n\n```vue\n<script>\nimport { useCounterStore } from '../stores/counter'\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore()\n\n    // **only return the whole store** instead of destructuring\n    return { counterStore }\n  },\n  computed: {\n    quadrupleCounter() {\n      return this.counterStore.doubleCount * 2\n    },\n  },\n})\n</script>\n```\n\nThis is useful while migrating a component from the Options API to the Composition API but **should only be a migration step**. Always try not to mix both API styles within the same component.\n\n### Without `setup()`\n\nYou can use the same `mapState()` function used in the [previous section of state](./state.md#options-api) to map to getters:\n\n```js\nimport { mapState } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  computed: {\n    // gives access to this.doubleCount inside the component\n    // same as reading from store.doubleCount\n    ...mapState(useCounterStore, ['doubleCount']),\n    // same as above but registers it as this.myOwnName\n    ...mapState(useCounterStore, {\n      myOwnName: 'doubleCount',\n      // you can also write a function that gets access to the store\n      double: (store) => store.doubleCount,\n    }),\n  },\n}\n```\n"
  },
  {
    "path": "packages/docs/core-concepts/index.md",
    "content": "# Defining a Store\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/define-your-first-pinia-store\"\n  title=\"Learn how to define and use stores in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/651ecff2e4c322668b0a17af\"\n  mp-link=\"https://masteringpinia.com/lessons/quick-start-with-pinia\"\n  title=\"Get started with Pinia\"\n/>\n\nBefore diving into core concepts, we need to know that a store is defined using `defineStore()` and that it requires a **unique** name, passed as the first argument:\n\n```js\nimport { defineStore } from 'pinia'\n\n// You can name the return value of `defineStore()` anything you want,\n// but it's best to use the name of the store and surround it with `use`\n// and `Store` (e.g. `useUserStore`, `useCartStore`, `useProductStore`)\n// the first argument is a unique id of the store across your application\nexport const useAlertsStore = defineStore('alerts', {\n  // other options...\n})\n```\n\nThis _name_, also referred to as _id_, is necessary and is used by Pinia to connect the store to the devtools. Naming the returned function _use..._ is a convention across composables to make its usage idiomatic.\n\n`defineStore()` accepts two distinct values for its second argument: a Setup function or an Options object.\n\n<RuleKitLink />\n\n## Option Stores\n\nSimilar to Vue's Options API, we can also pass an Options Object with `state`, `actions`, and `getters` properties.\n\n```js {2-10}\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({ count: 0, name: 'Eduardo' }),\n  getters: {\n    doubleCount: (state) => state.count * 2,\n  },\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n```\n\nYou can think of `state` as the `data` of the store, `getters` as the `computed` properties of the store, and `actions` as the `methods`.\n\nOption stores should feel intuitive and simple to get started with.\n\n## Setup Stores\n\nThere is also another possible syntax to define stores. Similar to the Vue Composition API's [setup function](https://vuejs.org/api/composition-api-setup.html), we can pass in a function that defines reactive properties and methods and returns an object with the properties and methods we want to expose.\n\n```js\nexport const useCounterStore = defineStore('counter', () => {\n  const count = ref(0)\n  const name = ref('Eduardo')\n  const doubleCount = computed(() => count.value * 2)\n  function increment() {\n    count.value++\n  }\n\n  return { count, name, doubleCount, increment }\n})\n```\n\nIn _Setup Stores_:\n\n- `ref()`s become `state` properties\n- `computed()`s become `getters`\n- `function()`s become `actions`\n\nNote that you **must** return **all state properties** in setup stores for Pinia to pick them up as state. In other words, you cannot have [_private_ state properties in stores](https://masteringpinia.com/blog/how-to-create-private-state-in-stores). Not returning all state properties or **making them readonly** will break [SSR](../cookbook/composables.md), devtools, and other plugins.\n\nSetup stores bring a lot more flexibility than [Option Stores](#Option-Stores) as you can create watchers within a store and freely use any [composable](https://vuejs.org/guide/reusability/composables.html#composables). However, keep in mind that using composables will get more complex when using SSR.\n\nSetup stores are also able to rely on globally _provided_ properties like the Router or the Route. Any property [provided at the App level](https://vuejs.org/api/application.html#app-provide) can be accessed from the store using `inject()`, just like in components:\n\n```ts\nimport { inject } from 'vue'\nimport { useRoute } from 'vue-router'\nimport { defineStore } from 'pinia'\n\nexport const useSearchFilters = defineStore('search-filters', () => {\n  const route = useRoute()\n  // this assumes `app.provide('appProvided', 'value')` was called\n  const appProvided = inject('appProvided')\n\n  // ...\n\n  return {\n    // ...\n  }\n})\n```\n\n:::warning\nDo not return properties like `route` or `appProvided` (from the example above) as they do not belong to the store itself and you can directly access them within components with `useRoute()` and `inject('appProvided')`.\n:::\n\n## What syntax should I pick?\n\nAs with [Vue's Composition API and Options API](https://vuejs.org/guide/introduction.html#which-to-choose), pick the one that you feel the most comfortable with. Both have their strengths and weaknesses. Options stores are easier to work with while Setup stores are more flexible and powerful. If you want to dive deeper into the differences, check the [Option Stores vs Setup Stores chapter](https://masteringpinia.com/lessons/when-to-choose-one-syntax-over-the-other) in Mastering Pinia.\n\n## Using the store\n\nWe are _defining_ a store because the store won't be created until `use...Store()` is called within a component `<script setup>` (or within `setup()` **like all composables**):\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\n\n// access the `store` variable anywhere in the component ✨\nconst store = useCounterStore()\n</script>\n```\n\n:::tip\nIf you are not using `setup` components yet, [you can still use Pinia with _map helpers_](../cookbook/options-api.md).\n:::\n\nYou can define as many stores as you want and **you should define each store in a different file** to get the most out of Pinia (like automatically allowing your bundler to code split and providing TypeScript inference).\n\nOnce the store is instantiated, you can access any property defined in `state`, `getters`, and `actions` directly on the store. We will look at these in detail in the next pages but autocompletion will help you.\n\nNote that `store` is an object wrapped with `reactive`, meaning there is no need to write `.value` after getters but, like `props` in `setup`, **we cannot destructure it**:\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\nimport { computed } from 'vue'\n\nconst store = useCounterStore()\n// ❌ This won't work because it breaks reactivity\n// same as reactive: https://vuejs.org/guide/essentials/reactivity-fundamentals.html#limitations-of-reactive\nconst { name, doubleCount } = store // [!code warning]\nname // will always be \"Eduardo\" // [!code warning]\ndoubleCount // will always be 0 // [!code warning]\n\nsetTimeout(() => {\n  store.increment()\n}, 1000)\n\n// ✅ this one will be reactive\n// 💡 but you could also just use `store.doubleCount` directly\nconst doubleValue = computed(() => store.doubleCount)\n</script>\n```\n\n## Destructuring from a Store\n\nIn order to extract properties from the store while keeping its reactivity, you need to use `storeToRefs()`. It will create refs for every reactive property. This is useful when you are only using state from the store but not calling any action. Note you can destructure actions directly from the store as they are bound to the store itself too:\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\nimport { storeToRefs } from 'pinia'\n\nconst store = useCounterStore()\n// `name` and `doubleCount` are reactive refs\n// This will also extract refs for properties added by plugins\n// but skip any action or non reactive (non ref/reactive) property\nconst { name, doubleCount } = storeToRefs(store)\n// the increment action can just be destructured\nconst { increment } = store\n</script>\n```\n"
  },
  {
    "path": "packages/docs/core-concepts/outside-component-usage.md",
    "content": "# Using a store outside of a component\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/651ed1ec4c2f339c6860fd06\"\n  mp-link=\"https://masteringpinia.com/lessons/how-does-usestore-work\"\n  title=\"Using stores outside of components\"\n/>\n\nPinia stores rely on the `pinia` instance to share the same store instance across all calls. Most of the time, this works out of the box by just calling your `useStore()` function. For example, in `setup()`, you don't need to do anything else. But things are a bit different outside of a component.\nBehind the scenes, `useStore()` _injects_ the `pinia` instance you gave to your `app`. This means that if the `pinia` instance cannot be automatically injected, you have to manually provide it to the `useStore()` function.\nYou can solve this differently depending on the kind of application you are writing.\n\n<RuleKitLink />\n\n## Single Page Applications\n\nIf you are not doing any SSR (Server Side Rendering), any call of `useStore()` after installing the pinia plugin with `app.use(pinia)` will work:\n\n```js\nimport { useUserStore } from '@/stores/user'\nimport { createPinia } from 'pinia'\nimport { createApp } from 'vue'\nimport App from './App.vue'\n\n// ❌  fails because it's called before the pinia is created\nconst userStore = useUserStore()\n\nconst pinia = createPinia()\nconst app = createApp(App)\napp.use(pinia)\n\n// ✅ works because the pinia instance is now active\nconst userStore = useUserStore()\n```\n\nThe easiest way to ensure this is always applied is to _defer_ calls of `useStore()` by placing them inside functions that will always run after pinia is installed.\n\nLet's take a look at this example of using a store inside of a navigation guard with Vue Router:\n\n```js\nimport { createRouter } from 'vue-router'\nconst router = createRouter({\n  // ...\n})\n\n// ❌ Depending on the order of imports this will fail\nconst store = useUserStore()\n\nrouter.beforeEach((to, from, next) => {\n  // we wanted to use the store here\n  if (store.isLoggedIn) next()\n  else next('/login')\n})\n\nrouter.beforeEach((to) => {\n  // ✅ This will work because the router starts its navigation after\n  // the router is installed and pinia will be installed too\n  const store = useUserStore()\n\n  if (to.meta.requiresAuth && !store.isLoggedIn) return '/login'\n})\n```\n\n## SSR Apps\n\nWhen dealing with Server Side Rendering, you will have to pass the `pinia` instance to `useStore()`. This prevents pinia from sharing global state between different application instances.\n\nThere is a whole section dedicated to it in the [SSR guide](/ssr/index.md), this is just a short explanation.\n"
  },
  {
    "path": "packages/docs/core-concepts/plugins.md",
    "content": "# Plugins\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/What-is-a-pinia-plugin\"\n  title=\"Learn all about Pinia plugins\"\n/>\n\nPinia stores can be fully extended thanks to a low level API. Here is a list of things you can do:\n\n- Add new properties to stores\n- Add new options when defining stores\n- Add new methods to stores\n- Wrap existing methods\n- Intercept actions and its results\n- Implement side effects like [Local Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)\n- Apply **only** to specific stores\n\n<RuleKitLink />\n\nPlugins are added to the pinia instance with `pinia.use()`. The simplest example is adding a static property to all stores by returning an object:\n\n```js\nimport { createPinia } from 'pinia'\n\n// add a property named `secret` to every store that is created\n// after this plugin is installed this could be in a different file\nfunction SecretPiniaPlugin() {\n  return { secret: 'the cake is a lie' }\n}\n\nconst pinia = createPinia()\n// give the plugin to pinia\npinia.use(SecretPiniaPlugin)\n\n// in another file\nconst store = useStore()\nstore.secret // 'the cake is a lie'\n```\n\nThis is useful to add global objects like the router, modal, or toast managers.\n\n## Introduction\n\nA Pinia plugin is a function that optionally returns properties to be added to a store. It takes one optional argument, a _context_:\n\n```js\nexport function myPiniaPlugin(context) {\n  context.pinia // the pinia created with `createPinia()`\n  context.app // the current app created with `createApp()`\n  context.store // the store the plugin is augmenting\n  context.options // the options object defining the store passed to `defineStore()`\n  // ...\n}\n```\n\nThis function is then passed to `pinia` with `pinia.use()`:\n\n```js\npinia.use(myPiniaPlugin)\n```\n\nPlugins are only applied to stores created **after the plugins themselves, and after `pinia` is passed to the app**, otherwise they won't be applied.\n\n## Augmenting a Store\n\nYou can add properties to every store by simply returning an object of them in a plugin:\n\n```js\npinia.use(() => ({ hello: 'world' }))\n```\n\nYou can also set the property directly on the `store` but **if possible use the return version so they can be automatically tracked by devtools**:\n\n```js\npinia.use(({ store }) => {\n  store.hello = 'world'\n})\n```\n\nAny property _returned_ by a plugin will be automatically tracked by devtools so in order to make `hello` visible in devtools, make sure to add it to `store._customProperties` **in dev mode only** if you want to debug it in devtools:\n\n```js\n// from the example above\npinia.use(({ store }) => {\n  store.hello = 'world'\n  // make sure your bundler handles this. webpack and vite should do it by default\n  if (process.env.NODE_ENV === 'development') {\n    // add any keys you set on the store\n    store._customProperties.add('hello')\n  }\n})\n```\n\nNote that every store is wrapped with [`reactive`](https://vuejs.org/api/reactivity-core#reactive), automatically unwrapping any Ref (`ref()`, `computed()`, ...) it contains:\n\n```js\nconst sharedRef = ref('shared')\npinia.use(({ store }) => {\n  // each store has its individual `hello` property\n  store.hello = ref('secret')\n  // it gets automatically unwrapped\n  store.hello // 'secret'\n\n  // all stores are sharing the value `shared` property\n  store.shared = sharedRef\n  store.shared // 'shared'\n})\n```\n\nThis is why you can access all computed properties without `.value` and why they are reactive.\n\n### Adding new state\n\nIf you want to add new state properties to a store or properties that are meant to be used during hydration, **you will have to add it in two places**:\n\n- On the `store` so you can access it with `store.myState`\n- On `store.$state` so it can be used in devtools and **be serialized during SSR**.\n\nOn top of that, you will certainly have to use a `ref()` (or other reactive API) in order to share the value across different accesses:\n\n```js\nimport { toRef, ref } from 'vue'\n\npinia.use(({ store }) => {\n  // to correctly handle SSR, we need to make sure we are not overriding an\n  // existing value\n  if (!Object.hasOwn(store.$state, 'hasError')) {\n    // hasError is defined within the plugin, so each store has their individual\n    // state property\n    const hasError = ref(false)\n    // setting the variable on `$state`, allows it be serialized during SSR\n    store.$state.hasError = hasError\n  }\n  // we need to transfer the ref from the state to the store, this way\n  // both accesses: store.hasError and store.$state.hasError will work\n  // and share the same variable\n  // See https://vuejs.org/api/reactivity-utilities.html#toref\n  store.hasError = toRef(store.$state, 'hasError')\n\n  // in this case it's better not to return `hasError` since it\n  // will be displayed in the `state` section in the devtools\n  // anyway and if we return it, devtools will display it twice.\n})\n```\n\nNote that state changes or additions that occur within a plugin (that includes calling `store.$patch()`) happen before the store is active and therefore **do not trigger any subscriptions**.\n\n#### Resetting state added in plugins\n\nBy default, `$reset()` will not reset state added by plugins but you can override it to also reset the state you add:\n\n```js\nimport { toRef, ref } from 'vue'\n\npinia.use(({ store }) => {\n  // this is the same code as above for reference\n  if (!Object.hasOwn(store.$state, 'hasError')) {\n    const hasError = ref(false)\n    store.$state.hasError = hasError\n  }\n  store.hasError = toRef(store.$state, 'hasError')\n\n  // make sure to set the context (`this`) to the store\n  const originalReset = store.$reset.bind(store)\n\n  // override the $reset function\n  return {\n    $reset() {\n      originalReset()\n      store.hasError = false\n    },\n  }\n})\n```\n\n## Adding new external properties\n\nWhen adding external properties, class instances that come from other libraries, or simply things that are not reactive, you should wrap the object with `markRaw()` before passing it to pinia. Here is an example adding the router to every store:\n\n```js\nimport { markRaw } from 'vue'\n// adapt this based on where your router is\nimport { router } from './router'\n\npinia.use(({ store }) => {\n  store.router = markRaw(router)\n})\n```\n\n## Calling `$subscribe` inside plugins\n\nYou can use [store.$subscribe](./state.md#Subscribing-to-the-state) and [store.$onAction](./actions.md#Subscribing-to-actions) inside plugins too:\n\n```ts\npinia.use(({ store }) => {\n  store.$subscribe(() => {\n    // react to store changes\n  })\n  store.$onAction(() => {\n    // react to store actions\n  })\n})\n```\n\n## Adding new options\n\nIt is possible to create new options when defining stores to later on consume them from plugins. For example, you could create a `debounce` option that allows you to debounce any action:\n\n```js\ndefineStore('search', {\n  actions: {\n    searchContacts() {\n      // ...\n    },\n  },\n\n  // this will be read by a plugin later on\n  debounce: {\n    // debounce the action searchContacts by 300ms\n    searchContacts: 300,\n  },\n})\n```\n\nThe plugin can then read that option to wrap actions and replace the original ones:\n\n```js\n// use any debounce library\nimport debounce from 'lodash/debounce'\n\npinia.use(({ options, store }) => {\n  if (options.debounce) {\n    // we are overriding the actions with new ones\n    return Object.keys(options.debounce).reduce((debouncedActions, action) => {\n      debouncedActions[action] = debounce(\n        store[action],\n        options.debounce[action]\n      )\n      return debouncedActions\n    }, {})\n  }\n})\n```\n\nNote that custom options are passed as the 3rd argument when using the setup syntax:\n\n```js\ndefineStore(\n  'search',\n  () => {\n    // ...\n  },\n  {\n    // this will be read by a plugin later on\n    debounce: {\n      // debounce the action searchContacts by 300ms\n      searchContacts: 300,\n    },\n  }\n)\n```\n\n## TypeScript\n\nEverything shown above can be done with typing support, so you don't ever need to use `any` or `@ts-ignore`.\n\n### Typing plugins\n\nA Pinia plugin can be typed as follows:\n\n```ts\nimport { PiniaPluginContext } from 'pinia'\n\nexport function myPiniaPlugin(context: PiniaPluginContext) {\n  // ...\n}\n```\n\n### Typing new store properties\n\nWhen adding new properties to stores, you should also extend the `PiniaCustomProperties` interface.\n\n```ts\nimport 'pinia'\nimport type { Router } from 'vue-router'\n\ndeclare module 'pinia' {\n  export interface PiniaCustomProperties {\n    // by using a setter we can allow both strings and refs\n    set hello(value: string | Ref<string>)\n    get hello(): string\n\n    // you can define simpler values too\n    simpleNumber: number\n\n    // type the router added by the plugin above (#adding-new-external-properties)\n    router: Router\n  }\n}\n```\n\nIt can then be written and read safely:\n\n```ts\npinia.use(({ store }) => {\n  store.hello = 'Hola'\n  store.hello = ref('Hola')\n\n  store.simpleNumber = Math.random()\n  // @ts-expect-error: we haven't typed this correctly\n  store.simpleNumber = ref(Math.random())\n})\n```\n\n`PiniaCustomProperties` is a generic type that allows you to reference properties of a store. Imagine the following example where we copy over the initial options as `$options` (this would only work for option stores):\n\n```ts\npinia.use(({ options }) => ({ $options: options }))\n```\n\nWe can properly type this by using the 4 generic types of `PiniaCustomProperties`:\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface PiniaCustomProperties<Id, S, G, A> {\n    $options: {\n      id: Id\n      state?: () => S\n      getters?: G\n      actions?: A\n    }\n  }\n}\n```\n\n:::tip\nWhen extending types in generics, they must be named **exactly as in the source code**. `Id` cannot be named `id` or `I`, and `S` cannot be named `State`. Here is what every letter stands for:\n\n- S: State\n- G: Getters\n- A: Actions\n- SS: Setup Store / Store\n\n:::\n\n### Typing new state\n\nWhen adding new state properties (to both, the `store` and `store.$state`), you need to add the type to `PiniaCustomStateProperties` instead. Differently from `PiniaCustomProperties`, it only receives the `State` generic:\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface PiniaCustomStateProperties<S> {\n    hello: string\n  }\n}\n```\n\n### Typing new creation options\n\nWhen creating new options for `defineStore()`, you should extend the `DefineStoreOptionsBase`. Differently from `PiniaCustomProperties`, it only exposes two generics: the State and the Store type, allowing you to limit what can be defined. For example, you can use the names of the actions:\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface DefineStoreOptionsBase<S, Store> {\n    // allow defining a number of ms for any of the actions\n    debounce?: Partial<Record<keyof StoreActions<Store>, number>>\n  }\n}\n```\n\n:::tip\n\nThere is also a `StoreGetters` type to extract the _getters_ from a Store type. You can also extend the options of _setup stores_ or _option stores_ **only** by extending the types `DefineStoreOptions` and `DefineSetupStoreOptions` respectively.\n\n:::\n\n## Nuxt\n\nWhen [using pinia alongside Nuxt](../ssr/nuxt.md), you will have to create a [Nuxt plugin](https://nuxt.com/docs/guide/directory-structure/plugins) first. This will give you access to the `pinia` instance:\n\n```ts{14-16}\n// plugins/myPiniaPlugin.ts\nimport { PiniaPluginContext } from 'pinia'\n\nfunction MyPiniaPlugin({ store }: PiniaPluginContext) {\n  store.$subscribe((mutation) => {\n    // react to store changes\n    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)\n  })\n\n  // Note this has to be typed if you are using TS\n  return { creationTime: new Date() }\n}\n\nexport default defineNuxtPlugin(({ $pinia }) => {\n  $pinia.use(MyPiniaPlugin)\n})\n```\n\n::: info\n\nThe above example is using TypeScript, you have to remove the type annotations `PiniaPluginContext` and `Plugin` as well as their imports if you are using a `.js` file.\n\n:::\n\n## Existing plugins\n\nYou can check existing [Pinia plugins on GitHub](https://github.com/topics/pinia-plugin) with the topic _pinia-plugin_.\n"
  },
  {
    "path": "packages/docs/core-concepts/state.md",
    "content": "# State\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-state-from-a-pinia-store\"\n  title=\"Learn all about state in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/the-3-pillars-of-pinia-state\"\n  title=\"Learn all about state in Pinia\"\n/>\n\nThe state is, most of the time, the central part of your store. People often start by defining the state that represents their app. In Pinia the state is defined as a function that returns the initial state. This allows Pinia to work in both Server and Client Side.\n\n```js\nimport { defineStore } from 'pinia'\n\nexport const useStore = defineStore('storeId', {\n  // arrow function recommended for full type inference\n  state: () => {\n    return {\n      // all these properties will have their type inferred automatically\n      count: 0,\n      name: 'Eduardo',\n      isAdmin: true,\n      items: [],\n      hasChanged: true,\n    }\n  },\n})\n```\n\n:::tip\n\nIn order for Vue to properly detect state, you must declare every state piece in `state`, even if its initial value is `undefined`.\n\n:::\n\n<RuleKitLink />\n\n## TypeScript\n\nYou don't need to do much in order to make your state compatible with TS: make sure [`strict`](https://www.typescriptlang.org/tsconfig#strict), or at the very least, [`noImplicitThis`](https://www.typescriptlang.org/tsconfig#noImplicitThis), is enabled and Pinia will infer the type of your state automatically! However, there are a few cases where you should give it a hand with some casting:\n\n```ts\nexport const useUserStore = defineStore('user', {\n  state: () => {\n    return {\n      // for initially empty lists\n      userList: [] as UserInfo[],\n      // for data that is not yet loaded\n      user: null as UserInfo | null,\n    }\n  },\n})\n\ninterface UserInfo {\n  name: string\n  age: number\n}\n```\n\nIf you prefer, you can define the state with an interface and type the return value of `state()`:\n\n```ts\ninterface State {\n  userList: UserInfo[]\n  user: UserInfo | null\n}\n\nexport const useUserStore = defineStore('user', {\n  state: (): State => {\n    return {\n      userList: [],\n      user: null,\n    }\n  },\n})\n\ninterface UserInfo {\n  name: string\n  age: number\n}\n```\n\n## Accessing the `state`\n\nBy default, you can directly read from and write to the state by accessing it through the `store` instance:\n\n```ts\nconst store = useStore()\n\nstore.count++\n```\n\nYes, this means **no verbose wrappers** like in Vuex, you can directly bind that to `v-model`:\n\n```vue-html\n<input v-model=\"store.count\" type=\"number\" />\n```\n\n::: info\n\nYou cannot add a new state property **if you don't define it in `state()`**. It must contain the initial state. e.g.: we can't do `store.secondCount = 2` if `secondCount` is not defined in `state()`.\n\n:::\n\n## Resetting the state\n\nIn [Option Stores](/core-concepts/index.md#option-stores), you can _reset_ the state to its initial value by calling the `$reset()` method on the store:\n\n```js\nconst store = useStore()\n\nstore.$reset()\n```\n\nInternally, this calls the `state()` function to create a new state object and replaces the current state with it.\n\nIn [Setup Stores](/core-concepts/index.md#setup-stores), you need to create your own `$reset()` method:\n\n```ts\nexport const useCounterStore = defineStore('counter', () => {\n  const count = ref(0)\n\n  function $reset() {\n    count.value = 0\n  }\n\n  return { count, $reset }\n})\n```\n\n### Usage with the Options API\n\n<VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-pinia-state-in-the-options-api\"\n  title=\"Access Pinia State via the Options API\"\n/>\n\nFor the following examples, you can assume the following store was created:\n\n```js\n// Example File Path:\n// ./src/stores/counter.js\n\nimport { defineStore } from 'pinia'\n\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n})\n```\n\nIf you are not using the Composition API, and you are using `computed`, `methods`, ..., you can use the `mapState()` helper to map state properties as readonly computed properties:\n\n```js\nimport { mapState } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  computed: {\n    // gives access to this.count inside the component\n    // same as reading from store.count\n    ...mapState(useCounterStore, ['count'])\n    // same as above but registers it as this.myOwnName\n    ...mapState(useCounterStore, {\n      myOwnName: 'count',\n      // you can also write a function that gets access to the store\n      double: store => store.count * 2,\n      // it can have access to `this` but it won't be typed correctly...\n      magicValue(store) {\n        return store.someGetter + this.count + this.double\n      },\n    }),\n  },\n}\n```\n\n#### Modifiable state\n\nIf you want to be able to write to these state properties (e.g. if you have a form), you can use `mapWritableState()` instead. Note you cannot pass a function like with `mapState()`:\n\n```js\nimport { mapWritableState } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  computed: {\n    // gives access to this.count inside the component and allows setting it\n    // this.count++\n    // same as reading from store.count\n    ...mapWritableState(useCounterStore, ['count']),\n    // same as above but registers it as this.myOwnName\n    ...mapWritableState(useCounterStore, {\n      myOwnName: 'count',\n    }),\n  },\n}\n```\n\n:::tip\nYou don't need `mapWritableState()` for collections like arrays unless you are replacing the whole array with `cartItems = []`, `mapState()` still allows you to call methods on your collections.\n:::\n\n## Mutating the state\n\n<!-- TODO: disable this with `strictMode` -->\n\nApart from directly mutating the store with `store.count++`, you can also call the `$patch` method. It allows you to apply multiple changes at the same time with a partial `state` object:\n\n```js\nstore.$patch({\n  count: store.count + 1,\n  age: 120,\n  name: 'DIO',\n})\n```\n\nHowever, some mutations are really hard or costly to apply with this syntax: any collection modification (e.g. pushing, removing, splicing an element from an array) requires you to create a new collection. Because of this, the `$patch` method also accepts a function to group these kinds of mutations that are difficult to apply with a patch object:\n\n```js\nstore.$patch((state) => {\n  state.items.push({ name: 'shoes', quantity: 1 })\n  state.hasChanged = true\n})\n```\n\n<!-- TODO: disable this with `strictMode`, `{ noDirectPatch: true }` -->\n\nThe main difference here is that `$patch()` allows you to group multiple changes into one single entry in the devtools. Note that **both direct changes to `state` and `$patch()` are tracked in devtools** and can be time traveled.\n\n## Replacing the `state`\n\nYou **cannot exactly replace** the state of a store as that would break reactivity. You can however _patch it_:\n\n```js\n// this doesn't actually replace `$state`\nstore.$state = { count: 24 }\n// it internally calls `$patch()`:\nstore.$patch({ count: 24 })\n```\n\nYou can also **set the initial state** of your whole application by changing the `state` of the `pinia` instance. This is used during [SSR for hydration](../ssr/#state-hydration).\n\n```js\npinia.state.value = {}\n```\n\n## Subscribing to the state\n\nYou can watch the state and its changes through the `$subscribe()` method of a store, similar to Vuex's [subscribe method](https://vuex.vuejs.org/api/#subscribe). The advantage of using `$subscribe()` over a regular `watch()` is that _subscriptions_ will trigger only once after _patches_ (e.g. when using the function version from above).\n\n```js\ncartStore.$subscribe((mutation, state) => {\n  // import { MutationType } from 'pinia'\n  mutation.type // 'direct' | 'patch object' | 'patch function'\n  // same as cartStore.$id\n  mutation.storeId // 'cart'\n  // only available with mutation.type === 'patch object'\n  mutation.payload // patch object passed to cartStore.$patch()\n\n  // persist the whole state to the local storage whenever it changes\n  localStorage.setItem('cart', JSON.stringify(state))\n})\n```\n\n### Flush timing\n\nUnder the hood, `$subscribe()` uses Vue's `watch()` function. You can pass the same options as you would with `watch()`. This is useful when you want to immediately trigger subscriptions after **each** state change:\n\n```ts{4}\ncartStore.$subscribe((mutation, state) => {\n  // persist the whole state to the local storage whenever it changes\n  localStorage.setItem('cart', JSON.stringify(state))\n}, { flush: 'sync' })\n```\n\n### Detaching subscriptions\n\nBy default, _state subscriptions_ are bound to the component where they are added (if the store is inside a component's `setup()`). Meaning, they will be automatically removed when the component is unmounted. If you also want to keep them after the component is unmounted, pass `{ detached: true }` as the second argument to _detach_ the _state subscription_ from the current component:\n\n```vue\n<script setup>\nconst someStore = useSomeStore()\n\n// this subscription will be kept even after the component is unmounted\nsomeStore.$subscribe(callback, { detached: true })\n</script>\n```\n\n:::tip\nYou can _watch_ the whole state on the `pinia` instance with a single `watch()`:\n\n```js\nwatch(\n  pinia.state,\n  (state) => {\n    // persist the whole state to the local storage whenever it changes\n    localStorage.setItem('piniaState', JSON.stringify(state))\n  },\n  { deep: true }\n)\n```\n\n:::\n"
  },
  {
    "path": "packages/docs/getting-started.md",
    "content": "# Getting Started\n\n## Installation\n\n<VueMasteryLogoLink for=\"pinia-cheat-sheet\">\n</VueMasteryLogoLink>\n\nInstall `pinia` with your favorite package manager:\n\n::: code-group\n\n```bash [npm]\nnpm install pinia\n```\n\n```bash [yarn]\nyarn add pinia\n```\n\n```bash [pnpm]\npnpm add pinia\n```\n\n```bash [bun]\nbun add pinia\n```\n\n:::\n\n:::tip\nIf your app is using Vue <2.7, you also need to install the composition api: `@vue/composition-api`. If you are using Nuxt, you should follow [these instructions](/ssr/nuxt.md).\n:::\n\nIf you are using the Vue CLI, you can instead give this [**unofficial plugin**](https://github.com/wobsoriano/vue-cli-plugin-pinia) a try.\n\nCreate a pinia instance (the root store) and pass it to the app as a plugin:\n\n```js {2,5-6,8}\nimport { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\n\nconst pinia = createPinia()\nconst app = createApp(App)\n\napp.use(pinia)\napp.mount('#app')\n```\n\n## What is a Store?\n\nA Store (like Pinia) is an entity holding state and business logic that isn't bound to your Component tree. In other words, **it hosts global state**. It's a bit like a component that is always there and that everybody can read off and write to. It has **three concepts**, the [state](./core-concepts/state.md), [getters](./core-concepts/getters.md) and [actions](./core-concepts/actions.md) and it's safe to assume these concepts are the equivalent of `data`, `computed` and `methods` in components.\n\n<RuleKitLink />\n\n## When should I use a Store\n\nA store should contain data that can be accessed throughout your application. This includes data that is used in many places, e.g. User information that is displayed in the navbar, as well as data that needs to be preserved through pages, e.g. a very complicated multi-step form.\n\nOn the other hand, you should avoid including in the store local data that could be hosted in a component instead, e.g. the visibility of an element local to a page.\n\nNot all applications need access to a global state, but if yours need one, Pinia will make your life easier.\n\n## When should I **not** use a Store\n\nSometimes we end up using a store for too many things. If you feel like your application is over using stores, you might want to re consider the purposes of your stores. Namely, if some of their logic should just be composables or if some of their state should be local to a component. This is covered in depth in the [(Not) Overusing stores](https://masteringpinia.com/lessons/not-overusing-stores) lesson of Mastering Pinia.\n"
  },
  {
    "path": "packages/docs/index.md",
    "content": "---\nlayout: home\n\ntitle: Pinia\ntitleTemplate: The intuitive store for Vue.js\n\nhero:\n  name: Pinia\n  text: The intuitive store for Vue.js\n  tagline: Type Safe, Extensible, and Modular by design. Forget you are even using a store.\n  image:\n    src: /logo.svg\n    alt: Pinia\n  actions:\n    - theme: brand\n      text: Get Started\n      link: /introduction\n    - theme: alt\n      text: Demo\n      link: https://stackblitz.com/github/piniajs/example-vue-3-vite\n    - theme: cta rulekit\n      text: RuleKit\n      link: https://rulekit.dev?from=pinia\n    - theme: cta mastering-pinia\n      text: ' '\n      link: https://masteringpinia.com\n    - theme: cta vueschool\n      text: Watch Video Introduction\n      link: https://vueschool.io/lessons/introduction-to-pinia?friend=vuerouter&utm_source=pinia&utm_medium=link&utm_campaign=homepage\n    - theme: cta vue-mastery\n      text: Get the Pinia Cheat Sheet\n      link: https://www.vuemastery.com/pinia?coupon=PINIA-DOCS&via=eduardo\n\nfeatures:\n  - title: 💡 Intuitive\n    details: Stores are as familiar as components. API designed to let you write well organized stores.\n  - title: 🔑 Type Safe\n    details: Types are inferred, which means stores provide you with autocompletion even in JavaScript!\n  - title: ⚙️ Devtools support\n    details: Pinia hooks into Vue devtools to give you an enhanced development experience.\n  - title: 🔌 Extensible\n    details: React to store changes and actions to extend Pinia with transactions, local storage synchronization, etc.\n  - title: 🏗 Modular by design\n    details: Build multiple stores and let your bundler code split them automatically.\n  - title: 📦 Extremely light\n    details: Pinia weighs ~1.5kb, you will forget it's even there!\n---\n\n<script setup>\nimport HomeSponsors from './.vitepress/theme/components/HomeSponsors.vue'\nimport './.vitepress/theme/styles/home-links.css'\n</script>\n\n<HomeSponsors />\n"
  },
  {
    "path": "packages/docs/introduction.md",
    "content": "# Introduction\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/introduction-to-pinia\"\n  title=\"Get started with Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/651ecf274c2f339c6860e36b\"\n  mp-link=\"https://masteringpinia.com/lessons/the-what-and-why-of-state-management-and-stores\"\n  title=\"Create your own Pinia from scratch\"\n/>\n\nPinia [started](https://github.com/vuejs/pinia/commit/06aeef54e2cad66696063c62829dac74e15fd19e) as an experiment to redesign what a Store for Vue could look like with the [Composition API](https://github.com/vuejs/composition-api) around November 2019. Since then, the initial principles have remained the same and Vue 2 support has been dropped in 2025, but Pinia **doesn't require you to use the composition API**.\n\n## Why should I use Pinia?\n\n<!--\nhttps://masteringpinia.com/lessons/why-use-pinia\n -->\n\nPinia is a store library for Vue, it allows you to share a state across components/pages. If you are familiar with the Composition API, you might be thinking you can already share a global state with a simple `export const state = reactive({})`. This is true for single page applications but **exposes your application to [security vulnerabilities](https://vuejs.org/guide/scaling-up/ssr.html#cross-request-state-pollution)** if it is server side rendered. But even in small single page applications, you get a lot from using Pinia:\n\n- Testing utilities\n- Plugins: extend Pinia features with plugins\n- Proper TypeScript support or **autocompletion** for JS users\n- Server Side Rendering support\n- Devtools support\n  - A timeline to track actions and mutations\n  - Stores appear in components where they are used\n  - Time travel and easier debugging\n- Hot module replacement\n  - Modify your stores without reloading your page\n  - Keep any existing state while developing\n\nIf you still have doubts, check out [the **official** Mastering Pinia course](https://masteringpinia.com). In the beginning we cover how to build our own `defineStore()` function and then we move to the official Pinia API.\n\n<RuleKitLink />\n\n<VueMasteryLogoLink for=\"pinia-cheat-sheet\">\n</VueMasteryLogoLink>\n\n## Basic example\n\nThis is what using Pinia looks like in terms of API (make sure to check the [Getting Started](./getting-started.md) for complete instructions). You start by creating a store:\n\n```js\n// stores/counter.js\nimport { defineStore } from 'pinia'\n\nexport const useCounterStore = defineStore('counter', {\n  state: () => {\n    return { count: 0 }\n  },\n  // could also be defined as\n  // state: () => ({ count: 0 })\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n```\n\nAnd then you _use_ it in a component:\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\n\nconst counter = useCounterStore()\n\ncounter.count++\n// with autocompletion ✨\ncounter.$patch({ count: counter.count + 1 })\n// or using an action instead\ncounter.increment()\n</script>\n\n<template>\n  <!-- Access the state directly from the store -->\n  <div>Current Count: {{ counter.count }}</div>\n</template>\n```\n\n[Try it in the Playground](https://play.pinia.vuejs.org/#eNqNVM1O3DAQfpVpVGkXQWIQLYfVgqCIQ3toq9JjLsEZWNPEtuzJstUqb9IH6HP1STq2k/2hFeKyG49nvvnmsz+vsytri2WH2Sybe+mUJfBInb0otWqtcQRr6Dxem04TulsyDqGHe2damBRCpnDx6CelLrU02hMMQTh/Xjg9SEmpJv4fHpZaCHhStICqIyNNaxskZTT8+fV7m/zWViQX03UCn409Eggcwgn0DM5IxnFXpR+g0lDJCKSYFFb1Fkxp6bBFTYHQXKSxeWBeEHL/ipBXAPM3eQ5XUqL3QAsET7wDtXIoqfmZREjxoEqep6JaLS+uO+cYH+L0M1gPvDeE+34uQl5ov2mZHWVJ8rytLEtqNB/KOmCWw4YvMwYLkRCzSqsqRMpMxO8CfZvfOfPk45GU2dGYesknLGpckjGNzyurUtmCyPqZELLWnF9jo5au0EhC21b8U3N5VrwvTkSj7gQ3EkrXuNpvwxV5je1r0MfUy+Pi5F1xFlGXpwNoG1ADaF/qnmUhzzfrXj08EyVcFtWg+2LDOe+LUzWNefoUY+Q63FCUC5Q//hN/9KvE+qtDlm+JO2NR5R6Q0vbN7Wdc8fdmszV113D2C5vf0JumCxxT2odO10x7Jy+y/RjPmO/ud3+zItR+HCoQjWrE/Cjz9Qujb+meFqc7Km7NyhJuzF3jvdK4b+x4m6KjcRXTkrGfvwPnu8XTyYA/OUpUoltmMD2A84uRnOOnxWnuOtj4OHAbB2P3cripoWq8gTt2WkTntR+29yC3jwGjsJFh8LvfSLHj8zEEbFjlt29PiKTu4bc/yPq/puS2IQ==)\n\nYou can even use a function (similar to a component `setup()`) to define a Store for more advanced use cases:\n\n```js\nexport const useCounterStore = defineStore('counter', () => {\n  const count = ref(0)\n  function increment() {\n    count.value++\n  }\n\n  return { count, increment }\n})\n```\n\n[Try it in the Playground](https://play.pinia.vuejs.org/#eNqNVEFu2zAQ/MpWKGAHscQGaXMwnCBpkEN7aIumR10Uah0zlUiCXCkuDP2kD+i7+pIuSVt20iLoSeJydnZ2yOUmu7K26DvM5tnCS6csgUfq7EWpVWuNI9hA5/HadJrQ3ZJxCAMsnWlhUgiZwsWDn5S61NJoT7ANwvnzxOlRAqWc+D0+LrUQ8KhoBVVHRprWNkjKaPj989ce/NpWJFfTTSKf72okEjiGExiYnJmM46pK30OloZKRSLEorOo9mdLSYYuagqCFSG1zw7wg5PoVIa8AFq/yHK6kRO+BVgieeAdq5VBS8yOZkOLBlTxPSbXqL64755gfYvdz2Gx1j4KHYSECLpQfS2azLFmet5VlS43mQ9kEznK74cuMyUIkxKzSqgqRMhPxv0Df5nfOPPp4JGU220Ev+YRFjT0Z0/i8siqlrYisnwsha834GhvVu0IjCW1b8VfO5VnxrjgRjboTXEgoXeP6aRnOyGts/4d9B718U5y8Lc4ia3+6JW0DayAdSj2wLeT5Zi3V/TNTwmVRDbrPNpzzU3OqpjGPH2OMXIejRLlC+f0f8Qe/Tqq/OGT7ejxoiyp3j5S2b24/4Zr/x83W1F3D6Bc2v6I3TRc0Jtj7Ttcs+wAX1X6IZ8x395u/WRNqv2sqCI1uRHy0+fqF1vdyT4vTAxf3w8oWjsPtcDkONBPzHI9bNS6VxqczHy9aHHZcR1ia+edPxPlh8nSyLT2ZwfQIzi+S1oPXgvGsY/qG5xFg2end4I5zuusuoou+ajoMT0fsLXwcv1lOs+YImO1TY/NH2fAHelGuuQ==)\n\nIf you are still not into `setup()` and Composition API, don't worry, Pinia also supports a similar set of [_map helpers_ like Vuex](https://vuex.vuejs.org/guide/state.html#the-mapstate-helper). You define stores the same way but then use `mapStores()`, `mapState()`, or `mapActions()`:\n\n```js {22,24,28}\nconst useCounterStore = defineStore('counter', {\n  state: () => ({ count: 0 }),\n  getters: {\n    double: (state) => state.count * 2,\n  },\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n\nconst useUserStore = defineStore('user', {\n  // ...\n})\n\nexport default defineComponent({\n  computed: {\n    // other computed properties\n    // ...\n    // gives access to this.counterStore and this.userStore\n    ...mapStores(useCounterStore, useUserStore),\n    // gives read access to this.count and this.double\n    ...mapState(useCounterStore, ['count', 'double']),\n  },\n  methods: {\n    // gives access to this.increment()\n    ...mapActions(useCounterStore, ['increment']),\n  },\n})\n```\n\n[Try it in the Playground](https://play.pinia.vuejs.org/#eNqdVcFy0zAQ/RWNL0lpIrUUesikmRTooRyAoXDCHBxrm6i1JY8kp5nJ+N9ZS7bsOIFhekmk1b7dt0/a9T66LQq6LSGaRXOTalHYRSxFXihtyZ5weBQSPircS5CWVORRq5yMEDDqueVJ8WCVBjPxy8SCW92mVihpAqwQUiR9YGkweCktaIcPjpSl3kyfzMD/pzl2RnPjGUvYOV9knpSZ++9XMN7HkpAUt6UFPiNuSwhjRNkN6HBCCq0K0FaACR6U0rBeiy0YkqQpGEOsInYjDG04e3aJ5N5ak3MmD8YoQa7xoP7JQYFnk0E6DQk/mbNLxlW5ygaZ8DaOE/0aOeRoQkYeM/rt81XuNwe7Udz0BTpZspCphrwW9qyftLn4U2kDop+wQvSchfeHGwt5kSFz3BEy52K7cIGQ0B4vqQvZCFBVc1Y7Be9Prijn7us7dFmV1ipJlmkm0uebOAqs4mhx367nzLshZM4CoWgS+fc4xULx1SmJveNkwjDuwMRREC6O3KOvLXHE3JqCyacrrV78q42j5p7jaIl9xThsrVKZmSaF8LCNtYWZMZZyif4cMrHVVIJlssjZEWZ5Td/TS5aJFcNETEgOu8M0iJhyyP8neuu6vKCX7+i1i7q9aoLmdVR3hXiDKIs1qZKPYj0Qpe4pkYH+WrhHcSBOkmXq5bOzWV1CoJhuIH0+YX8yO8/6G7YP6C30yrKJXgNeYH189/AFdrgOh7niJTbGvw6/g1FZWXP0bh9KyZF2z8+xvXd3LOT6h7nbWZCmLaom2nWQk7meO38rvaN7Ra96KnaTDyUcTOLDwdeO0zD0UH5jj4bqTR889n0PGjvfUTH1fJiR8Rm5WZBx01wzckEq357IEb27SeC7CQEO6FBu1TTiG/K2N0YSPwcCuDcuWhPpzbHzc2/z4HYwoCbNgH+9IN1XY6BGHbmVop3xLmn1B2TmaJo=)\n\nYou will find more information about each _map helper_ in the core concepts.\n\n## Official Course\n\nThe official course for Pinia is [Mastering Pinia](https://masteringpinia.com). Written by Pinia's author, it covers everything from the basics to advanced topics like plugins, testing, and server-side rendering. It is the best way to get started with Pinia and to master it.\n\n## Why _Pinia_\n\nPinia (pronounced `/piːnjʌ/`, like \"peenya\" in English) is the closest word to _piña_ (_pineapple_ in Spanish) that is a valid package name. A pineapple is in reality a group of individual flowers that join together to create a multiple fruit. Similar to stores, each one is born individually, but they are all connected at the end. It's also a delicious tropical fruit indigenous to South America.\n\n## A more realistic example\n\nHere is a more complete example of the API you will be using with Pinia **with types even in JavaScript**. For some people, this might be enough to get started without reading further but we still recommend checking the rest of the documentation or even skipping this example and coming back once you have read about all of the _Core Concepts_.\n\n```js\nimport { defineStore } from 'pinia'\n\nexport const useTodos = defineStore('todos', {\n  state: () => ({\n    /** @type {{ text: string, id: number, isFinished: boolean }[]} */\n    todos: [],\n    /** @type {'all' | 'finished' | 'unfinished'} */\n    filter: 'all',\n    // type will be automatically inferred to number\n    nextId: 0,\n  }),\n  getters: {\n    finishedTodos(state) {\n      // autocompletion! ✨\n      return state.todos.filter((todo) => todo.isFinished)\n    },\n    unfinishedTodos(state) {\n      return state.todos.filter((todo) => !todo.isFinished)\n    },\n    /**\n     * @returns {{ text: string, id: number, isFinished: boolean }[]}\n     */\n    filteredTodos(state) {\n      if (this.filter === 'finished') {\n        // call other getters with autocompletion ✨\n        return this.finishedTodos\n      } else if (this.filter === 'unfinished') {\n        return this.unfinishedTodos\n      }\n      return this.todos\n    },\n  },\n  actions: {\n    // any amount of arguments, return a promise or not\n    addTodo(text) {\n      // you can directly mutate the state\n      this.todos.push({ text, id: this.nextId++, isFinished: false })\n    },\n  },\n})\n```\n\n[Try it in the Playground](https://play.pinia.vuejs.org/#eNqtVs1y2zYQfpU1L5QdmUzGbQ4cyWO3k86kh7STuKcwB4pcWohJgIMfWRqVb9IH6HP1SboA+Cu7nkzbiygQu99++Haxy2Nw2zTRzmCQBCuVS9ZoUKhNc51yVjdCajiCxBJaKKWoISTTcLKltJB4Jz5iqQaThnGWTY2MIpNCjBZRrO06+qrILOW54EqDe/XJ4sF6cFmc99tHKFmlUS67JxY95nrKYjHCkGvvzPHRWt/hXpM5nWcRhm67NDzXTHDICoe3OIdjygFYCYuziVe0yyqD3SYQgjaS3AFaiwIT8lGP9NTbGj55S3xCUoFwVrFPAElPC411U2UaaQWwqrINVtcrxhujYXdZiwKrdRp4KdIA9KFBWsusYIKWDpnWWVWlwTXcVtUq9hD/Ba2kxKotFhbyp+7//4Fr+BT5t2E1w95K/zR+baMxilEKSQhWfmB8XhoUIXnAQ7cdMYvuXcn5lKM3Uf2xRrL5FvOHjdhPnI9Hl+9I23JqKXMOMa6YZxh3FDs5/PYHfATLKumsT+NP6mKMbQPQ6oZO0UhUKkJOx7N59TXWcZrptDFaUz0nBVPZpsKCrKeFbOHyiuUPM5TbgsT2noSyiofiC5aBv8aXddbQfRWcGoW7BGm3QTIn/bVIA3f37Zs0iN3/CFV9uZHiUaEk/zRY9qY31EriAndaiEpdZg3zblutG5XEcV5wsidx2E5GHHXMmzp+4nPzNvo+ekPSb2IKFDNe4H4ehjwuC6y/Bb03vXkdvfkueutQd1cdaG1RuxvfkixaUWsp2f2JKLmoG1ah/KWxbWUuDt1G8fize6elwYGiK7Fn3n9VVHWW9a+UfJQ7nBxLZ/IeKZt2+92nDy6zwyYVlanI+oXNj6hEZSxHb/aD4QXRntg5tu9djhm/v1Pv9hq56g9liTo1nL2T+ccXjj7SvYqupip2c4AEHMZFgdQA0E+C05mSctw7M9/Xh8mynnotQgcbLn18pamSE6DWvr6GRUcpvriAG3vN3G0mhRKyk3TQJbAiAW7qjZ01Y0dIYENFhxmH9vOXFi5ij+MiJfD5S6fbBDckBUP4HcK+n7nF2OzCEcX3rQScS48UuzYAj6yqYIOQGS3qTLOcbA7U7EqU1OmIQEfWe5E++j2Rfe1Q2nP3IOkJnmh2h+8Z+BHr9BlGmwtsY9lKrtCm8gz++uPPftePPi9q5NPn2S/c6HUinzRTN/j6UgEYFXg+/rdEOHs5BGWhQ6NseDz17xLdw8wS9U/M7VeD3rKeL6zXNNyHdE8Mncg2kSD0lgy7BFGu9fZE/Kn2gzZdkImKvUkLWCl8nsmk9GZcpqAnyRlgT5LjbF1upsL738x9UY3VZuuJHyCrheEaRAnUC0xNo0wte7gMGrrmjIgLCVxo79h/SdmszevzIAzJx6FgEnNN16E2NhVEC33d9LYjz6gxarvwJeBT7/b8fXn1al4BZWZFbGdVZX/b86D9GztAvyY=)\n\n## Comparison with Vuex\n\nPinia started out as an exploration of what the next iteration of Vuex could look like, incorporating many ideas from core team discussions for Vuex 5. Eventually, we realized that Pinia already implements most of what we wanted in Vuex 5, and decided to make it the new recommendation instead.\n\nCompared to Vuex, Pinia provides a simpler API with less ceremony, offers Composition-API-style APIs, and most importantly, has solid type inference support when used with TypeScript.\n\n### RFCs\n\nInitially Pinia didn't go through any RFC process. I tested out ideas based on my experience developing applications, reading other people's code, working for clients who use Pinia, and answering questions on Discord.\nThis allowed me to provide a solution that works and is adapted to a variety of cases and application sizes. I used to publish often and made the library evolve while keeping its core API the same.\n\nNow that Pinia has become the default state management solution, it is subject to the same RFC process as other core libraries in the Vue ecosystem and its API has entered a stable state.\n\n### Comparison with Vuex 3.x/4.x\n\n> Vuex 3.x is Vuex for Vue 2 while Vuex 4.x is for Vue 3\n\nPinia API is very different from Vuex ≤4, namely:\n\n- _mutations_ no longer exist. They were often perceived as **_extremely_ verbose**. They initially brought devtools integration but that is no longer an issue.\n- No need to create custom complex wrappers to support TypeScript, everything is typed and the API is designed in a way to leverage TS type inference as much as possible.\n- No more magic strings to inject, import the functions, call them, enjoy autocompletion!\n- No need to dynamically add stores, they are all dynamic by default and you won't even notice. Note you can still manually use a store to register it whenever you want but because it is automatic you don't need to worry about it.\n- No more nested structuring of _modules_. You can still nest stores implicitly by importing and _using_ a store inside another but Pinia offers a flat structuring by design while still enabling ways of cross composition among stores. **You can even have circular dependencies of stores**.\n- No _namespaced modules_. Given the flat architecture of stores, \"namespacing\" stores is inherent to how they are defined and you could say all stores are namespaced.\n\nFor more detailed instructions on how to convert an existing Vuex ≤4 project to use Pinia, see the [Migration from Vuex Guide](./cookbook/migration-vuex.md).\n"
  },
  {
    "path": "packages/docs/package.json",
    "content": "{\n  \"name\": \"@pinia/docs\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"predocs\": \"node run-typedoc.mjs\",\n    \"docs\": \"vitepress dev .\",\n    \"docs:api\": \"node run-typedoc.mjs\",\n    \"docs:translation:compare\": \"v-translation compare\",\n    \"docs:translation:update\": \"v-translation update\",\n    \"docs:translation:status\": \"v-translation status\",\n    \"docs:build\": \"vitepress build .\",\n    \"docs:preview\": \"vitepress preview .\"\n  },\n  \"dependencies\": {\n    \"@chenfengyuan/vue-countdown\": \"^2.1.3\",\n    \"@vueuse/core\": \"^14.0.0\",\n    \"pinia\": \"workspace:*\",\n    \"typedoc-vitepress-theme\": \"^1.1.2\",\n    \"vitepress\": \"1.6.4\",\n    \"vitepress-translation-helper\": \"^0.2.2\",\n    \"vue-use-spring\": \"^0.3.3\"\n  }\n}\n"
  },
  {
    "path": "packages/docs/run-typedoc.mjs",
    "content": "import path from 'node:path'\nimport { createTypeDocApp } from './typedoc-markdown.mjs'\n\nconst __dirname = path.dirname(new URL(import.meta.url).pathname)\n\ncreateTypeDocApp({\n  textContentMappings: {\n    'title.indexPage': 'API Reference',\n    'title.memberPage': '{name}',\n  },\n  tsconfig: path.resolve(__dirname, './typedoc.tsconfig.json'),\n  // entryPointStrategy: 'packages',\n  categorizeByGroup: true,\n  githubPages: false,\n  readme: 'none',\n  indexFormat: 'table',\n  disableSources: true,\n  plugin: ['typedoc-plugin-markdown', 'typedoc-vitepress-theme'],\n  useCodeBlocks: true,\n  entryPoints: [\n    path.resolve(__dirname, '../pinia/src/index.ts'),\n    path.resolve(__dirname, '../testing/src/index.ts'),\n    path.resolve(__dirname, '../nuxt/src/module.ts'),\n  ],\n}).then((app) => app.build())\n"
  },
  {
    "path": "packages/docs/ssr/index.md",
    "content": "# Server Side Rendering (SSR)\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/ssr-friendly-state\"\n  title=\"Learn about SSR best practices\"\n/>\n\n:::tip\nIf you are using **Nuxt,** you need to read [**these instructions**](./nuxt.md) instead.\n:::\n\nCreating stores with Pinia should work out of the box for SSR as long as you call your `useStore()` functions at the top of `setup` functions, `getters` and `actions`:\n\n```vue\n<script setup>\n// this works because pinia knows what application is running inside of\n// `setup`\nconst main = useMainStore()\n</script>\n```\n\n<RuleKitLink />\n\n## Using the store outside of `setup()`\n\nIf you need to use the store somewhere else, you need to pass the `pinia` instance [that was passed to the app](../getting-started.md#installation) to the `useStore()` function call:\n\n```js\nconst pinia = createPinia()\nconst app = createApp(App)\n\napp.use(router)\napp.use(pinia)\n\nrouter.beforeEach((to) => {\n  // ✅ This will work make sure the correct store is used for the\n  // current running app\n  const main = useMainStore(pinia)\n\n  if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'\n})\n```\n\nPinia conveniently adds itself as `$pinia` to your app so you can use it in functions like `serverPrefetch()`:\n\n```js\nexport default {\n  serverPrefetch() {\n    const store = useStore(this.$pinia)\n  },\n}\n```\n\nNote you don't need to do anything special when using `onServerPrefetch()`:\n\n```vue\n<script setup>\nconst store = useStore()\nonServerPrefetch(async () => {\n  // ✅ this will work\n  await store.fetchData()\n})\n</script>\n```\n\n## State hydration\n\nTo hydrate the initial state, you need to make sure the rootState is included somewhere in the HTML for Pinia to pick it up later on. Depending on what you are using for SSR, **you should escape the state for security reasons**. We recommend using [devalue](https://github.com/Rich-Harris/devalue) which is the one used by Nuxt:\n\n```js\nimport devalue from 'devalue'\nimport { createPinia } from 'pinia'\n// retrieve the rootState server side\nconst pinia = createPinia()\nconst app = createApp(App)\napp.use(router)\napp.use(pinia)\n\n// after rendering the page, the root state is built and can be read directly\n// on `pinia.state.value`.\n\n// serialize, escape (VERY important if the content of the state can be changed\n// by the user, which is almost always the case), and place it somewhere on\n// the page, for example, as a global variable.\ndevalue(pinia.state.value)\n```\n\nDepending on what you are using for SSR, you will set an _initial state_ variable that will be serialized in the HTML. You should also protect yourself from XSS attacks. You can use [other alternatives](https://github.com/Rich-Harris/devalue#see-also) to `devalue` depending on what you need, e.g. if you can serialize and parse your state with `JSON.stringify()`/`JSON.parse()`, **you could improve your performance by a lot**.\n\nIf you are not using Nuxt you will need to handle the serialization and hydration of the state yourself. Here are some examples:\n\n- [Vitesse template](https://github.com/antfu/vitesse/blob/main/src/modules/pinia.ts)\n- [vite-plugin-ssr](https://vite-plugin-ssr.com/pinia)\n\nAdapt this strategy to your environment. **Make sure to hydrate pinia's state before calling any `useStore()` function** on client side. For example, if we serialize the state into a `<script>` tag to make it accessible globally on client side through `window.__pinia`, we can write this:\n\n```ts\nconst pinia = createPinia()\nconst app = createApp(App)\napp.use(pinia)\n\n// `isClient` depends on the environment, e.g. on Nuxt it's `import.meta.client`\nif (isClient) {\n  pinia.state.value = JSON.parse(window.__pinia)\n}\n```\n"
  },
  {
    "path": "packages/docs/ssr/nuxt.md",
    "content": "# Nuxt\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/ssr-friendly-state\"\n  title=\"Learn about SSR best practices\"\n/>\n\nUsing Pinia with [Nuxt](https://nuxt.com/) is easier since Nuxt takes care of a lot of things when it comes to _server side rendering_. For instance, **you don't need to care about serialization nor XSS attacks**. Pinia supports Nuxt 3 and 4.\n\n<RuleKitLink />\n\n## Installation\n\n```bash\nnpx nuxi@latest module add pinia\n```\n\nThis will add both `@pinia/nuxt` and `pinia` to your project. **If you notice that `pinia` is not installed, please install it manually** with your package manager: `npm i pinia`.\n\n:::tip\nIf you're using npm, you might encounter an _ERESOLVE unable to resolve dependency tree_ error. In that case, add the following to your `package.json`:\n\n```js\n\"overrides\": {\n  \"vue\": \"latest\"\n}\n```\n\n:::\n\nWe supply a _module_ to handle everything for you, you only need to add it to `modules` in your `nuxt.config.js` file:\n\n```js\n// nuxt.config.js\nexport default defineNuxtConfig({\n  // ... other options\n  modules: [\n    // ...\n    '@pinia/nuxt',\n  ],\n})\n```\n\nAnd that's it, use your store as usual!\n\n## Awaiting for actions in pages\n\nAs with `onServerPrefetch()`, you can call a store action within the `callOnce()` composable.\nThis will allow Nuxt to run the action only once and avoids refetching data that is already present.\n\n```vue{3-4}\n<script setup>\nconst store = useStore()\n// we could also extract the data, but it's already present in the store\nawait callOnce('user', () => store.fetchUser())\n</script>\n```\n\nDepending on your requirements, you can choose to run the action only once on the client, or on every navigation (which is closer to data fetching behavior of `useFetch()`/`useAsyncData()`)\n\n```vue{3}\n<script setup>\nconst store = useStore()\nawait callOnce('user', () => store.fetchUser(), { mode: 'navigation' })\n</script>\n```\n\n::: tip\n\nIf you want to use a store outside of `setup()` or an _injection aware_ context (e.g. Navigation guards, other stores, Nuxt Middlewares, etc), remember to pass the `pinia` instance to `useStore()`, for the reasons alluded to [here](https://pinia.vuejs.org/core-concepts/outside-component-usage.html#SSR-Apps). Retrieving the `pinia` instance might vary.\n\n```ts\nimport { useStore } from '~/stores/myStore'\n\n// this line is usually inside a function that is able to retrieve\n// the pinia instance\nconst store = useStore(pinia)\n```\n\nFortunately, most of the time you **don't need to go through this hassle**.\n\n:::\n\n## Auto imports\n\nBy default `@pinia/nuxt` exposes a few auto imports:\n\n- `usePinia()`, which is similar to `getActivePinia()` but works better with Nuxt.\n- `defineStore()` to define stores\n- `storeToRefs()` when you need to extract individual refs from a store\n- `acceptHMRUpdate()` for [hot module replacement](../cookbook/hot-module-replacement.md)\n\nIt also automatically imports **all stores** defined within your `stores` folder (`app/stores` in Nuxt 4). It doesn't lookup for nested stores though. You can customize this behavior by setting the `storesDirs` option:\n\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n  // ... other options\n  modules: ['@pinia/nuxt'],\n  pinia: {\n    storesDirs: ['./stores/**', './custom-folder/stores/**'],\n  },\n})\n```\n\nNote the folders are relative to the root of your project. If you change the `srcDir` option, you need to adapt the paths accordingly.\n"
  },
  {
    "path": "packages/docs/typedoc-markdown.mjs",
    "content": "// @ts-check\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { Application, TSConfigReader, PageEvent } from 'typedoc'\n\nconst __dirname = path.dirname(new URL(import.meta.url).pathname)\n\n/** @satisfies {Partial<import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions>} */\nconst DEFAULT_OPTIONS = {\n  cleanOutputDir: true,\n  excludeInternal: true,\n  out: path.resolve(__dirname, './api'),\n  entryFileName: 'index.md',\n  // hideBreadcrumbs: false,\n  preserveAnchorCasing: true,\n}\n\n/**\n *\n * @param {Partial<import('typedoc').TypeDocOptions>} config\n */\nexport async function createTypeDocApp(config = {}) {\n  const options = {\n    ...DEFAULT_OPTIONS,\n    ...config,\n  }\n\n  const app = await Application.bootstrapWithPlugins(options)\n\n  // If you want TypeDoc to load tsconfig.json / typedoc.json files\n  app.options.addReader(new TSConfigReader())\n\n  app.renderer.on(\n    PageEvent.END,\n    /**\n     *\n     * @param {import('typedoc').PageEvent} page\n     */\n    (page) => {\n      if (!page.contents) {\n        return\n      }\n      page.contents = prependYAML(page.contents, {\n        // TODO: figure out a way to point to the source files?\n        editLink: false,\n      })\n    }\n  )\n\n  async function serve() {\n    app.convertAndWatch(handleProject)\n  }\n\n  async function build() {\n    if (\n      (await exists(options.out)) &&\n      (await fs.stat(options.out)).isDirectory()\n    ) {\n      await fs.rm(options.out, { recursive: true })\n    }\n    const project = await app.convert()\n    return handleProject(project)\n  }\n\n  /**\n   *\n   * @param {import('typedoc').ProjectReflection | undefined} project\n   */\n  async function handleProject(project) {\n    if (project) {\n      // Rendered docs\n      try {\n        await app.generateOutputs(project)\n        app.logger.info(`generated at ${options.out}.`)\n      } catch (error) {\n        app.logger.error(error)\n      }\n    } else {\n      app.logger.error('No project')\n    }\n  }\n\n  return {\n    build,\n    serve,\n  }\n}\n\nasync function exists(path) {\n  try {\n    await fs.access(path)\n    return true\n  } catch {\n    return false\n  }\n}\n\n/**\n * @typedef {Record<string, string | number | boolean>} FrontMatterVars\n */\n\n/**\n * Prepends YAML block to a string\n * @param {string} contents - string to prepend to\n * @param {FrontMatterVars} vars - object of required front matter variables\n */\nfunction prependYAML(contents, vars) {\n  return contents\n    .replace(/^/, toYAML(vars) + '\\n\\n')\n    .replace(/[\\r\\n]{3,}/g, '\\n\\n')\n}\n\n/**\n * Converts YAML object to a YAML string\n * @param {FrontMatterVars} vars\n */\nfunction toYAML(vars) {\n  const yaml = `---\n${Object.entries(vars)\n  .map(\n    ([key, value]) =>\n      `${key}: ${\n        typeof value === 'string' ? `\"${escapeDoubleQuotes(value)}\"` : value\n      }`\n  )\n  .join('\\n')}\n---`\n  return yaml\n}\n\n/**\n * Escapes double quotes in a string\n * @param {string} str - string to escape\n */\nfunction escapeDoubleQuotes(str) {\n  return str.replace(/\"/g, '\\\\\"')\n}\n"
  },
  {
    "path": "packages/docs/typedoc.tsconfig.json",
    "content": "{\n  \"include\": [\"../pinia/src/global.d.ts\", \"../*/src/**/*.ts\"],\n  \"exclude\": [\n    \"../test-vue-2\",\n    \"../pinia/__tests__/test-utils.ts\",\n    \"../pinia/test-dts\",\n    \"../*/__tests__/**/*.ts\",\n    \"../*/src/*.spec.ts\",\n    \"../nuxt/playground\",\n    \"../playground\",\n    \"../online-playground\",\n    \"../nuxt/src/runtime\",\n    \"**/*.spec.ts\"\n  ],\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"rootDir\": \"..\",\n    \"outDir\": \"dist\",\n    \"sourceMap\": false,\n    \"noEmit\": true,\n    \"paths\": {\n      \"@pinia/*\": [\"../*/src\"],\n      \"pinia\": [\"../pinia/src\"]\n    },\n\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n\n    \"noUnusedLocals\": true,\n    \"strictNullChecks\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitThis\": true,\n    \"noImplicitReturns\": false,\n    \"strict\": true,\n    \"isolatedModules\": true,\n\n    \"experimentalDecorators\": true,\n    \"esModuleInterop\": true,\n    \"removeComments\": false,\n    \"jsx\": \"preserve\",\n    \"lib\": [\"esnext\", \"dom\"],\n    \"types\": [\"vitest\", \"node\", \"vite/client\"]\n  }\n}\n"
  },
  {
    "path": "packages/docs/vite-typedoc-plugin.ts",
    "content": "import { Plugin } from 'vite'\nimport _fs from 'fs'\nimport { TypeDocOptions } from 'typedoc'\nimport { createTypeDocApp } from './typedoc-markdown'\n\nexport default function TypeDocPlugin(\n  config: Partial<TypeDocOptions> = {}\n): Plugin {\n  const { serve, setTargetMode } = createTypeDocApp(config)\n  setTargetMode('serve')\n\n  return {\n    name: 'typedoc',\n    apply: 'serve',\n    buildStart() {\n      return serve()\n    },\n  }\n}\n"
  },
  {
    "path": "packages/docs/vite.config.ts",
    "content": "import { defineConfig, type Plugin } from 'vite'\nimport _fs from 'fs'\nimport path from 'path'\n// import TypeDocPlugin from './vite-typedoc-plugin'\n\nconst fs = _fs.promises\n\nexport default defineConfig({\n  clearScreen: false,\n  plugins: [\n    ...(process.env.NETLIFY ? [] : [copyPiniaPlugin()]),\n    // TODO: actual plugin that works well\n    // TypeDocPlugin({\n    //   name: 'Pinia',\n    //   entryPoints: [\n    //     path.resolve(__dirname, '../pinia/src/index.ts'),\n    //     path.resolve(__dirname, '../testing/src/index.ts'),\n    //     path.resolve(__dirname, '../nuxt/src/index.ts'),\n    //   ],\n    // }),\n  ],\n  define: {\n    __DEV__: 'true',\n    __BROWSER__: 'true',\n  },\n  optimizeDeps: {\n    exclude: ['@vueuse/shared', '@vueuse/core', 'pinia'],\n  },\n})\n\nfunction copyPiniaPlugin(): Plugin {\n  return {\n    name: 'copy-pinia',\n    async generateBundle() {\n      const filePath = path.resolve(__dirname, '../pinia/dist/pinia.mjs')\n\n      // throws if file doesn't exist\n      await fs.access(filePath)\n\n      this.emitFile({\n        type: 'asset',\n        fileName: 'pinia.mjs',\n        source: await fs.readFile(filePath, 'utf-8'),\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/docs/zh/api/enums/pinia.MutationType.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / MutationType\n\n# Enumeration: MutationType %{#enumeration-mutationtype}%\n\n[pinia](../modules/pinia.md).MutationType\n\nSubscriptionCallback 的可能类型\n\n## Enumeration Members %{#enumeration-members}%\n\n### direct %{#direct}%\n\n• **direct** = `\"direct\"`\n\nDirect mutation of the state:\n\n- `store.name = 'new name'`\n- `store.$state.name = 'new name'`\n- `store.list.push('new item')`\n\n---\n\n### patchFunction %{#patchfunction}%\n\n• **patchFunction** = `\"patch function\"`\n\n通过 `$patch` 和一个函数更改 state：\n\n- `store.$patch(state => state.name = 'newName')`\n\n---\n\n### patchObject %{#patchobject}%\n\n• **patchObject** = `\"patch object\"`\n\n通过 `$patch` 和一个对象更改 state：\n\n- `store.$patch({ name: 'newName' })`\n"
  },
  {
    "path": "packages/docs/zh/api/index.md",
    "content": "API 文档\n\n# API 文档 %{#api-documentation}%\n\n## 模块 %{#modules}%\n\n- [@pinia/nuxt](modules/pinia_nuxt.md)\n- [@pinia/testing](modules/pinia_testing.md)\n- [pinia](modules/pinia.md)\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.DefineSetupStoreOptions.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / DefineSetupStoreOptions\n\n# 接口：DefineSetupStoreOptions<Id, S, G, A\\> %{#interface-definesetupstoreoptions-id-s-g-a}%\n\n[pinia](../modules/pinia.md).DefineSetupStoreOptions\n\n`defineStore()` 的选项参数，用于设置 store。\n可以通过插件 API 扩展来增强 store 的功能。\n\n**`See`**\n\n[DefineStoreOptionsBase](pinia.DefineStoreOptionsBase.md).\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 | 类型                                                 |\n| :--- | :--------------------------------------------------- |\n| `Id` | extends `string`                                     |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | `G`                                                  |\n| `A`  | `A`                                                  |\n\n## 层次结构 %{#hierarchy}%\n\n- [`DefineStoreOptionsBase`](pinia.DefineStoreOptionsBase.md)<`S`, [`Store`](../modules/pinia.md#store)<`Id`, `S`, `G`, `A`\\>\\>\n\n  ↳ **`DefineSetupStoreOptions`**\n\n## 属性\n\n### actions %{#actions}%\n\n• `Optional` **actions**: `A`\n\n提取的 action。由 useStore() 添加。不应该由用户在创建 store 时添加。\n可以在插件中使用，以获得用 setup 函数定义的 store 中的 action 列表。\n注意这个属性一定是会定义的。\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.DefineStoreOptions.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / DefineStoreOptions\n\n# 接口：DefineStoreOptions<Id, S, G, A\\> %{#interface-definestoreoptions-id-s-g-a}%\n\n[pinia](../modules/pinia.md).DefineStoreOptions\n\n用于 option store 的 `defineStore()` 的配置参数。\n可以通过插件 API 扩展来增强 store。\n\n**`See`**\n\n[DefineStoreOptionsBase](pinia.DefineStoreOptionsBase.md).\n\n## 类型参数 %{#type-parameters}%\n\n| 名字 | 类型                                                 |\n| :--- | :--------------------------------------------------- |\n| `Id` | extends `string`                                     |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | `G`                                                  |\n| `A`  | `A`                                                  |\n\n## 层次结构 %{#hierarchy}%\n\n- [`DefineStoreOptionsBase`](pinia.DefineStoreOptionsBase.md)<`S`, [`Store`](../modules/pinia.md#store)<`Id`, `S`, `G`, `A`\\>\\>\n\n  ↳ **`DefineStoreOptions`**\n\n## 属性\n\n### actions %{#actions}%\n\n• `Optional` **actions**: `A` & `ThisType`<`A` & `UnwrapRef`<`S`\\> & [`_StoreWithState`](pinia._StoreWithState.md)<`Id`, `S`, `G`, `A`\\> & [`_StoreWithGetters`](../modules/pinia.md#_storewithgetters)<`G`\\> & [`PiniaCustomProperties`](pinia.PiniaCustomProperties.md)<`string`, [`StateTree`](../modules/pinia.md#statetree), [`_GettersTree`](../modules/pinia.md#_getterstree)<[`StateTree`](../modules/pinia.md#statetree)\\>, [`_ActionsTree`](../modules/pinia.md#_actionstree)\\>\\>\n\naction 的可选对象\n\n---\n\n### getters %{#getters}%\n\n• `Optional` **getters**: `G` & `ThisType`<`UnwrapRef`<`S`\\> & [`_StoreWithGetters`](../modules/pinia.md#_storewithgetters)<`G`\\> & [`PiniaCustomProperties`](pinia.PiniaCustomProperties.md)<`string`, [`StateTree`](../modules/pinia.md#statetree), [`_GettersTree`](../modules/pinia.md#_getterstree)<[`StateTree`](../modules/pinia.md#statetree)\\>, [`_ActionsTree`](../modules/pinia.md#_actionstree)\\>\\> & [`_GettersTree`](../modules/pinia.md#_getterstree)<`S`\\>\n\ngetter 的可选对象\n\n---\n\n### id %{#id}%\n\n• **id**: `Id`\n\n唯一的字符串密钥，用于识别整个应用中的 store。\n\n---\n\n### state %{#state}%\n\n• `Optional` **state**: () => `S`\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (): `S`\n\n创建一个新 state 的函数。\n**必须是一个箭头函数**，以确保正确的类型标注!\n\n##### 返回值\n\n`S`\n\n## 方法 %{#methods}%\n\n### hydrate %{#hydrate}%\n\n▸ `Optional` **hydrate**(`storeState`, `initialState`): `void`\n\n当 store 定义中使用了复杂的 state (如仅客户端的引用)，并且仅从 `pinia.state` 中复制值是不够时，\n允许在 SSR 期间对 store 进行 hydrating。\n\n**`Example`**\n\n如果在你的 `state` 中，你使用了任何在服务器和客户端有不同值的 `customRef`、`computed` 或 `ref`，\n你需要手动激活它们。\n例如，一个存储在本地存储的自定义 ref：\n\n```ts\nconst useStore = defineStore('main', {\n  state: () => ({\n    n: useLocalStorage('key', 0),\n  }),\n  hydrate(storeState, initialState) {\n    // @ts-expect-error: https://github.com/microsoft/TypeScript/issues/43826\n    storeState.n = useLocalStorage('key', 0)\n  },\n})\n```\n\n#### 参数\n\n| 名字           | 类型              | 描述                           |\n| :------------- | :---------------- | :----------------------------- |\n| `storeState`   | `UnwrapRef`<`S`\\> | the current state in the store |\n| `initialState` | `UnwrapRef`<`S`\\> | initialState                   |\n\n#### 返回值\n\n`void`\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.DefineStoreOptionsBase.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / DefineStoreOptionsBase\n\n# 接口：DefineStoreOptionsBase<S, Store\\> %{#interface-definestoreoptionsbase-s-store}%\n\n[pinia](../modules/pinia.md).DefineStoreOptionsBase\n\n传递给 `defineStore()` 的选项，在 option store 和 setup store 之间是通用的。\n如果你想为这两种 store 添加自定义的选项，\n请扩展这个接口。\n\n## 类型参数 %{#type-parameters}%\n\n| Name    | Type                                                 |\n| :------ | :--------------------------------------------------- |\n| `S`     | extends [`StateTree`](../modules/pinia.md#statetree) |\n| `Store` | `Store`                                              |\n\n## 层次结构 %{#hierarchy}%\n\n- **`DefineStoreOptionsBase`**\n\n  ↳ [`DefineStoreOptions`](pinia.DefineStoreOptions.md)\n\n  ↳ [`DefineSetupStoreOptions`](pinia.DefineSetupStoreOptions.md)\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.DefineStoreOptionsInPlugin.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / DefineStoreOptionsInPlugin\n\n# 接口：DefineStoreOptionsInPlugin<Id, S, G, A\\> %{#interface-definestoreoptionsinplugin-id-s-g-a}%\n\n[pinia](../modules/pinia.md).DefineStoreOptionsInPlugin\n\n创建 pinia 插件时可用的 `options`。\n\n## 类型参数 %{#type-parameters}%\n\n| 名字 | 类型                                                 |\n| :--- | :--------------------------------------------------- |\n| `Id` | extends `string`                                     |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | `G`                                                  |\n| `A`  | `A`                                                  |\n\n## 层次结构 %{#hierarchy}%\n\n- `Omit`<[`DefineStoreOptions`](pinia.DefineStoreOptions.md)<`Id`, `S`, `G`, `A`\\>, `\"id\"` \\| `\"actions\"`\\>\n\n  ↳ **`DefineStoreOptionsInPlugin`**\n\n## 属性\n\n### actions %{#actions}%\n\n• **actions**: `A`\n\n提取的 action 对象。当使用 setup API 建立 store 时，由 useStore() 添加，\n否则使用传递给 `defineStore()` 的对象。\n如果没有定义 action，则默认为一个空对象。\n\n---\n\n### getters %{#getters}%\n\n• `Optional` **getters**: `G` & `ThisType`<`UnwrapRef`<`S`\\> & [`_StoreWithGetters`](../modules/pinia.md#_storewithgetters)<`G`\\> & [`PiniaCustomProperties`](pinia.PiniaCustomProperties.md)<`string`, [`StateTree`](../modules/pinia.md#statetree), [`_GettersTree`](../modules/pinia.md#_getterstree)<[`StateTree`](../modules/pinia.md#statetree)\\>, [`_ActionsTree`](../modules/pinia.md#_actionstree)\\>\\> & [`_GettersTree`](../modules/pinia.md#_getterstree)<`S`\\>\n\ngetter 的可选对象\n\n#### 继承于\n\nOmit.getters\n\n---\n\n### state\n\n• `Optional` **state**: () => `S`\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (): `S`\n\n创建一个新 state 的函数。\n**必须是一个箭头函数**，以确保正确的类型标注!\n\n#### 返回值\n\n`S`\n\n#### 继承于\n\nOmit.state\n\n## 方法 %{#methods}%\n\n### hydrate %{#hydrate}%\n\n▸ `Optional` **hydrate**(`storeState`, `initialState`): `void`\n\n当 store 定义中使用了复杂的 state (如仅客户端的引用)，并且从 `pinia.state` 中复制值是不够时，\n允许在 SSR 期间对 store 进行 hydrating。\n\n**`Example`**\n\n如果在你的 `state` 中，你使用了任何 `customRef`，任何 `computed`，或任何在服务器和客户端有不同值的 `ref`，\n你需要手动激活它们。\n例如，一个存储在本地存储的自定义 ref：\n\n```ts\nconst useStore = defineStore('main', {\n  state: () => ({\n    n: useLocalStorage('key', 0),\n  }),\n  hydrate(storeState, initialState) {\n    // @ts-expect-error: https://github.com/microsoft/TypeScript/issues/43826\n    storeState.n = useLocalStorage('key', 0)\n  },\n})\n```\n\n#### 参数\n\n| 名字           | 类型              | 描述                           |\n| :------------- | :---------------- | :----------------------------- |\n| `storeState`   | `UnwrapRef`<`S`\\> | the current state in the store |\n| `initialState` | `UnwrapRef`<`S`\\> | initialState                   |\n\n#### 返回值\n\n`void`\n\n#### 继承于\n\nOmit.hydrate\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.MapStoresCustomization.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / MapStoresCustomization\n\n# 接口：MapStoresCustomization %{#interface-mapstorescustomization}%\n\n[pinia](../modules/pinia.md).MapStoresCustomization\n\n允许自定义映射辅助函数的接口。用以下属性来扩展这个接口：\n\n- `suffix`: 字符串。影响 `mapStores()` 的后缀，默认为`Store`。\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.Pinia.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / Pinia\n\n# 接口：Pinia %{#interface-pinia}%\n\n[pinia](../modules/pinia.md).Pinia\n\nEvery application must own its own pinia to be able to create stores\n\n## 层次结构 %{#hierarchy}%\n\n- **`Pinia`**\n\n  ↳ [`TestingPinia`](pinia_testing.TestingPinia.md)\n\n## 属性\n\n### 安装 %{#install}%\n\n• **install**: (`app`: `App`<`any`\\>) => `void`\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (`app`): `void`\n\n##### 参数\n\n| Name  | Type          |\n| :---- | :------------ |\n| `app` | `App`<`any`\\> |\n\n##### 返回值\n\n`void`\n\n---\n\n### state %{#state}%\n\n• **state**: `Ref`<`Record`<`string`, [`StateTree`](../modules/pinia.md#statetree)\\>\\>\n\n根 state\n\n## 方法 %{#methods}%\n\n### use %{#use}%\n\n▸ **use**(`plugin`): [`Pinia`](pinia.Pinia.md)\n\n添加 store 插件来扩展每一个 store\n\n#### 参数 %{#paramters}%\n\n| Name     | Type                                  | Description         |\n| :------- | :------------------------------------ | :------------------ |\n| `plugin` | [`PiniaPlugin`](pinia.PiniaPlugin.md) | store plugin to add |\n\n#### 返回值\n\n[`Pinia`](pinia.Pinia.md)\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.PiniaCustomProperties.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / PiniaCustomProperties\n\n# 接口：PiniaCustomProperties<Id, S, G, A\\> %{#interface-piniacustomproperties-id-s-g-a}%\n\n[pinia](../modules/pinia.md).PiniaCustomProperties\n\n当用户通过插件添加属性时，接口可被扩展。\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 | 类型                                                                                                |\n| :--- | :-------------------------------------------------------------------------------------------------- |\n| `Id` | extends `string` = `string`                                                                         |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) = [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | [`_GettersTree`](../modules/pinia.md#_getterstree)<`S`\\>                                            |\n| `A`  | [`_ActionsTree`](../modules/pinia.md#_actionstree)                                                  |\n\n## Accessors %{#accessors}%\n\n### route %{#route}%\n\n• `get` **route**(): `RouteLocationNormalized`\n\n#### 返回值\n\n`RouteLocationNormalized`\n\n• `set` **route**(`value`): `void`\n\n#### 参数\n\n| Name    | Type                                                                       |\n| :------ | :------------------------------------------------------------------------- |\n| `value` | `RouteLocationNormalizedLoaded` \\| `Ref`<`RouteLocationNormalizedLoaded`\\> |\n\n#### 返回值\n\n`void`\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.PiniaCustomStateProperties.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / PiniaCustomStateProperties\n\n# 接口：PiniaCustomStateProperties<S\\> %{#interface-piniacustomstateproperties-s}%\n\n[pinia](../modules/pinia.md).PiniaCustomStateProperties\n\n通过 `pinia.use()` 添加到每个 `store.$state` 的属性。\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 | 类型                                                                                                |\n| :--- | :-------------------------------------------------------------------------------------------------- |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) = [`StateTree`](../modules/pinia.md#statetree) |\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.PiniaPlugin.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / PiniaPlugin\n\n# 接口：PiniaPlugin %{#interface-piniaplugin}%\n\n[pinia](../modules/pinia.md).PiniaPlugin\n\n## Callable %{#callable}%\n\n### PiniaPlugin %{#piniaplugin}%\n\n▸ **PiniaPlugin**(`context`): `void` \\| `Partial`<[`PiniaCustomProperties`](pinia.PiniaCustomProperties.md)<`string`, [`StateTree`](../modules/pinia.md#statetree), [`_GettersTree`](../modules/pinia.md#_getterstree)<[`StateTree`](../modules/pinia.md#statetree)\\>, [`_ActionsTree`](../modules/pinia.md#_actionstree)\\> & [`PiniaCustomStateProperties`](pinia.PiniaCustomStateProperties.md)<[`StateTree`](../modules/pinia.md#statetree)\\>\\>\n\n用于扩展每个 store 的插件。返回一个扩展 store 的对象或\n没有返回值。\n\n#### 参数\n\n| 名称      | 类型                                                                                                                                                                                                                                                                | 描述    |\n| :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------ |\n| `context` | [`PiniaPluginContext`](pinia.PiniaPluginContext.md)<`string`, [`StateTree`](../modules/pinia.md#statetree), [`_GettersTree`](../modules/pinia.md#_getterstree)<[`StateTree`](../modules/pinia.md#statetree)\\>, [`_ActionsTree`](../modules/pinia.md#_actionstree)\\> | Context |\n\n#### 返回值\n\n`void` \\| `Partial`<[`PiniaCustomProperties`](pinia.PiniaCustomProperties.md)<`string`, [`StateTree`](../modules/pinia.md#statetree), [`_GettersTree`](../modules/pinia.md#_getterstree)<[`StateTree`](../modules/pinia.md#statetree)\\>, [`_ActionsTree`](../modules/pinia.md#_actionstree)\\> & [`PiniaCustomStateProperties`](pinia.PiniaCustomStateProperties.md)<[`StateTree`](../modules/pinia.md#statetree)\\>\\>\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.PiniaPluginContext.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / PiniaPluginContext\n\n# 接口：PiniaPluginContext<Id, S, G, A\\> %{#interface-piniaplugincontext-id-s-g-a}%\n\n[pinia](../modules/pinia.md).PiniaPluginContext\n\n传递给 Pinia 插件的上下文参数。\n\n## 类型参数 %{#type-parameters}%\n\n| Name | Type                                                                                                |\n| :--- | :-------------------------------------------------------------------------------------------------- |\n| `Id` | extends `string` = `string`                                                                         |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) = [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | [`_GettersTree`](../modules/pinia.md#_getterstree)<`S`\\>                                            |\n| `A`  | [`_ActionsTree`](../modules/pinia.md#_actionstree)                                                  |\n\n## 属性\n\n### app %{#app}%\n\n• **app**: `App`<`any`\\>\n\n用 `Vue.createApp()`创建的当前应用。\n\n---\n\n### options %{#options}%\n\n• **options**: [`DefineStoreOptionsInPlugin`](pinia.DefineStoreOptionsInPlugin.md)<`Id`, `S`, `G`, `A`\\>\n\n调用 `defineStore()` 时定义 store 的初始选项。\n\n---\n\n### pinia %{#pinia}%\n\n• **pinia**: [`Pinia`](pinia.Pinia.md)\n\npinia 实例\n\n---\n\n### store %{#store}%\n\n• **store**: [`Store`](../modules/pinia.md#store)<`Id`, `S`, `G`, `A`\\>\n\n目前正在扩展的 store\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.StoreDefinition.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / StoreDefinition\n\n# 接口：StoreDefinition<Id, S, G, A\\> %{#interface-storedefinition-id-s-g-a}%\n\n[pinia](../modules/pinia.md).StoreDefinition\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 | 类型                                                                                                |\n| :--- | :-------------------------------------------------------------------------------------------------- |\n| `Id` | extends `string` = `string`                                                                         |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) = [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | [`_GettersTree`](../modules/pinia.md#_getterstree)<`S`\\>                                            |\n| `A`  | [`_ActionsTree`](../modules/pinia.md#_actionstree)                                                  |\n\n## Callable %{#callable}%\n\n### StoreDefinition %{#storedefinition}%\n\n▸ **StoreDefinition**(`pinia?`, `hot?`): [`Store`](../modules/pinia.md#store)<`Id`, `S`, `G`, `A`\\>\n\n返回一个 store，有需要才创建它。\n\n#### 参数\n\n| 名称     | 类型                                               | 描述                                 |\n| :------- | :------------------------------------------------- | :----------------------------------- |\n| `pinia?` | `null` \\| [`Pinia`](pinia.Pinia.md)                | Pinia instance to retrieve the store |\n| `hot?`   | [`StoreGeneric`](../modules/pinia.md#storegeneric) | dev only hot module replacement      |\n\n#### 返回值\n\n[`Store`](../modules/pinia.md#store)<`Id`, `S`, `G`, `A`\\>\n\n## 属性\n\n### $id %{#id}%\n\n• **$id**: `Id`\n\nstore 的 id。供映射辅助函数使用。\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.StoreProperties.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / StoreProperties\n\n# 接口：StoreProperties<Id\\> %{#interface-storeproperties-id}%\n\n[pinia](../modules/pinia.md).StoreProperties\n\nstore 的属性。\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 | 类型             |\n| :--- | :--------------- |\n| `Id` | extends `string` |\n\n## 层次结构 %{#hierarchy}%\n\n- **`StoreProperties`**\n\n  ↳ [`_StoreWithState`](pinia._StoreWithState.md)\n\n## 属性\n\n### $id %{#id}%\n\n• **$id**: `Id`\n\nstore 的唯一标识符\n\n---\n\n### \\_customProperties %{#customproperties}%\n\n• **\\_customProperties**: `Set`<`string`\\>\n\n供 devtools 插件使用，用于检索插件添加的属性。\n在生产环境中会被移除。\n开发者可用于添加应在 devtools 中显示的 store 的属性键。\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.SubscriptionCallbackMutationDirect.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / SubscriptionCallbackMutationDirect\n\n# 接口：SubscriptionCallbackMutationDirect %{#interface-subscriptioncallbackmutationdirect}%\n\n[pinia](../modules/pinia.md).SubscriptionCallbackMutationDirect\n\n当用 `store.someState = newValue`\n或 `store.$state.someState = newValue` 直接改变 store 的状态时，\n传递给订阅回调的上下文。\n\n## 层次结构 %{#hierarchy}%\n\n- [`_SubscriptionCallbackMutationBase`](pinia._SubscriptionCallbackMutationBase.md)\n\n  ↳ **`SubscriptionCallbackMutationDirect`**\n\n## 属性\n\n### 事件 %{#events}%\n\n• **events**: `DebuggerEvent`\n\n只支持开发环境。不同的 mutation 调用。\n\n---\n\n### storeId %{#storeid}%\n\n• **storeId**: `string`\n\n执行 mutation 的 store 的 `id`\n\n#### 继承于\n\n[\\_SubscriptionCallbackMutationBase](pinia._SubscriptionCallbackMutationBase.md).[storeId](pinia._SubscriptionCallbackMutationBase.md#storeid)\n\n---\n\n### 类型 %{#type}%\n\n• **type**: [`direct`](../enums/pinia.MutationType.md#direct)\n\nmutation 的类型\n\n#### 重写 %{#overrides}%\n\n[\\_SubscriptionCallbackMutationBase](pinia._SubscriptionCallbackMutationBase.md).[type](pinia._SubscriptionCallbackMutationBase.md#type)\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.SubscriptionCallbackMutationPatchFunction.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / SubscriptionCallbackMutationPatchFunction\n\n# 接口：SubscriptionCallbackMutationPatchFunction %{#interface-subscriptioncallbackmutationpatchfunction}%\n\n[pinia](../modules/pinia.md).SubscriptionCallbackMutationPatchFunction\n\n当 `store.$patch()` 被一个函数调用时，\n传递给订阅回调的上下文。\n\n## 层次结构 %{#hierarchy}%\n\n- [`_SubscriptionCallbackMutationBase`](pinia._SubscriptionCallbackMutationBase.md)\n\n  ↳ **`SubscriptionCallbackMutationPatchFunction`**\n\n## 属性\n\n### 事件 %{#events}%\n\n• **events**: `DebuggerEvent`[]\n\n仅限开发环境。在回调中所有已完成的 mutation 的数组。\n\n---\n\n### storeId %{#storeid}%\n\n• **storeId**: `string`\n\n执行 mutation 的 store 的 `id`\n\n#### 继承于\n\n[\\_SubscriptionCallbackMutationBase](pinia._SubscriptionCallbackMutationBase.md).[storeId](pinia._SubscriptionCallbackMutationBase.md#storeid)\n\n---\n\n### 类型 %{#type}%\n\n• **type**: [`patchFunction`](../enums/pinia.MutationType.md#patchfunction)\n\nmutation 的类型\n\n#### 重写 %{#overrides}%\n\n[\\_SubscriptionCallbackMutationBase](pinia._SubscriptionCallbackMutationBase.md).[type](pinia._SubscriptionCallbackMutationBase.md#type)\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia.SubscriptionCallbackMutationPatchObject.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / SubscriptionCallbackMutationPatchObject\n\n# 接口：SubscriptionCallbackMutationPatchObject<S\\> %{#interface-subscriptioncallbackmutationpatchobject-s}%\n\n[pinia](../modules/pinia.md).SubscriptionCallbackMutationPatchObject\n\n当 `store.$patch()` 与一个对象一起被调用时，\n传递给订阅回调的上下文。\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 |\n| :--- |\n| `S`  |\n\n## 层次结构 %{#hierarchy}%\n\n- [`_SubscriptionCallbackMutationBase`](pinia._SubscriptionCallbackMutationBase.md)\n\n  ↳ **`SubscriptionCallbackMutationPatchObject`**\n\n## 属性\n\n### 事件 %{#events}%\n\n• **events**: `DebuggerEvent`[]\n\n仅限 DEV， patch 调用的数组。\n\n---\n\n### payload %{#payload}%\n\n• **payload**: [`_DeepPartial`](../modules/pinia.md#_deeppartial)<`S`\\>\n\n传递给 `store.$patch()` 的对象\n\n---\n\n### storeId %{#storeid}%\n\n• **storeId**: `string`\n\n执行 mutation 的 store 的 `id`\n\n#### 继承于\n\n[\\_SubscriptionCallbackMutationBase](pinia._SubscriptionCallbackMutationBase.md).[storeId](pinia._SubscriptionCallbackMutationBase.md#storeid)\n\n---\n\n### 类型 %{#type}%\n\n• **type**: [`patchObject`](../enums/pinia.MutationType.md#patchobject)\n\nmutation 的类型\n\n#### 重写 %{#overrides}%\n\n[\\_SubscriptionCallbackMutationBase](pinia._SubscriptionCallbackMutationBase.md).[type](pinia._SubscriptionCallbackMutationBase.md#type)\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia._StoreOnActionListenerContext.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / \\_StoreOnActionListenerContext\n\n# 接口：\\_StoreOnActionListenerContext<Store, ActionName, A\\> %{#interface-storeonactionlistenercontext-store-actionname-a}%\n\n[pinia](../modules/pinia.md).\\_StoreOnActionListenerContext\n\n[StoreOnActionListenerContext](../modules/pinia.md#storeonactionlistenercontext)的实际类型。\n存在的目的是重构。仅供内部使用。\n**仅**供内部使用\n\n## 类型参数 %{#type-parameters}%\n\n| 名称         | 类型             |\n| :----------- | :--------------- |\n| `Store`      | `Store`          |\n| `ActionName` | extends `string` |\n| `A`          | `A`              |\n\n## 属性\n\n### after %{#after}%\n\n• **after**: (`callback`: `A` extends `Record`<`ActionName`, [`_Method`](../modules/pinia.md#_method)\\> ? (`resolvedReturn`: [`_Awaited`](../modules/pinia.md#_awaited)<`ReturnType`<`A`[`ActionName`]\\>\\>) => `void` : () => `void`) => `void`\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (`callback`): `void`\n\naction 执行完的钩子。\n它接收 action 的返回值，如果是 Promise，它将被自动解包。\n\n##### 参数 %{#parameters}%\n\n| 名称       | 类型                                                                                                                                                                                                       |\n| :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `callback` | `A` extends `Record`<`ActionName`, [`_Method`](../modules/pinia.md#_method)\\> ? (`resolvedReturn`: [`_Awaited`](../modules/pinia.md#_awaited)<`ReturnType`<`A`[`ActionName`]\\>\\>) => `void` : () => `void` |\n\n##### 返回值\n\n`void`\n\n---\n\n### args %{#args}%\n\n• **args**: `A` extends `Record`<`ActionName`, [`_Method`](../modules/pinia.md#_method)\\> ? `Parameters`<`A`[`ActionName`]\\> : `unknown`[]\n\n传递给 action 的参数\n\n---\n\n### name %{#name}%\n\n• **name**: `ActionName`\n\naction 的名称\n\n---\n\n### onError %{#onerror}%\n\n• **onError**: (`callback`: (`error`: `unknown`) => `void`) => `void`\n\n#### 类型声明 %{#type-declaration_1}%\n\n▸ (`callback`): `void`\n\naction 的错误钩子。\n返回 `false` 以捕获错误并阻止其继续传播。\n\n##### 参数\n\n| 名称       | 类型                           |\n| :--------- | :----------------------------- |\n| `callback` | (`error`: `unknown`) => `void` |\n\n##### 返回值\n\n`void`\n\n---\n\n### store %{#store}%\n\n• **store**: `Store`\n\n正在调用 action 的 Store\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia._StoreWithState.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / \\_StoreWithState\n\n# 接口：\\_StoreWithState<Id, S, G, A\\> %{#interface-storewithstate-id-s-g-a}%\n\n[pinia](../modules/pinia.md).\\_StoreWithState\n\n具有 state 和部分功能的基础 store。不应直接使用。\n\n## 类型参数 %{#type-parameters}%\n\n| 名称 | 类型                                                 |\n| :--- | :--------------------------------------------------- |\n| `Id` | extends `string`                                     |\n| `S`  | extends [`StateTree`](../modules/pinia.md#statetree) |\n| `G`  | `G`                                                  |\n| `A`  | `A`                                                  |\n\n## 层次结构 %{#hierarchy}%\n\n- [`StoreProperties`](pinia.StoreProperties.md)<`Id`\\>\n\n  ↳ **`_StoreWithState`**\n\n## 属性\n\n### $id %{#id}%\n\n• **$id**: `Id`\n\nstore 的唯一标识符\n\n#### 继承于\n\n[StoreProperties](pinia.StoreProperties.md).[$id](pinia.StoreProperties.md#$id)\n\n---\n\n### $state %{#state}%\n\n• **$state**: `UnwrapRef`<`S`\\> & [`PiniaCustomStateProperties`](pinia.PiniaCustomStateProperties.md)<`S`\\>\n\nStore 的 State。给它赋值可替换整个 state。\n\n---\n\n### \\_customProperties %{#customproperties}%\n\n• **\\_customProperties**: `Set`<`string`\\>\n\n供 devtools 插件使用，用于检索插件添加的属性。\n生产版本会被移除。\n用户可以用它来添加应在 devtools 中显示的 store 属性键。\n\n#### 继承自\n\n[StoreProperties](pinia.StoreProperties.md).[\\_customProperties](pinia.StoreProperties.md#_customproperties)\n\n## 方法 %{#methods}%\n\n### $dispose %{#dispose}%\n\n▸ **$dispose**(): `void`\n\n停止 store 的相关作用域，并从 store 注册表中删除它。\n插件可以覆盖此方法来清理已添加的任何副作用函数。\n例如， devtools 插件停止显示来自 devtools 的已停止的 store。\n\n#### 返回值\n\n`void`\n\n---\n\n### $onAction %{#onaction}%\n\n▸ **$onAction**(`callback`, `detached?`): () => `void`\n\n设置一个回调，当一个 action 即将被调用时，就会被调用。\n回调接收一个对象，\n其包含被调用 action 的所有相关信息：\n\n- `store`: 被调用的 store\n- `name`: action 的名称\n- `args`: 传递给 action 的参数\n\n除此之外，它会接收两个函数，\n允许在 action 完成或失败时执行的回调。\n\n它还会返回一个用来删除回调的函数。\n请注意，当在组件内调用 `store.$onAction()` 时，除非 `detached` 被设置为 true，\n否则当组件被卸载时，它将被自动清理掉。\n\n#### 参数\n\n| 名称        | 类型                                                                                       | 描述                                                         |\n| :---------- | :----------------------------------------------------------------------------------------- | :----------------------------------------------------------- |\n| `callback`  | [`StoreOnActionListener`](../modules/pinia.md#storeonactionlistener)<`Id`, `S`, `G`, `A`\\> | callback called before every action                          |\n| `detached?` | `boolean`                                                                                  | detach the subscription from the context this is called from |\n\n#### 返回值\n\n`fn`\n\n删除侦听器的函数\n\n▸ (): `void`\n\n##### 返回值\n\n`void`\n\n**`Example`**\n\n```js\nstore.$onAction(({ after, onError }) => {\n  // 你可以在这里创建所有钩子之间的共享变量，\n  // 同时设置侦听器并清理它们。\n  after((resolvedValue) => {\n    // 可以用来清理副作用\n    // `resolvedValue` 是 action 返回的值，\n    // 如果是一个 Promise，它将是已解决的值\n  })\n  onError((error) => {\n    // 可以用于向上传递错误\n  })\n})\n```\n\n---\n\n### $patch %{#patch}%\n\n▸ **$patch**(`partialState`): `void`\n\n将一个 state 补丁应用于当前状态。允许传递嵌套值\n\n#### 参数\n\n| 名称           | 类型                                                                   | 描述                        |\n| :------------- | :--------------------------------------------------------------------- | :-------------------------- |\n| `partialState` | [`_DeepPartial`](../modules/pinia.md#_deeppartial)<`UnwrapRef`<`S`\\>\\> | patch to apply to the state |\n\n#### 返回值\n\n`void`\n\n▸ **$patch**<`F`\\>(`stateMutator`): `void`\n\n将多个变更分组到一个函数中。\n当 mutation 对象(如 Sets 或数组)或者应用对象补丁不方便时很有用，例如追加到数组中。\n传递给 `$patch()` 的函数**必须是同步的**。\n\n#### 类型参数 %{#type-parameters_1}%\n\n| 名称 | 类型                                          |\n| :--- | :-------------------------------------------- |\n| `F`  | extends (`state`: `UnwrapRef`<`S`\\>) => `any` |\n\n#### 参数\n\n| 名称           | 类型                                                         | 描述                                           |\n| :------------- | :----------------------------------------------------------- | :--------------------------------------------- |\n| `stateMutator` | `ReturnType`<`F`\\> extends `Promise`<`any`\\> ? `never` : `F` | function that mutates `state`, cannot be async |\n\n#### 返回值 {returns}\n\n`void`\n\n---\n\n### $reset %{#reset}%\n\n▸ **$reset**(): `void`\n\n通过建立一个新的状态对象，将 store 重设为初始状态。\nTODO: make this options only\n\n#### 返回值\n\n`void`\n\n---\n\n### $subscribe %{#subscribe}%\n\n▸ **$subscribe**(`callback`, `options?`): () => `void`\n\n设置一个回调，当状态发生变化时被调用。它会返回一个用来移除此回调的函数。\n请注意，当在组件内调用 `store.$subscribe()` 时，除非 `detached` 被设置为 true，\n否则当组件被卸载时，它将被自动清理掉。\n\n#### 参数\n\n| 名称       | 类型                                                                     | 描述                                                                                                                                                                                        |\n| :--------- | :----------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `callback` | [`SubscriptionCallback`](../modules/pinia.md#subscriptioncallback)<`S`\\> | callback passed to the watcher                                                                                                                                                              |\n| `options?` | { `detached?`: `boolean` } & `WatchOptions`<`boolean`\\>                  | `watch` options + `detached` to detach the subscription from the context (usually a component) this is called from. Note that the `flush` option does not affect calls to `store.$patch()`. |\n\n#### 返回值\n\n`fn`\n\n删除侦听器的函数\n\n▸ (): `void`\n\n设置一个回调，当状态发生变化时被调用。它还会返回一个用来移除回调的函数。\n请注意，当在组件内调用 `store.$subscribe()` 时，除非 `detached` 被设置为 true，\n否则当组件被卸载时，它将被自动清理掉。\n\n##### 返回值\n\n`void`\n\n移除侦听器的函数\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia._SubscriptionCallbackMutationBase.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [pinia](../modules/pinia.md) / \\_SubscriptionCallbackMutationBase\n\n# 接口：\\_SubscriptionCallbackMutationBase %{#interface-subscriptioncallbackmutationbase}%\n\n[pinia](../modules/pinia.md).\\_SubscriptionCallbackMutationBase\n\n传递给订阅回调的上下文的基本类型。内部类型。\n\n## 层次结构 %{#hierarchy}%\n\n- **`_SubscriptionCallbackMutationBase`**\n\n  ↳ [`SubscriptionCallbackMutationDirect`](pinia.SubscriptionCallbackMutationDirect.md)\n\n  ↳ [`SubscriptionCallbackMutationPatchFunction`](pinia.SubscriptionCallbackMutationPatchFunction.md)\n\n  ↳ [`SubscriptionCallbackMutationPatchObject`](pinia.SubscriptionCallbackMutationPatchObject.md)\n\n## 属性\n\n### storeId %{#storeid}%\n\n• **storeId**: `string`\n\n执行 mutation 的 store 的`id`。\n\n---\n\n### 类型 %{#type}%\n\n• **type**: [`MutationType`](../enums/pinia.MutationType.md)\n\nmutation 的类型\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia_nuxt.ModuleOptions.md",
    "content": "---\neditLink: false\n---\n\n[API 文档](../index.md) / [@pinia/nuxt](../modules/pinia_nuxt.md) / ModuleOptions\n\n# 接口：ModuleOptions\n\n[@pinia/nuxt](../modules/pinia_nuxt.md).ModuleOptions\n\n## 属性 %{#Properties}%\n\n### autoImports %{#Properties-autoImports}%\n\n• `Optional` **autoImports**: (`string` \\| [`string`, `string`])[]\n\n将被添加到 nuxt.config.js 文件的自动导入数组。\n\n**`Example`**\n\n```js\nautoImports: [\n // automatically import `defineStore`\n 'defineStore',\n // automatically import `defineStore` as `definePiniaStore`\n ['defineStore', 'definePiniaStore',\n]\n```\n\n---\n\n### disableVuex %{#Properties-disableVuex}%\n\n• `Optional` **disableVuex**: `boolean`\n\n默认情况下，Pinia 会禁用 Vuex，将此选项设置为 `false` 可启用 Vuex，然后便可同时使用 Pinia 和 Vuex（仅在 Nuxt 2 中支持）。\n\n**`Default`**\n\n`true`\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia_testing.TestingOptions.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [@pinia/testing](../modules/pinia_testing.md) / TestingOptions\n\n# 接口：TestingOptions %{#interface-testingoptions}%\n\n[@pinia/testing](../modules/pinia_testing.md).TestingOptions\n\n## 属性\n\n### createSpy %{#createspy}%\n\n• `Optional` **createSpy**: (`fn?`: (...`args`: `any`[]) => `any`) => (...`args`: `any`[]) => `any`\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (`fn?`): (...`args`: `any`[]) => `any`\n\n用于创建 action 和 `$patch()` 的 spy 的函数。\n在 jest 项目中默认为 `jest.fn()`，在 vitest 项目中默认为 `vi.fn()`。\n\n##### 参数\n\n| 名称  | 类型                          |\n| :---- | :---------------------------- |\n| `fn?` | (...`args`: `any`[]) => `any` |\n\n##### 返回值\n\n`fn`\n\n▸ (...`args`): `any`\n\n##### 参数\n\n| 名称      | 类型    |\n| :-------- | :------ |\n| `...args` | `any`[] |\n\n##### 返回值\n\n`any`\n\n---\n\n### fakeApp %{#fakeapp}%\n\n• `Optional` **fakeApp**: `boolean`\n\n创建一个空的 App，并通过创建的测试 pinia 调用 `app.use(pinia)`。\n这样可以让你在单元测试时使用插件，\n因为插件**必须等待 pinia 安装好后才会执行**。\n默认为 false。\n\n---\n\n### initialState %{#initialstate}%\n\n• `Optional` **initialState**: [`StateTree`](../modules/pinia.md#statetree)\n\n允许你定义每个 store 的部分初始 state。\n这个 state 会在 store 创建后被应用，这样可以让你只设置测试中需要的几个属性。\n\n---\n\n### 插件 %{#plugins}%\n\n• `Optional` **plugins**: [`PiniaPlugin`](pinia.PiniaPlugin.md)[]\n\n在测试插件之前必装的插件。\n可以向你的应用添加测试时使用的任意插件。\n\n---\n\n### stubActions %{#stubactions}%\n\n• `Optional` **stubActions**: `boolean`\n\n当设置为 false 时， actions 只会被监听，它们仍然会执行。\n当设置为 true 时，actions 将被替换为 spies，导致其代码不被执行。\n默认为 true。\n注意：当提供 `createSpy()` 时，它将**只**给 `fn` 参数 传递 `undefined`。\n你仍然需要在 `createSpy()` 中处理这个问题。\n\n---\n\n### stubPatch %{#stubpatch}%\n\n• `Optional` **stubPatch**: `boolean`\n\n当设置为 true 时，对 `$patch()` 的调用将不会改变状态。\n默认为 false。注意：当提供 `createSpy()` 时，它将**只**给 `fn` 参数 传递 `undefined`。\n你仍然需要在 `createSpy()` 中处理这个问题。\n"
  },
  {
    "path": "packages/docs/zh/api/interfaces/pinia_testing.TestingPinia.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / [@pinia/testing](../modules/pinia_testing.md) / TestingPinia\n\n# 接口：TestingPinia %{#interface-testingpinia}%\n\n[@pinia/testing](../modules/pinia_testing.md).TestingPinia\n\n专门为测试设计的 Pinia 实例。\n用测试中的特定属性扩展普通的 [Pinia](pinia.Pinia.md) 实例。\n\n## 层次结构 %{#hierarchy}%\n\n- [`Pinia`](pinia.Pinia.md)\n\n  ↳ **`TestingPinia`**\n\n## 属性\n\n### app %{#app}%\n\n• **app**: `App`<`any`\\>\n\nPinia 使用的应用\n\n---\n\n### 安装 %{#install}%\n\n• **install**: (`app`: `App`<`any`\\>) => `void`\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (`app`): `void`\n\n##### 参数\n\n| 名称  | 类型          |\n| :---- | :------------ |\n| `app` | `App`<`any`\\> |\n\n##### 返回值\n\n`void`\n\n#### 继承于\n\n[Pinia](pinia.Pinia.md).[install](pinia.Pinia.md#install)\n\n---\n\n### state %{#state}%\n\n• **state**: `Ref`<`Record`<`string`, [`StateTree`](../modules/pinia.md#statetree)\\>\\>\n\n根 state\n\n#### 继承于\n\n[Pinia](pinia.Pinia.md).[state](pinia.Pinia.md#state)\n\n## 方法 %{#methods}%\n\n### use %{#use}%\n\n▸ **use**(`plugin`): [`Pinia`](pinia.Pinia.md)\n\n增加了一个 store 插件来扩展每个 store\n\n#### 参数\n\n| 名称     | 类型                                  | 描述                |\n| :------- | :------------------------------------ | :------------------ |\n| `plugin` | [`PiniaPlugin`](pinia.PiniaPlugin.md) | store plugin to add |\n\n#### 返回值\n\n[`Pinia`](pinia.Pinia.md)\n\n#### 继承于\n\n[Pinia](pinia.Pinia.md).[use](pinia.Pinia.md#use)\n"
  },
  {
    "path": "packages/docs/zh/api/modules/pinia.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / pinia\n\n# 模块：pinia %{#module-pinia}%\n\n## 枚举 %{#enumerations}%\n\n- [MutationType](../enums/pinia.MutationType.md)\n\n## 接口 %{#interfaces}%\n\n- [DefineSetupStoreOptions](../interfaces/pinia.DefineSetupStoreOptions.md)\n- [DefineStoreOptions](../interfaces/pinia.DefineStoreOptions.md)\n- [DefineStoreOptionsBase](../interfaces/pinia.DefineStoreOptionsBase.md)\n- [DefineStoreOptionsInPlugin](../interfaces/pinia.DefineStoreOptionsInPlugin.md)\n- [MapStoresCustomization](../interfaces/pinia.MapStoresCustomization.md)\n- [Pinia](../interfaces/pinia.Pinia.md)\n- [PiniaCustomProperties](../interfaces/pinia.PiniaCustomProperties.md)\n- [PiniaCustomStateProperties](../interfaces/pinia.PiniaCustomStateProperties.md)\n- [PiniaPlugin](../interfaces/pinia.PiniaPlugin.md)\n- [PiniaPluginContext](../interfaces/pinia.PiniaPluginContext.md)\n- [StoreDefinition](../interfaces/pinia.StoreDefinition.md)\n- [StoreProperties](../interfaces/pinia.StoreProperties.md)\n- [SubscriptionCallbackMutationDirect](../interfaces/pinia.SubscriptionCallbackMutationDirect.md)\n- [SubscriptionCallbackMutationPatchFunction](../interfaces/pinia.SubscriptionCallbackMutationPatchFunction.md)\n- [SubscriptionCallbackMutationPatchObject](../interfaces/pinia.SubscriptionCallbackMutationPatchObject.md)\n- [\\_StoreOnActionListenerContext](../interfaces/pinia._StoreOnActionListenerContext.md)\n- [\\_StoreWithState](../interfaces/pinia._StoreWithState.md)\n- [\\_SubscriptionCallbackMutationBase](../interfaces/pinia._SubscriptionCallbackMutationBase.md)\n\n## 类型别名 %{#type-aliases}%\n\n### PiniaStorePlugin %{#piniastoreplugin}%\n\nƬ **PiniaStorePlugin**: [`PiniaPlugin`](../interfaces/pinia.PiniaPlugin.md)\n\n扩展每个 store 的插件。\n\n**`deprecated`**\n\n使用 PiniaPlugin 代替\n\n---\n\n### StateTree %{#statetree}%\n\nƬ **StateTree**: `Record`<`string` \\| `number` \\| `symbol`, `any`\\>\n\nStore 的通用 state\n\n---\n\n### Store %{#store}%\n\nƬ **Store**<`Id`, `S`, `G`, `A`\\>: [`_StoreWithState`](../interfaces/pinia._StoreWithState.md)<`Id`, `S`, `G`, `A`\\> & `UnwrapRef`<`S`\\> & [`_StoreWithGetters`](pinia.md#_storewithgetters)<`G`\\> & [`_ActionsTree`](pinia.md#_actionstree) extends `A` ? {} : `A` & [`PiniaCustomProperties`](../interfaces/pinia.PiniaCustomProperties.md)<`Id`, `S`, `G`, `A`\\> & [`PiniaCustomStateProperties`](../interfaces/pinia.PiniaCustomStateProperties.md)<`S`\\>\n\n创建 store 的 Store 类型。\n\n#### 类型参数\n\n| 名称 | 类型                                          |\n| :--- | :-------------------------------------------- |\n| `Id` | 扩展自 `string` = `string`                    |\n| `S`  | 扩展自 [`StateTree`](pinia.md#statetree) = {} |\n| `G`  | {}                                            |\n| `A`  | {}                                            |\n\n---\n\n### StoreActions %{#storeactions}%\n\nƬ **StoreActions**<`SS`\\>: `SS` extends [`Store`](pinia.md#store)<`string`, [`StateTree`](pinia.md#statetree), [`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>, infer A\\> ? `A` : [`_ExtractActionsFromSetupStore`](pinia.md#_extractactionsfromsetupstore)<`SS`\\>\n\n提取一个 store 类型的 action。\n对 Setup Store 或 Option Store 都有效。\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### StoreGeneric %{#storegeneric}%\n\nƬ **StoreGeneric**: [`Store`](pinia.md#store)<`string`, [`StateTree`](pinia.md#statetree), [`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>, [`_ActionsTree`](pinia.md#_actionstree)\\>\n\n泛型与 Store 的类型不安全版本。\n在访问字符串时不会失败，\n这使得编写不在意传递的 store 类型的通用函数更加容易。\n\n---\n\n### StoreGetters %{#storegetters}%\n\nƬ **StoreGetters**<`SS`\\>: `SS` extends [`Store`](pinia.md#store)<`string`, [`StateTree`](pinia.md#statetree), infer G, [`_ActionsTree`](pinia.md#_actionstree)\\> ? [`_StoreWithGetters`](pinia.md#_storewithgetters)<`G`\\> : [`_ExtractGettersFromSetupStore`](pinia.md#_extractgettersfromsetupstore)<`SS`\\>\n\n提取一个 store 类型的 getter。\n对 Setup Store 或 Option Store都有效。\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### StoreOnActionListener %{#storeonactionlistener}%\n\nƬ **StoreOnActionListener**<`Id`, `S`, `G`, `A`\\>: (`context`: [`StoreOnActionListenerContext`](pinia.md#storeonactionlistenercontext)<`Id`, `S`, `G`, {} extends `A` ? [`_ActionsTree`](pinia.md#_actionstree) : `A`\\>) => `void`\n\n#### 类型参数\n\n| 名称 | 类型                                     |\n| :--- | :--------------------------------------- |\n| `Id` | 扩展自 `string`                          |\n| `S`  | 扩展自 [`StateTree`](pinia.md#statetree) |\n| `G`  | `G`                                      |\n| `A`  | `A`                                      |\n\n#### 类型声明 %{#type-declaration}%\n\n▸ (`context`): `void`\n\n`store.$onAction()` 的参数\n\n##### 参数\n\n| 名称      | 类型                                                                                                                                                     |\n| :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `context` | [`StoreOnActionListenerContext`](pinia.md#storeonactionlistenercontext)<`Id`, `S`, `G`, {} extends `A` ? [`_ActionsTree`](pinia.md#_actionstree) : `A`\\> |\n\n##### 返回值\n\n`void`\n\n---\n\n### StoreOnActionListenerContext %{#storeonactionlistenercontext}%\n\nƬ **StoreOnActionListenerContext**<`Id`, `S`, `G`, `A`\\>: [`_ActionsTree`](pinia.md#_actionstree) extends `A` ? [`_StoreOnActionListenerContext`](../interfaces/pinia._StoreOnActionListenerContext.md)<[`StoreGeneric`](pinia.md#storegeneric), `string`, [`_ActionsTree`](pinia.md#_actionstree)\\> : { [Name in keyof A]: Name extends string ? \\_StoreOnActionListenerContext<Store<Id, S, G, A\\>, Name, A\\> : never }[keyof `A`]\n\n传递给 `store.$onAction(context => {})` 的回调的上下文对象。\nTODO：应该只有Id，Store 和 Action 来生成适当的对象。\n\n#### 类型参数\n\n| 名称 | 类型                                     |\n| :--- | :--------------------------------------- |\n| `Id` | 扩展自 `string`                          |\n| `S`  | 扩展自 [`StateTree`](pinia.md#statetree) |\n| `G`  | `G`                                      |\n| `A`  | `A`                                      |\n\n---\n\n### StoreState %{#storestate}%\n\nƬ **StoreState**<`SS`\\>: `SS` extends [`Store`](pinia.md#store)<`string`, infer S, [`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>, [`_ActionsTree`](pinia.md#_actionstree)\\> ? `UnwrapRef`<`S`\\> : [`_ExtractStateFromSetupStore`](pinia.md#_extractstatefromsetupstore)<`SS`\\>\n\n提取一个 store 类型的 state。\n对 Setup Store 或 Option Store 都有效。请注意，它自动解包 refs。\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### SubscriptionCallback %{#subscriptioncallback}%\n\nƬ **SubscriptionCallback**<`S`\\>: (`mutation`: [`SubscriptionCallbackMutation`](pinia.md#subscriptioncallbackmutation)<`S`\\>, `state`: `UnwrapRef`<`S`\\>) => `void`\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `S`  |\n\n#### 类型声明 %{#type-declaration_1}%\n\n▸ (`mutation`, `state`): `void`\n\n订阅的回调\n\n##### 参数\n\n| 名称       | 类型                                                                          |\n| :--------- | :---------------------------------------------------------------------------- |\n| `mutation` | [`SubscriptionCallbackMutation`](pinia.md#subscriptioncallbackmutation)<`S`\\> |\n| `state`    | `UnwrapRef`<`S`\\>                                                             |\n\n##### 返回值\n\n`void`\n\n---\n\n### SubscriptionCallbackMutation %{#subscriptioncallbackmutation}%\n\nƬ **SubscriptionCallbackMutation**<`S`\\>: [`SubscriptionCallbackMutationDirect`](../interfaces/pinia.SubscriptionCallbackMutationDirect.md) \\| [`SubscriptionCallbackMutationPatchObject`](../interfaces/pinia.SubscriptionCallbackMutationPatchObject.md)<`S`\\> \\| [`SubscriptionCallbackMutationPatchFunction`](../interfaces/pinia.SubscriptionCallbackMutationPatchFunction.md)\n\n传递给订阅回调的上下文对象。\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `S`  |\n\n---\n\n### \\_ActionsTree %{#actionstree}%\n\nƬ **\\_ActionsTree**: `Record`<`string`, [`_Method`](pinia.md#_method)\\>\n\n行动的对象的类型。仅供内部使用。\n**仅**供内部使用\n\n---\n\n### \\_Awaited %{#awaited}%\n\nƬ **\\_Awaited**<`T`\\>: `T` extends `null` \\| `undefined` ? `T` : `T` extends `object` & { `then`: (`onfulfilled`: `F`) => `any` } ? `F` extends (`value`: infer V, ...`args`: `any`) => `any` ? [`_Awaited`](pinia.md#_awaited)<`V`\\> : `never` : `T`\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `T`  |\n\n---\n\n### \\_DeepPartial %{#deeppartial}%\n\nƬ **\\_DeepPartial**<`T`\\>: { [K in keyof T]?: \\_DeepPartial<T[K]\\> }\n\n递归的 `Partial<T>`。 [['$patch']](pinia.md#store)会用到。\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `T`  |\n\n---\n\n### \\_ExtractActionsFromSetupStore %{#extractactionsfromsetupstore}%\n\nƬ **\\_ExtractActionsFromSetupStore**<`SS`\\>: `SS` extends `undefined` \\| `void` ? {} : [`_ExtractActionsFromSetupStore_Keys`](pinia.md#_extractactionsfromsetupstore_keys)<`SS`\\> extends keyof `SS` ? `Pick`<`SS`, [`_ExtractActionsFromSetupStore_Keys`](pinia.md#_extractactionsfromsetupstore_keys)<`SS`\\>\\> : `never`\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### \\_ExtractActionsFromSetupStore_Keys %{#extractactionsfromsetupstore-keys}%\n\nƬ **\\_ExtractActionsFromSetupStore_Keys**<`SS`\\>: keyof { [K in keyof SS as SS[K] extends \\_Method ? K : never]: any }\n\n能够通过 IDE 进行重构的类型。\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### \\_ExtractGettersFromSetupStore %{#extractgettersfromsetupstore}%\n\nƬ **\\_ExtractGettersFromSetupStore**<`SS`\\>: `SS` extends `undefined` \\| `void` ? {} : [`_ExtractGettersFromSetupStore_Keys`](pinia.md#_extractgettersfromsetupstore_keys)<`SS`\\> extends keyof `SS` ? `Pick`<`SS`, [`_ExtractGettersFromSetupStore_Keys`](pinia.md#_extractgettersfromsetupstore_keys)<`SS`\\>\\> : `never`\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### \\_ExtractGettersFromSetupStore_Keys %{#extractgettersfromsetupstore-keys}%\n\nƬ **\\_ExtractGettersFromSetupStore_Keys**<`SS`\\>: keyof { [K in keyof SS as SS[K] extends ComputedRef ? K : never]: any }\n\n能够通过 IDE 进行重构的类型。\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### \\_ExtractStateFromSetupStore %{#extractstatefromsetupstore}%\n\nƬ **\\_ExtractStateFromSetupStore**<`SS`\\>: `SS` extends `undefined` \\| `void` ? {} : [`_ExtractStateFromSetupStore_Keys`](pinia.md#_extractstatefromsetupstore_keys)<`SS`\\> extends keyof `SS` ? [`_UnwrapAll`](pinia.md#_unwrapall)<`Pick`<`SS`, [`_ExtractStateFromSetupStore_Keys`](pinia.md#_extractstatefromsetupstore_keys)<`SS`\\>\\>\\> : `never`\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### \\_ExtractStateFromSetupStore_Keys %{#extractstatefromsetupstore-keys}%\n\nƬ **\\_ExtractStateFromSetupStore_Keys**<`SS`\\>: keyof { [K in keyof SS as SS[K] extends \\_Method \\| ComputedRef ? never : K]: any }\n\n能够通过 IDE 进行重构的类型。\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n---\n\n### \\_GettersTree %{#getterstree}%\n\nƬ **\\_GettersTree**<`S`\\>: `Record`<`string`, (`state`: `UnwrapRef`<`S`\\> & `UnwrapRef`<[`PiniaCustomStateProperties`](../interfaces/pinia.PiniaCustomStateProperties.md)<`S`\\>\\>) => `any` \\| () => `any`\\>\n\n推断参数的 Getter 对象的类型。仅供内部使用。\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 | 类型                                      |\n| :--- | :---------------------------------------- |\n| `S`  | extends [`StateTree`](pinia.md#statetree) |\n\n---\n\n### \\_MapActionsObjectReturn %{#mapactionsobjectreturn}%\n\nƬ **\\_MapActionsObjectReturn**<`A`, `T`\\>: { [key in keyof T]: A[T[key]] }\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 | 类型                                   |\n| :--- | :------------------------------------- |\n| `A`  | `A`                                    |\n| `T`  | extends `Record`<`string`, keyof `A`\\> |\n\n---\n\n### \\_MapActionsReturn %{#mapactionsreturn}%\n\nƬ **\\_MapActionsReturn**<`A`\\>: { [key in keyof A]: A[key] }\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `A`  |\n\n---\n\n### \\_MapStateObjectReturn %{#mapstateobjectreturn}%\n\nƬ **\\_MapStateObjectReturn**<`Id`, `S`, `G`, `A`, `T`\\>: { [key in keyof T]: Function }\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 | 类型                                                                                                                            |\n| :--- | :------------------------------------------------------------------------------------------------------------------------------ |\n| `Id` | extends `string`                                                                                                                |\n| `S`  | extends [`StateTree`](pinia.md#statetree)                                                                                       |\n| `G`  | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\>                                                                           |\n| `A`  | `A`                                                                                                                             |\n| `T`  | extends `Record`<`string`, keyof `S` \\| keyof `G` \\| (`store`: [`Store`](pinia.md#store)<`Id`, `S`, `G`, `A`\\>) => `any`\\> = {} |\n\n---\n\n### \\_MapStateReturn %{#mapstatereturn}%\n\nƬ **\\_MapStateReturn**<`S`, `G`, `Keys`\\>: { [key in Keys]: Function }\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称   | 类型                                                    |\n| :----- | :------------------------------------------------------ |\n| `S`    | extends [`StateTree`](pinia.md#statetree)               |\n| `G`    | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\>   |\n| `Keys` | extends keyof `S` \\| keyof `G` = keyof `S` \\| keyof `G` |\n\n---\n\n### \\_MapWritableStateObjectReturn %{#mapwritablestateobjectreturn}%\n\nƬ **\\_MapWritableStateObjectReturn**<`S`, `T`\\>: { [key in keyof T]: Object }\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 | 类型                                      |\n| :--- | :---------------------------------------- |\n| `S`  | extends [`StateTree`](pinia.md#statetree) |\n| `T`  | extends `Record`<`string`, keyof `S`\\>    |\n\n---\n\n### \\_MapWritableStateReturn %{#mapwritablestatereturn}%\n\nƬ **\\_MapWritableStateReturn**<`S`\\>: { [key in keyof S]: Object }\n\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 | Type                                      |\n| :--- | :---------------------------------------- |\n| `S`  | extends [`StateTree`](pinia.md#statetree) |\n\n---\n\n### \\_Method %{#method}%\n\nƬ **\\_Method**: (...`args`: `any`[]) => `any`\n\n#### 类型声明 %{#type-declaration_2}%\n\n▸ (...`args`): `any`\n\n可以推断参数和返回值类型的函数通用类型。\n\n**仅**供内部使用\n\n##### 参数\n\n| 名称      | 类型    |\n| :-------- | :------ |\n| `...args` | `any`[] |\n\n##### 返回值\n\n`any`\n\n---\n\n### \\_Spread %{#spread}%\n\nƬ **\\_Spread**<`A`\\>: `A` extends [infer L, ...(infer R)] ? [`_StoreObject`](pinia.md#_storeobject)<`L`\\> & [`_Spread`](pinia.md#_spread)<`R`\\> : `unknown`\n\n**仅**供内部使用.\n\n#### 类型参数\n\n| 名称 | 类型                     |\n| :--- | :----------------------- |\n| `A`  | extends readonly `any`[] |\n\n---\n\n### \\_StoreObject %{#storeobject}%\n\nƬ **\\_StoreObject**<`S`\\>: `S` extends [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<infer Ids, infer State, infer Getters, infer Actions\\> ? { [Id in \\`${Ids}${MapStoresCustomization extends Record<\"suffix\", string\\> ? MapStoresCustomization[\"suffix\"] : \"Store\"}\\`]: Function } : {}\n\n**仅**供内部使用.\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `S`  |\n\n---\n\n### \\_StoreWithActions %{#storewithactions}%\n\nƬ **\\_StoreWithActions**<`A`\\>: { [k in keyof A]: A[k] extends Function ? Function : never }\n\n为 action 增强的 Store。仅供内部使用。\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `A`  |\n\n---\n\n### \\_StoreWithGetters %{#storewithgetters}%\n\nƬ **\\_StoreWithGetters**<`G`\\>: { readonly [k in keyof G]: G[k] extends Function ? R : UnwrapRef<G[k]\\> }\n\nStore augmented with getters. For internal usage only.\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `G`  |\n\n---\n\n### \\_UnwrapAll %{#unwrapall}%\n\nƬ **\\_UnwrapAll**<`SS`\\>: { [K in keyof SS]: UnwrapRef<SS[K]\\> }\n\n能够通过 IDE 进行重构的类型。\n**仅**供内部使用\n\n#### 类型参数\n\n| 名称 |\n| :--- |\n| `SS` |\n\n## 变量 %{#variables}%\n\n### PiniaVuePlugin %{#piniavueplugin}%\n\n• **PiniaVuePlugin**: `Plugin`\n\nVue 2 插件，必须安装该插件才能使 pinia 工作。\n注意，**如果你使用的是 Nuxt，那你不需要这个插件**。请使用 `buildModule` 代替：\n<https://pinia.vuejs.org/zh/ssr/nuxt.html>\n\n**`Example`**\n\n```js\nimport Vue from 'vue'\nimport { PiniaVuePlugin, createPinia } from 'pinia'\n\nVue.use(PiniaVuePlugin)\nconst pinia = createPinia()\n\nnew Vue({\n  el: '#app',\n  // ...\n  pinia,\n})\n```\n\n**`param`**\n\n从 'vue' 导入的 `Vue`。\n\n## 函数 %{#functions}%\n\n### acceptHMRUpdate %{#accepthmrupdate}%\n\n▸ **acceptHMRUpdate**(`initialUseStore`, `hot`): (`newModule`: `any`) => `any`\n\n创建一个 _accept_ 函数，在 Vite 应用中传递给 `import.meta.hot`。\n\n**`Example`**\n\n```js\nconst useUser = defineStore(...)\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot))\n}\n```\n\n#### 参数\n\n| 名称              | 类型                                                                                                                                                                                                                            | 描述                                    |\n| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------- |\n| `initialUseStore` | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`string`, [`StateTree`](pinia.md#statetree), [`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>, [`_ActionsTree`](pinia.md#_actionstree)\\> | return of the defineStore to hot update |\n| `hot`             | `any`                                                                                                                                                                                                                           | `import.meta.hot`                       |\n\n#### 返回值\n\n`fn`\n\n▸ (`newModule`): `any`\n\n##### 参数\n\n| 名称        | 类型  |\n| :---------- | :---- |\n| `newModule` | `any` |\n\n##### 返回值\n\n`any`\n\n---\n\n### createPinia %{#createpinia}%\n\n▸ **createPinia**(): [`Pinia`](../interfaces/pinia.Pinia.md)\n\n创建一个 Pinia 实例，供应用使用。\n\n#### 返回值\n\n[`Pinia`](../interfaces/pinia.Pinia.md)\n\n---\n\n### defineStore %{#definestore}%\n\n▸ **defineStore**<`Id`, `S`, `G`, `A`\\>(`id`, `options`): [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\>\n\n创建一个 `useStore` 函数，检索 store 实例\n\n#### 类型参数\n\n| 名称 | 类型                                          |\n| :--- | :-------------------------------------------- |\n| `Id` | 扩展自 `string`                               |\n| `S`  | 扩展自 [`StateTree`](pinia.md#statetree) = {} |\n| `G`  | 扩展自 `_GettersTree`<`S`\\> = {}              |\n| `A`  | {}                                            |\n\n#### 参数\n\n| 名称      | 类型                                                                                                     | 描述                             |\n| :-------- | :------------------------------------------------------------------------------------------------------- | :------------------------------- |\n| `id`      | `Id`                                                                                                     | id of the store (must be unique) |\n| `options` | `Omit`<[`DefineStoreOptions`](../interfaces/pinia.DefineStoreOptions.md)<`Id`, `S`, `G`, `A`\\>, `\"id\"`\\> | options to define the store      |\n\n#### 返回值\n\n[`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\>\n\n▸ **defineStore**<`Id`, `S`, `G`, `A`\\>(`options`): [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\>\n\n创建一个 `useStore` 函数，检索 store 实例\n\n#### 类型参数\n\n| 名称 | 类型                                                       |\n| :--- | :--------------------------------------------------------- |\n| `Id` | extends `string`                                           |\n| `S`  | extends [`StateTree`](pinia.md#statetree) = {}             |\n| `G`  | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\> = {} |\n| `A`  | {}                                                         |\n\n#### 参数\n\n| 名称      | 类型                                                                                    | 描述                        |\n| :-------- | :-------------------------------------------------------------------------------------- | :-------------------------- |\n| `options` | [`DefineStoreOptions`](../interfaces/pinia.DefineStoreOptions.md)<`Id`, `S`, `G`, `A`\\> | options to define the store |\n\n#### 返回值\n\n[`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\>\n\n▸ **defineStore**<`Id`, `SS`\\>(`id`, `storeSetup`, `options?`): [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, [`_ExtractStateFromSetupStore`](pinia.md#_extractstatefromsetupstore)<`SS`\\>, [`_ExtractGettersFromSetupStore`](pinia.md#_extractgettersfromsetupstore)<`SS`\\>, [`_ExtractActionsFromSetupStore`](pinia.md#_extractactionsfromsetupstore)<`SS`\\>\\>\n\n创建一个 `useStore` 函数，检索 store 实例\n\n#### 类型参数\n\n| 名称 | 类型            |\n| :--- | :-------------- |\n| `Id` | 扩展自 `string` |\n| `SS` | `SS`            |\n\n#### 参数\n\n| 名称         | 类型                                                                                                                                                                                                                                                                                                                                 | 描述                             |\n| :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------- |\n| `id`         | `Id`                                                                                                                                                                                                                                                                                                                                 | id of the store (must be unique) |\n| `storeSetup` | () => `SS`                                                                                                                                                                                                                                                                                                                           | function that defines the store  |\n| `options?`   | [`DefineSetupStoreOptions`](../interfaces/pinia.DefineSetupStoreOptions.md)<`Id`, [`_ExtractStateFromSetupStore`](pinia.md#_extractstatefromsetupstore)<`SS`\\>, [`_ExtractGettersFromSetupStore`](pinia.md#_extractgettersfromsetupstore)<`SS`\\>, [`_ExtractActionsFromSetupStore`](pinia.md#_extractactionsfromsetupstore)<`SS`\\>\\> | extra options                    |\n\n#### 返回值\n\n[`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, [`_ExtractStateFromSetupStore`](pinia.md#_extractstatefromsetupstore)<`SS`\\>, [`_ExtractGettersFromSetupStore`](pinia.md#_extractgettersfromsetupstore)<`SS`\\>, [`_ExtractActionsFromSetupStore`](pinia.md#_extractactionsfromsetupstore)<`SS`\\>\\>\n\n---\n\n### getActivePinia %{#getactivepinia}%\n\n▸ **getActivePinia**(): `undefined` \\| [`Pinia`](../interfaces/pinia.Pinia.md)\n\n如果有的话，获取当前激活的 pinia\n\n#### 返回值\n\n`undefined` \\| [`Pinia`](../interfaces/pinia.Pinia.md)\n\n---\n\n### mapActions %{#mapactions}%\n\n▸ **mapActions**<`Id`, `S`, `G`, `A`, `KeyMapper`\\>(`useStore`, `keyMapper`): [`_MapActionsObjectReturn`](pinia.md#_mapactionsobjectreturn)<`A`, `KeyMapper`\\>\n\n通过生成一个传递到组件的 `methods` 字段的对象，\n允许直接使用 store 的 action，而不需要使用组合式 API(`setup()`)。\n该对象的值是 action，\n而键是产生的方法名称。\n\n**`Example`**\n\n```js\nexport default {\n  methods: {\n    // 其他方法属性\n    // useCounterStore 有两个 action，分别是 `increment` 与 `setCount`。\n    ...mapActions(useCounterStore, { moar: 'increment', setIt: 'setCount' }),\n  },\n\n  created() {\n    this.moar()\n    this.setIt(2)\n  },\n}\n```\n\n#### 类型参数\n\n| 名称        | 类型                                     |\n| :---------- | :--------------------------------------- |\n| `Id`        | 扩展自 `string`                          |\n| `S`         | 扩展自 [`StateTree`](pinia.md#statetree) |\n| `G`         | 扩展自 `_GettersTree`<`S`\\>              |\n| `A`         | `A`                                      |\n| `KeyMapper` | 扩展自 `Record`<`string`, keyof `A`\\>    |\n\n#### 参数\n\n| 名称        | 类型                                                                              | 描述                                       |\n| :---------- | :-------------------------------------------------------------------------------- | :----------------------------------------- |\n| `useStore`  | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from                          |\n| `keyMapper` | `KeyMapper`                                                                       | object to define new names for the actions |\n\n#### 返回值\n\n[`_MapActionsObjectReturn`](pinia.md#_mapactionsobjectreturn)<`A`, `KeyMapper`\\>\n\n▸ **mapActions**<`Id`, `S`, `G`, `A`\\>(`useStore`, `keys`): [`_MapActionsReturn`](pinia.md#_mapactionsreturn)<`A`\\>\n\n允许直接使用 store 里的 action，\n而不是必须使用组合式 API(`setup()`)，\n通过生成一个对象，传递到组件的 `methods` 字段。\n\n**`Example`**\n\n```js\nexport default {\n  methods: {\n    // 其他方法属性\n    ...mapActions(useCounterStore, ['increment', 'setCount']),\n  },\n\n  created() {\n    this.increment()\n    this.setCount(2) // 像往常一样传递参数\n  },\n}\n```\n\n#### 类型参数\n\n| 名称 | 类型                                                 |\n| :--- | :--------------------------------------------------- |\n| `Id` | 扩展自 `string`                                      |\n| `S`  | 扩展自 [`StateTree`](pinia.md#statetree)             |\n| `G`  | 扩展自 [`_GettersTree`](pinia.md#_getterstree)<`S`\\> |\n| `A`  | `A`                                                  |\n\n#### 参数\n\n| 名称       | 类型                                                                              | 描述                         |\n| :--------- | :-------------------------------------------------------------------------------- | :--------------------------- |\n| `useStore` | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from            |\n| `keys`     | keyof `A`[]                                                                       | array of action names to map |\n\n#### 返回值\n\n[`_MapActionsReturn`](pinia.md#_mapactionsreturn)<`A`\\>\n\n---\n\n### mapGetters %{#mapgetters}%\n\n▸ **mapGetters**<`Id`, `S`, `G`, `A`, `KeyMapper`\\>(`useStore`, `keyMapper`): [`_MapStateObjectReturn`](pinia.md#_mapstateobjectreturn)<`Id`, `S`, `G`, `A`, `KeyMapper`\\>\n\n通过生成一个对象传递到组件的 `computed` 字段中。\n允许使用来自一个 store 的 state 和 getter，而不必使用组合式 API(`setup()`)。\n该对象的值是 state 属性/ getter\n而键则是产生的计算属性的名称。\n另外，你还可以传递一个自定义函数，\n该函数将接收 state 的作为其第一个参数。\n注意，虽然它可以通过 `this` 访问组件的实例，但它没有标注类型。\n\n**`Example`**\n\n```js\nexport default {\n  computed: {\n    // 其他计算属性\n    // useCounterStore 有一个名为 `count` 的 state 属性以及一个名为 `double` 的 getter\n    ...mapState(useCounterStore, {\n      n: 'count',\n      triple: (store) => store.n * 3,\n      // 注意如果你想要使用 `this`，那你不能使用箭头函数\n      custom(store) {\n        return this.someComponentValue + store.n\n      },\n      doubleN: 'double',\n    }),\n  },\n\n  created() {\n    this.n // 2\n    this.doubleN // 4\n  },\n}\n```\n\n#### 类型参数\n\n| 名称        | 类型                                                                                                                       |\n| :---------- | :------------------------------------------------------------------------------------------------------------------------- |\n| `Id`        | extends `string`                                                                                                           |\n| `S`         | extends [`StateTree`](pinia.md#statetree)                                                                                  |\n| `G`         | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\>                                                                      |\n| `A`         | `A`                                                                                                                        |\n| `KeyMapper` | extends `Record`<`string`, keyof `S` \\| keyof `G` \\| (`store`: [`Store`](pinia.md#store)<`Id`, `S`, `G`, `A`\\>) => `any`\\> |\n\n#### 参数\n\n| 名称        | 类型                                                                              | 描述                                  |\n| :---------- | :-------------------------------------------------------------------------------- | :------------------------------------ |\n| `useStore`  | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from                     |\n| `keyMapper` | `KeyMapper`                                                                       | object of state properties or getters |\n\n#### 返回值\n\n[`_MapStateObjectReturn`](pinia.md#_mapstateobjectreturn)<`Id`, `S`, `G`, `A`, `KeyMapper`\\>\n\n▸ **mapGetters**<`Id`, `S`, `G`, `A`, `Keys`\\>(`useStore`, `keys`): [`_MapStateReturn`](pinia.md#_mapstatereturn)<`S`, `G`, `Keys`\\>\n\n通过生成一个对象传递到组件的 `computed` 字段中，\n以允许使用来自一个 store 的 state 和 getter，\n而不必使用组合式 API(`setup()`)，\n\n**`Example`**\n\n```js\nexport default {\n  computed: {\n    // 其他计算属性\n    ...mapState(useCounterStore, ['count', 'double']),\n  },\n\n  created() {\n    this.count // 2\n    this.double // 4\n  },\n}\n```\n\n#### 类型参数\n\n| 名称   | 类型                                                  |\n| :----- | :---------------------------------------------------- |\n| `Id`   | extends `string`                                      |\n| `S`    | extends [`StateTree`](pinia.md#statetree)             |\n| `G`    | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\> |\n| `A`    | `A`                                                   |\n| `Keys` | extends `string` \\| `number` \\| `symbol`              |\n\n#### 参数\n\n| 名称       | 类型                                                                              | 描述                                 |\n| :--------- | :-------------------------------------------------------------------------------- | :----------------------------------- |\n| `useStore` | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from                    |\n| `keys`     | readonly `Keys`[]                                                                 | array of state properties or getters |\n\n#### 返回值\n\n[`_MapStateReturn`](pinia.md#_mapstatereturn)<`S`, `G`, `Keys`\\>\n\n---\n\n### mapState %{#mapstate}%\n\n▸ **mapState**<`Id`, `S`, `G`, `A`, `KeyMapper`\\>(`useStore`, `keyMapper`): [`_MapStateObjectReturn`](pinia.md#_mapstateobjectreturn)<`Id`, `S`, `G`, `A`, `KeyMapper`\\>\n\n通过生成一个对象，并传递至组件的 `computed` 字段，\n以允许在不使用组合式 API(`setup()`)的情况下使用一个 store 的 state 和 getter。\n该对象的值是 state 属性/getter，\n而键是生成的计算属性名称。\n你也可以选择传递一个自定义函数，该函数将接收 store 作为其第一个参数。\n注意，虽然它可以通过 `this` 访问组件实例，但它没有标注类型。\n\n**`Example`**\n\n```js\nexport default {\n  computed: {\n    // 其他计算属性\n    // useCounterStore 拥有一个名为 `count` 的 state 属性和一个名为 `double` 的 getter\n    ...mapState(useCounterStore, {\n      n: 'count',\n      triple: (store) => store.n * 3,\n      // 如果想使用 `this`，就不能使用箭头函数\n      custom(store) {\n        return this.someComponentValue + store.n\n      },\n      doubleN: 'double',\n    }),\n  },\n\n  created() {\n    this.n // 2\n    this.doubleN // 4\n  },\n}\n```\n\n#### 类型参数\n\n| 名称        | 类型                                                                                                                       |\n| :---------- | :------------------------------------------------------------------------------------------------------------------------- |\n| `Id`        | extends `string`                                                                                                           |\n| `S`         | extends [`StateTree`](pinia.md#statetree)                                                                                  |\n| `G`         | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\>                                                                      |\n| `A`         | `A`                                                                                                                        |\n| `KeyMapper` | extends `Record`<`string`, keyof `S` \\| keyof `G` \\| (`store`: [`Store`](pinia.md#store)<`Id`, `S`, `G`, `A`\\>) => `any`\\> |\n\n#### 参数\n\n| 名称        | 类型                                                                              | 描述                                  |\n| :---------- | :-------------------------------------------------------------------------------- | :------------------------------------ |\n| `useStore`  | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from                     |\n| `keyMapper` | `KeyMapper`                                                                       | object of state properties or getters |\n\n#### 返回值\n\n[`_MapStateObjectReturn`](pinia.md#_mapstateobjectreturn)<`Id`, `S`, `G`, `A`, `KeyMapper`\\>\n\n▸ **mapState**<`Id`, `S`, `G`, `A`, `Keys`\\>(`useStore`, `keys`): [`_MapStateReturn`](pinia.md#_mapstatereturn)<`S`, `G`, `Keys`\\>\n\n通过生成一个对象，并传递到组件的 `computed` 字段，\n允许在不使用组合式 API(`setup()`)的情况下\n使用一个 store 的 state 和 getter，\n\n**`Example`**\n\n```js\nexport default {\n  computed: {\n    // 其他计算属性\n    ...mapState(useCounterStore, ['count', 'double']),\n  },\n\n  created() {\n    this.count // 2\n    this.double // 4\n  },\n}\n```\n\n#### 类型参数\n\n| 名称   | 类型                                                  |\n| :----- | :---------------------------------------------------- |\n| `Id`   | extends `string`                                      |\n| `S`    | extends [`StateTree`](pinia.md#statetree)             |\n| `G`    | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\> |\n| `A`    | `A`                                                   |\n| `Keys` | extends `string` \\| `number` \\| `symbol`              |\n\n#### 参数\n\n| 名称       | 类型                                                                              | 描述                                 |\n| :--------- | :-------------------------------------------------------------------------------- | :----------------------------------- |\n| `useStore` | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from                    |\n| `keys`     | readonly `Keys`[]                                                                 | array of state properties or getters |\n\n#### 返回值\n\n[`_MapStateReturn`](pinia.md#_mapstatereturn)<`S`, `G`, `Keys`\\>\n\n---\n\n### mapStores %{#mapstores}%\n\n▸ **mapStores**<`Stores`\\>(...`stores`): [`_Spread`](pinia.md#_spread)<`Stores`\\>\n\n通过生成一个对象，传递到组件的 `computed` 字段\n以允许在不使用组合式 API(`setup()`)的情况下使用 store。\n它接受一个 store 定义的列表参数。\n\n**`Example`**\n\n```js\nexport default {\n  computed: {\n    // 其他计算属性\n    ...mapStores(useUserStore, useCartStore),\n  },\n\n  created() {\n    this.userStore // id 为 \"user\" 的 store\n    this.cartStore // id 为 \"cart\" 的 store\n  },\n}\n```\n\n#### 类型参数\n\n| 名称     | 类型         |\n| :------- | :----------- |\n| `Stores` | 扩展 `any`[] |\n\n#### 参数\n\n| 名称        | 类型          | 描述                               |\n| :---------- | :------------ | :--------------------------------- |\n| `...stores` | [...Stores[]] | list of stores to map to an object |\n\n#### 返回值\n\n[`_Spread`](pinia.md#_spread)<`Stores`\\>\n\n---\n\n### mapWritableState %{#mapwritablestate}%\n\n▸ **mapWritableState**<`Id`, `S`, `G`, `A`, `KeyMapper`\\>(`useStore`, `keyMapper`): [`_MapWritableStateObjectReturn`](pinia.md#_mapwritablestateobjectreturn)<`S`, `KeyMapper`\\>\n\n除了创建的计算属性的 setter，其他与 `mapState()` 相同，\n所以 state 可以被修改。\n与 `mapState()` 不同的是，只有 `state` 属性可以被添加。\n\n#### 类型参数\n\n| 名称        | 类型                                                  |\n| :---------- | :---------------------------------------------------- |\n| `Id`        | extends `string`                                      |\n| `S`         | extends [`StateTree`](pinia.md#statetree)             |\n| `G`         | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\> |\n| `A`         | `A`                                                   |\n| `KeyMapper` | extends `Record`<`string`, keyof `S`\\>                |\n\n#### 参数\n\n| 名称        | 类型                                                                              | 描述                       |\n| :---------- | :-------------------------------------------------------------------------------- | :------------------------- |\n| `useStore`  | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from          |\n| `keyMapper` | `KeyMapper`                                                                       | object of state properties |\n\n#### 返回值\n\n[`_MapWritableStateObjectReturn`](pinia.md#_mapwritablestateobjectreturn)<`S`, `KeyMapper`\\>\n\n▸ **mapWritableState**<`Id`, `S`, `G`, `A`\\>(`useStore`, `keys`): [`_MapWritableStateReturn`](pinia.md#_mapwritablestatereturn)<`S`\\>\n\n通过生成一个对象并传递到组件的 `computed` 字段\n以允许在不使用组合式 API(`setup()`)的情况下\n使用来自一个 store 的 state 和 getter，。\n\n#### 类型参数\n\n| 名称 | 类型                                                  |\n| :--- | :---------------------------------------------------- |\n| `Id` | extends `string`                                      |\n| `S`  | extends [`StateTree`](pinia.md#statetree)             |\n| `G`  | extends [`_GettersTree`](pinia.md#_getterstree)<`S`\\> |\n| `A`  | `A`                                                   |\n\n#### 参数\n\n| 名称       | 类型                                                                              | 描述                      |\n| :--------- | :-------------------------------------------------------------------------------- | :------------------------ |\n| `useStore` | [`StoreDefinition`](../interfaces/pinia.StoreDefinition.md)<`Id`, `S`, `G`, `A`\\> | store to map from         |\n| `keys`     | keyof `S`[]                                                                       | array of state properties |\n\n#### 返回值\n\n[`_MapWritableStateReturn`](pinia.md#_mapwritablestatereturn)<`S`\\>\n\n---\n\n### setActivePinia %{#setactivepinia}%\n\n▸ **setActivePinia**(`pinia`): `undefined` \\| [`Pinia`](../interfaces/pinia.Pinia.md)\n\n设置或取消设置激活的 pinia。\n在 SSR 和内部调用 action 和 getter 时使用。\n\n#### 参数\n\n| 名称    | 类型                                                   | 描述           |\n| :------ | :----------------------------------------------------- | :------------- |\n| `pinia` | `undefined` \\| [`Pinia`](../interfaces/pinia.Pinia.md) | Pinia instance |\n\n#### 返回值\n\n`undefined` \\| [`Pinia`](../interfaces/pinia.Pinia.md)\n\n---\n\n### setMapStoreSuffix %{#setmapstoresuffix}%\n\n▸ **setMapStoreSuffix**(`suffix`): `void`\n\n改变由 `mapStores()` 添加的后缀。可以设置为空字符串。\n默认为`\"Store\"`。如果你需要使用 TypeScript，\n请确保扩展 MapStoresCustomization 接口。\n\n#### 参数 %{#parameters}%\n\n| 名称     | 类型     | 描述       |\n| :------- | :------- | :--------- |\n| `suffix` | `string` | new suffix |\n\n#### 返回值\n\n`void`\n\n---\n\n### skipHydrate %{#skiphydrate}%\n\n▸ **skipHydrate**<`T`\\>(`obj`): `T`\n\n告诉 Pinia 跳过给定对象的激活过程。当你在 store 中返回一个有状态的对象，但它并不是真正的状态时，(仅)在 setup store 中这是很有用的。\n例如，在一个 setup store 中返回一个路由器实例。\n\n#### 类型参数\n\n| 名称 | 类型  |\n| :--- | :---- |\n| `T`  | `any` |\n\n#### 参数\n\n| 名称  | 类型 | 描述          |\n| :---- | :--- | :------------ |\n| `obj` | `T`  | target object |\n\n#### 返回值\n\n`T`\n\nobj\n\n---\n\n### storeToRefs %{#storetorefs}%\n\n▸ **storeToRefs**<`SS`\\>(`store`): `ToRefs`<[`StoreState`](pinia.md#storestate)<`SS`\\> & [`StoreGetters`](pinia.md#storegetters)<`SS`\\> & [`PiniaCustomStateProperties`](../interfaces/pinia.PiniaCustomStateProperties.md)<[`StoreState`](pinia.md#storestate)<`SS`\\>\\>\\>\n\n创建一个引用对象，包含 store 的所有 state、\ngetter 和 plugin 添加的 state 属性。\n类似于 `toRefs()`，但专门为 Pinia store 设计，\n所以 method 和非响应式属性会被完全忽略。\n\n#### 类型参数\n\n| 名称 | 类型                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| :--- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `SS` | extends [`_StoreWithState`](../interfaces/pinia._StoreWithState.md)<`string`, [`StateTree`](pinia.md#statetree), [`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>, [`_ActionsTree`](pinia.md#_actionstree), `SS`\\> & [`StateTree`](pinia.md#statetree) & [`_StoreWithGetters`](pinia.md#_storewithgetters)<[`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>\\> & [`PiniaCustomProperties`](../interfaces/pinia.PiniaCustomProperties.md)<`string`, [`StateTree`](pinia.md#statetree), [`_GettersTree`](pinia.md#_getterstree)<[`StateTree`](pinia.md#statetree)\\>, [`_ActionsTree`](pinia.md#_actionstree), `SS`\\> & [`PiniaCustomStateProperties`](../interfaces/pinia.PiniaCustomStateProperties.md)<[`StateTree`](pinia.md#statetree), `SS`\\> |\n\n#### 参数\n\n| 名称    | 类型 | 描述                           |\n| :------ | :--- | :----------------------------- |\n| `store` | `SS` | store to extract the refs from |\n\n#### 返回值\n\n`ToRefs`<[`StoreState`](pinia.md#storestate)<`SS`\\> & [`StoreGetters`](pinia.md#storegetters)<`SS`\\> & [`PiniaCustomStateProperties`](../interfaces/pinia.PiniaCustomStateProperties.md)<[`StoreState`](pinia.md#storestate)<`SS`\\>\\>\\>\n"
  },
  {
    "path": "packages/docs/zh/api/modules/pinia_nuxt.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / @pinia/nuxt\n\n# 模块: @pinia/nuxt %{#module-pinia-nuxt}%\n\n## 接口 %{#interfaces}%\n\n- [ModuleOptions](../interfaces/pinia_nuxt.ModuleOptions.md)\n\n## 函数 %{#functions}%\n\n### 默认值 %{#default}%\n\n▸ **default**(`this`, `inlineOptions`, `nuxt`): `void` \\| `Promise`<`void`\\>\n\n#### 参数\n\n| Name            | Type                                                         |\n| :-------------- | :----------------------------------------------------------- |\n| `this`          | `void`                                                       |\n| `inlineOptions` | [`ModuleOptions`](../interfaces/pinia_nuxt.ModuleOptions.md) |\n| `nuxt`          | `Nuxt`                                                       |\n\n#### 返回值\n\n`void` \\| `Promise`<`void`\\>\n"
  },
  {
    "path": "packages/docs/zh/api/modules/pinia_testing.md",
    "content": "---\nsidebar: 'auto'\neditLinks: false\nsidebarDepth: 3\n---\n\n[API 文档](../index.md) / @pinia/testing\n\n# 模块：@pinia/testing %{#module-pinia-testing}%\n\n## 接口 %{#interfaces}%\n\n- [TestingOptions](../interfaces/pinia_testing.TestingOptions.md)\n- [TestingPinia](../interfaces/pinia_testing.TestingPinia.md)\n\n## 函数 %{#functions}%\n\n### createTestingPinia %{#createtestingpinia}%\n\n▸ **createTestingPinia**(`options?`): [`TestingPinia`](../interfaces/pinia_testing.TestingPinia.md)\n\n创建一个为单元测试设计的 pinia 实例，**需要 mocking** store。\n默认情况下，**所有的 action 都是 mocked 的**，因此不会执行。\n这可以让你对 store 和组件进行单独的单元测试。\n你可以通过 `stubActions` 选项来改变这一点。\n如果你使用 jest，将它们替换为 `jest.fn()`，\n否则，你必须提供你自己的 `createSpy` 选项。\n\n#### 参数\n\n| 名称      | 类型                                                              | 描述                      |\n| :-------- | :---------------------------------------------------------------- | :------------------------ |\n| `options` | [`TestingOptions`](../interfaces/pinia_testing.TestingOptions.md) | 配置 pinia 测试实例的选项 |\n\n#### 返回值\n\n[`TestingPinia`](../interfaces/pinia_testing.TestingPinia.md)\n\n一个增强的 Pinia 实例\n"
  },
  {
    "path": "packages/docs/zh/cookbook/composables.md",
    "content": "# 处理组合式函数 %{#dealing-with-composables}%\n\n<RuleKitLink />\n\n[组合式函数](https://cn.vuejs.org/guide/reusability/composables.html#composables)是利用 Vue 组合式 API 来封装和复用有状态逻辑的函数。无论你是自己写，还是使用[外部库](https://vueuse.org/)，或者两者都有，你都可以在 pinia store 中充分发挥组合式函数的力量。\n\n## Option Stores %{#option-stores}%\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/using-composables-in-option-stores\"\n  title=\"Using Composables in Option Stores\"\n/>\n\n当定义一个 option store 时，你可以在 `state` 属性中调用组合式函数：\n\n```ts\nexport const useAuthStore = defineStore('auth', {\n  state: () => ({\n    user: useLocalStorage('pinia/auth/login', 'bob'),\n  }),\n})\n```\n\n请记住，**你只能返回可写的状态** (例如，一个 `ref()`) 。下面是一些可用的组合式函数的示例：\n\n- [useLocalStorage](https://vueuse.org/core/useLocalStorage/)\n- [useAsyncState](https://vueuse.org/core/useAsyncState/)\n\n下面是一些不可在 option store 中使用的组合式函数 (但可在 setup store 中使用) ：\n\n- [useMediaControls](https://vueuse.org/core/useMediaControls/): exposes functions\n- [useMemoryInfo](https://vueuse.org/core/useMemory/): exposes readonly data\n- [useEyeDropper](https://vueuse.org/core/useEyeDropper/): exposes readonly data and functions\n\n## Setup Stores %{#setup-stores}%\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/using-composables-in-setup-stores\"\n  title=\"Using Composables in Setup Stores\"\n/>\n\n另外，当定义一个 setup store 时，你几乎可以使用任何组合式函数，因为每一个属性都会被辨别为 state 、action 或者 getter：\n\n```ts\nimport { defineStore, skipHydrate } from 'pinia'\nimport { useMediaControls } from '@vueuse/core'\n\nexport const useVideoPlayer = defineStore('video', () => {\n  // 我们不会直接暴露 (返回) 这个元素\n  const videoElement = ref<HTMLVideoElement>()\n  const src = ref('/data/video.mp4')\n  const { playing, volume, currentTime, togglePictureInPicture } =\n    useMediaControls(video, { src })\n\n  function loadVideo(element: HTMLVideoElement, src: string) {\n    videoElement.value = element\n    src.value = src\n  }\n\n  return {\n    src,\n    playing,\n    volume,\n    currentTime,\n\n    loadVideo,\n    togglePictureInPicture,\n  }\n})\n```\n\n:::warning\n和常规的状态不同，`ref<HTMLVideoElement>()` 包含了一个不可序列化的 DOM 元素引用。这就是为什么我们不直接返回它的原因。由于它是客户端专用的状态，我们知道它不会被设置在服务器上，并且在客户端上**始终**以 `undefined` 作为开始。\n:::\n\n## 服务端渲染 %{#ssr}%\n\n当处理[服务端渲染](../ssr/index.md)时，你有一些需要额外注意的内容，以便在 store 中使用组合式函数。\n\n在 [Option Store](#option-stores) 中，你需要定义一个 `hydrate()` 函数。当 store 在客户端 (浏览器) 上被实例化的过程中，创建 store 时有一个可用的初始状态时，这个函数就会被调用。我们需要定义这个函数的原因是，在这种情况下，`state()` 是不会被调用的。\n\n```ts\nimport { defineStore, skipHydrate } from 'pinia'\nimport { useLocalStorage } from '@vueuse/core'\n\nexport const useAuthStore = defineStore('auth', {\n  state: () => ({\n    user: useLocalStorage('pinia/auth/login', 'bob'),\n  }),\n\n  hydrate(state, initialState) {\n    // 在这种情况下，我们可以完全忽略初始状态\n    // 因为我们想从浏览器中读取数值。\n    state.user = useLocalStorage('pinia/auth/login', 'bob')\n  },\n})\n```\n\n在 [Setup Store](#setup-stores) 中，对于任何不应该从初始状态中接收的 state 属性 你都需要使用一个名为 `skipHydrate()` 的辅助函数。与 option store 不同，setup store 不能直接**跳过调用 `state()`**，所以我们用 `skipHydrate()` 标记那些不能被激活的属性。请注意，这只适用于可写的响应式属性：\n\n```ts\nimport { defineStore, skipHydrate } from 'pinia'\nimport { useEyeDropper, useLocalStorage } from '@vueuse/core'\n\nconst useColorStore = defineStore('colors', () => {\n  const { isSupported, open, sRGBHex } = useEyeDropper()\n  const lastColor = useLocalStorage('lastColor', sRGBHex)\n  // ...\n  return {\n    lastColor: skipHydrate(lastColor), // Ref<string>\n    open, // Function\n    isSupported, // boolean (非响应式)\n  }\n})\n```\n"
  },
  {
    "path": "packages/docs/zh/cookbook/composing-stores.md",
    "content": "# 组合式 Store %{#composing-stores}%\n\n<RuleKitLink />\n\n组合式 store 是可以相互使用，Pinia 当然也支持它。但有一个规则需要遵循：\n\n如果**两个或更多的 store 相互使用**，它们不可以通过 _getters_ 或 _actions_ 创建一个无限循环。它们也不可以**同时**在它们的 setup 函数中直接互相读取对方的 state：\n\n```js\nconst useX = defineStore('x', () => {\n  const y = useY()\n\n  // ❌ 这是不可以的，因为 y 也试图读取 x.name\n  y.name\n\n  function doSomething() {\n    // ✅ 读取 computed 或 action 中的 y 属性\n    const yName = y.name\n    // ...\n  }\n\n  return {\n    name: ref('I am X'),\n  }\n})\n\nconst useY = defineStore('y', () => {\n  const x = useX()\n\n  // ❌ 这是不可以的，因为 x 也试图读取 y.name\n  x.name\n\n  function doSomething() {\n    // ✅ 读取 computed 或 action 中的 x 属性\n    const xName = x.name\n    // ...\n  }\n\n  return {\n    name: ref('I am Y'),\n  }\n})\n```\n\n## 嵌套 store %{#nested-stores}%\n\n注意，如果一个 store 使用另一个 store，你可以直接导入并在 _actions_ 和 _getters_ 中调用 `useStore()` 函数。然后你就可以像在 Vue 组件中那样使用 store。参考[共享 Getter](#shared-getters) 和[共享 Action](#shared-actions)。\n\n对于 _setup store_ ，你直接使用 store 函数**顶部**的一个 store：\n\n```ts\nimport { useUserStore } from './user'\n\nexport const useCartStore = defineStore('cart', () => {\n  const user = useUserStore()\n  const list = ref([])\n\n  const summary = computed(() => {\n    return `Hi ${user.name}, you have ${list.value.length} items in your cart. It costs ${price.value}.`\n  })\n\n  function purchase() {\n    return apiPurchase(user.id, this.list)\n  }\n\n  return { summary, purchase }\n})\n```\n\n## 共享 Getter %{#shared-getters}%\n\n你可以直接在一个 _getter_ 中调用 `useUserStore()`：\n\n```js\nimport { defineStore } from 'pinia'\nimport { useUserStore } from './user'\n\nexport const useCartStore = defineStore('cart', {\n  getters: {\n    summary(state) {\n      const user = useUserStore()\n\n      return `Hi ${user.name}, you have ${state.list.length} items in your cart. It costs ${state.price}.`\n    },\n  },\n})\n```\n\n## 共享 Actions %{#shared-actions}%\n\n_actions_ 也一样：\n\n```js\nimport { defineStore } from 'pinia'\nimport { useUserStore } from './user'\n\nexport const useCartStore = defineStore('cart', {\n  actions: {\n    async orderCart() {\n      const user = useUserStore()\n\n      try {\n        await apiOrderCart(user.token, this.items)\n        // 其他 action\n        this.emptyCart()\n      } catch (err) {\n        displayError(err)\n      }\n    },\n  },\n})\n```\n"
  },
  {
    "path": "packages/docs/zh/cookbook/hot-module-replacement.md",
    "content": "# HMR (Hot Module Replacement) %{#hmr-hot-module-replacement}%\n\n<RuleKitLink />\n\nPinia 支持热更新，所以你可以编辑你的 store，并直接在你的应用中与它们互动，而不需要重新加载页面，允许你保持当前的 state、并添加甚至删除 state、action 和 getter。\n\n目前，只有 [Vite](https://cn.vitejs.dev/guide/api-hmr#hmr-api) 被官方支持，不过任何实现 `import.meta.hot` 规范的构建工具都应该能正常工作。(例外的是，[webpack](https://webpack.js.org/api/module-variables/#importmetawebpackhot) 似乎使用的是 `import.meta.webpackHot` 而不是 `import.meta.hot` )\n你只需要在任何 store 声明旁边添加这段代码。比方说，你有三个 store：`auth.js`、 `cart.js` 和 `chat.js`, 你必须在每个 **store 声明**后都添加(和调整)这段代码。\n\n```js\n// auth.js\nimport { defineStore, acceptHMRUpdate } from 'pinia'\n\nconst useAuth = defineStore('auth', {\n  // 配置...\n})\n\n// 确保传递正确的 store 声明，本例中为 `useAuth`\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot))\n}\n```\n"
  },
  {
    "path": "packages/docs/zh/cookbook/index.md",
    "content": "# 手册 %{#cookbook}%\n\n<RuleKitLink />\n\n- [从 Vuex ≤4 迁移](./migration-vuex.md)。用于转换 Vuex ≤4 项目的迁移指南。\n- [HMR](./hot-module-replacement.md)：如何激活热更新并改善开发者体验。\n- [测试 Stores (WIP)](./testing.md): 如何对 Store 进行单元测试并在组件单元测试中模拟它们。\n- [Composing Stores](./composing-stores.md): 如何交叉使用多个 store，例如在购物车 store 中使用用户 store。\n- [选项式 API](./options-api.md): 如何在 `setup()` 外部使用 Pinia 而不使用组合式 API。\n- [从 0.0.7 迁移](./migration-0-0-7.md)。迁移指南，比更新日志有更多的例子。\n"
  },
  {
    "path": "packages/docs/zh/cookbook/migration-0-0-7.md",
    "content": "# Migrating from 0.0.7 %{#migrating-from-0-0-7}%\n\nThe versions after `0.0.7`: `0.1.0`, and `0.2.0`, came with a few big breaking changes. This guide helps you migrate whether you use Vue 2 or Vue 3. The whole changelog can be found in the repository:\n\n- [For Pinia <= 1 for Vue 2](https://github.com/vuejs/pinia/blob/v1/CHANGELOG.md)\n- [For Pinia >= 2 for Vue 3](https://github.com/vuejs/pinia/blob/v3/packages/pinia/CHANGELOG.md)\n\nIf you have questions or issues regarding the migration, feel free to [open a discussion](https://github.com/vuejs/pinia/discussions/categories/q-a) to ask for help.\n\n## No more `store.state` %{#no-more-store-state}%\n\nYou no longer access the store state via a `state` property, you can directly access any state property.\n\nGiven a store defined with:\n\n```js\nconst useStore({\n  id: 'main',\n  state: () => ({ count: 0 })\n})\n```\n\nDo\n\n```diff\n const store = useStore()\n\n-store.state.count++\n+store.count.++\n```\n\nYou can still access the whole store state with `$state` when needed:\n\n```diff\n-store.state = newState\n+store.$state = newState\n```\n\n## Rename of store properties %{#rename-of-store-properties}%\n\nAll store properties (`id`, `patch`, `reset`, etc) are now prefixed with `$` to allow properties defined on the store with the same names. Tip: you can refactor your whole codebase with F2 (or right-click + Refactor) on each of the store's properties\n\n```diff\n const store = useStore()\n-store.patch({ count: 0 })\n+store.$patch({ count: 0 })\n\n-store.reset()\n+store.$reset()\n\n-store.id\n+store.$id\n```\n\n## The Pinia instance %{#the-pinia-instance}%\n\nIt's now necessary to create a pinia instance and install it:\n\nIf you are using Vue 2 (Pinia <= 1):\n\n```js\nimport Vue from 'vue'\nimport { createPinia, PiniaVuePlugin } from 'pinia'\n\nconst pinia = createPinia()\nVue.use(PiniaVuePlugin)\nnew Vue({\n  el: '#app',\n  pinia,\n  // ...\n})\n```\n\nIf you are using Vue 3 (Pinia >= 2):\n\n```js\nimport { createApp } from 'vue'\nimport { createPinia, PiniaVuePlugin } from 'pinia'\nimport App from './App.vue'\n\nconst pinia = createPinia()\ncreateApp(App).use(pinia).mount('#app')\n```\n\nThe `pinia` instance is what holds the state and should **be unique per application**. Check the SSR section of the docs for more details.\n\n## SSR changes %{#ssr-changes}%\n\nThe SSR plugin `PiniaSsr` is no longer necessary and has been removed.\nWith the introduction of pinia instances, `getRootState()` is no longer necessary and should be replaced with `pinia.state.value`:\n\nIf you are using Vue 2 (Pinia <= 1):\n\n```diff\n// entry-server.js\n-import { getRootState, PiniaSsr } from 'pinia',\n+import { createPinia, PiniaVuePlugin } from 'pinia',\n\n\n-// install plugin to automatically use correct context in setup and onServerPrefetch\n-Vue.use(PiniaSsr);\n+Vue.use(PiniaVuePlugin)\n\n export default context => {\n+  const pinia = createPinia()\n   const app = new Vue({\n     // other options\n+    pinia\n   })\n\n   context.rendered = () => {\n     // pass state to context\n-    context.piniaState = getRootState(context.req)\n+    context.piniaState = pinia.state.value\n   };\n\n-   return { app }\n+   return { app, pinia }\n }\n```\n\n`setActiveReq()` and `getActiveReq()` have been replaced with `setActivePinia()` and `getActivePinia()` respectively. `setActivePinia()` can only be passed a `pinia` instance created with `createPinia()`. **Note that most of the time you won't directly use these functions**.\n"
  },
  {
    "path": "packages/docs/zh/cookbook/migration-v1-v2.md",
    "content": "# 从 0.x (v1) 迁移至 v2 %{#migrating-from-0-x-v1-to-v2}%\n\n<RuleKitLink />\n\n从 `2.0.0-rc.4` 版本开始，pinia 同时支持 Vue 2 和 Vue 3！这意味着，v2 版本的所有更新，将会让 Vue 2 和 Vue 3 的用户都受益。如果你使用的是 Vue 3，这对你来说没有任何改变，因为你已经在使用 rc 版本，你可以查看[发布日志](https://github.com/vuejs/pinia/blob/v2/packages/pinia/CHANGELOG.md)来了解所有更新的详细解释。如果你使用的不是 Vue 3，**那这个指南是为你准备的**!\n\n## 弃用 %{#deprecations}%\n\n让我们来看看你需要对你的代码做出的所有修改。首先，为了解所有弃用，确保你已经在运行最新的 0.x 版本：\n\n```shell\nnpm i 'pinia@^0.x.x'\n# 或者使用 yarn\nyarn add 'pinia@^0.x.x'\n```\n\n如果你正在使用 ESLint，可以考虑使用[这个插件](https://github.com/gund/eslint-plugin-deprecation)，来查找所有废弃的用法。否则，你得手动检查。这些都是被废弃且已经删除了的 API：\n\n- `createStore()` 变成 `defineStore()`\n- 在订阅中，`storeName` 变成 `storeId`\n- `PiniaPlugin` 更名为 `PiniaVuePlugin`(Vue 2 的 Pinia 插件)\n- `$subscribe()` 不再接受 _boolean_ 作为第二个参数，而是传递一个带有 `detached: true` 的对象。\n- Pinia 插件不再直接接收 store 的 `id`。使用 `store.$id` 代替。\n\n## 非兼容性更新 %{#breaking-changes}%\n\n删除下面这些后，你可以用下面命令升级到 V2 版了：\n\n```shell\nnpm i 'pinia@^2.x.x'\n# 或者使用 yarn\nyarn add 'pinia@^2.x.x'\n```\n\n然后开始更新你的代码。\n\n### 通用 Store 类型 %{#generic-store-type}%\n\n添加于 [2.0.0-rc.0](https://github.com/vuejs/pinia/blob/v2/packages/pinia/CHANGELOG.md#200-rc0-2021-07-28)\n\n用 `StoreGeneric` 取代 `GenericStore` 类型的全部用法。这是新的通用 store 类型，应该可以接受任何类型的 store。如果你在写函数时使用 `Store` 类型而不想传递其泛型 (例如`Store<Id, State, Getters, Actions>`)，你可以使用 `StoreGeneric`，因为没有泛型的 `Store` 类型会创建一个空的 store 类型：\n\n```ts\nfunction takeAnyStore(store: Store) {} // [!code --]\nfunction takeAnyStore(store: StoreGeneric) {} // [!code ++]\nfunction takeAnyStore(store: GenericStore) {} // [!code --]\nfunction takeAnyStore(store: StoreGeneric) {} // [!code ++]\n```\n\n## 针对插件的 `DefineStoreOptions` %{#definestoreoptions-for-plugins}%\n\n如果你在用 TypeScript 写插件并扩展了 `DefineStoreOptions` 类型来添加自定义选项，你应该把它改名为 `DefineStoreOptionsBase`。这个类型将同时适用于 setup 和 option store。\n\n```ts\ndeclare module 'pinia' {\n  export interface DefineStoreOptions<S, Store> { // [!code --]\n  export interface DefineStoreOptionsBase<S, Store> { // [!code ++]\n    debounce?: {\n      [k in keyof StoreActions<Store>]?: number\n    }\n  }\n}\n```\n\n## `PiniaStorePlugin` 已被重命名 %{#piniastoreplugin-was-renamed}%\n\n类型 `PiniaStorePlugin` 被重新命名为 `PiniaPlugin`。\n\n```ts\nimport { PiniaStorePlugin } from 'pinia' // [!code --]\nimport { PiniaPlugin } from 'pinia' // [!code ++]\nconst piniaPlugin: PiniaStorePlugin = () => { // [!code --]\nconst piniaPlugin: PiniaPlugin = () => { // [!code ++]\n  // ...\n}\n```\n\n**注意这个更新只能在升级到最新的没有弃用的 Pinia 版本后生效**。\n\n## `@vue/composition-api` 版本 %{#vue-composition-api-version}%\n\n由于 pinia 目前依赖于 `effectScope()` ，你使用的 `@vue/composition-api` 的版本必须是 `1.1.0` 及以上：\n\n```shell\nnpm i @vue/composition-api@latest\n# 或者使用 yarn\nyarn add @vue/composition-api@latest\n```\n\n## 支持 webpack 4 %{#webpack-4-support}%\n\n如果你使用的是 webpack 4 (Vue CLI 使用的是 webpack 4)，你可能会遇到这样的错误：\n\n```\nERROR  Failed to compile with 18 errors\n\n error  in ./node_modules/pinia/dist/pinia.mjs\n\nCan't import the named export 'computed' from non EcmaScript module (only default export is available)\n```\n\n这是构建文件为支持 Node.js 中的原生 ESM 模块进行的现代化适配。为更好地支持 Node，文件现在使用的扩展名是 `.mjs` 和 `.cjs`。要解决这个问题，你有两种可用的方法：\n\n- 如果你使用 Vue CLI 4.x，升级你的依赖。具体修复步骤如下。\n  - 如果你不可能升级，请将下面的代码添加到你的 `vue.config.js` 中：\n\n    ```js\n    // vue.config.js\n    module.exports = {\n      configureWebpack: {\n        module: {\n          rules: [\n            {\n              test: /\\.mjs$/,\n              include: /node_modules/,\n              type: 'javascript/auto',\n            },\n          ],\n        },\n      },\n    }\n    ```\n\n- 如果你手动处理 webpack，你将必须让它知道如何处理 `.mjs` 文件：\n\n  ```js\n  // webpack.config.js\n  module.exports = {\n    module: {\n      rules: [\n        {\n          test: /\\.mjs$/,\n          include: /node_modules/,\n          type: 'javascript/auto',\n        },\n      ],\n    },\n  }\n  ```\n\n## Devtools %{#devtools}%\n\nPinia v2 不再劫持 Vue Devtools v5，它需要的是 Vue Devtools v6。可以在 [Vue Devtools 文档](https://devtools.vuejs.org/guide/installation.html#chrome)中找到该扩展 **beta 版本**的下载链接。\n\n## Nuxt %{#nuxt}%\n\n如果你正在使用 Nuxt，pinia 现在有了专门的 Nuxt 软件包🎉。请用以下方法安装它：\n\n```bash\nnpm i @pinia/nuxt\n# 或者使用 yarn\nyarn add @pinia/nuxt\n```\n\n还要确保**更新你的 `@nuxtjs/composition-api` 包**。\n\n如果你使用 TypeScript，还要调整你的 `nuxt.config.js` 和 `tsconfig.json`：\n\n```js\n// nuxt.config.js\nmodule.exports {\n  buildModules: [\n    '@nuxtjs/composition-api/module',\n    'pinia/nuxt', // [!code --]\n    '@pinia/nuxt', // [!code ++]\n  ],\n}\n```\n\n```json\n// tsconfig.json\n{\n  \"types\": [\n    // ...\n    \"pinia/nuxt/types\" // [!code --]\n    \"@pinia/nuxt\" // [!code ++]\n  ]\n}\n```\n\n[Nuxt 专属章节](../ssr/nuxt.md)也值得一读。\n"
  },
  {
    "path": "packages/docs/zh/cookbook/migration-vuex.md",
    "content": "# 从 Vuex ≤4 迁移 %{#migrating-from-vuex-≤4}%\n\n<RuleKitLink />\n\n虽然 Vuex 和 Pinia store 的结构不同，但很多逻辑都可以复用。本指南的作用是帮助你完成迁移，并指出一些可能出现的常见问题。\n\n## 准备 %{#preparation}%\n\n首先，按照[入门指南](../getting-started.md)安装 Pinia。\n\n## 重构 store 的模块 %{#restructuring-modules-to-stores}%\n\nVuex 有一个概念，带有多个模块的单一 store。这些模块可以被命名，甚至可以互相嵌套。\n\n将这个概念过渡到 Pinia 最简单的方法是，你以前使用的每个模块现在都是一个 _store_。每个 store 都需要一个 `id`，类似于 Vuex 中的命名空间。这意味着每个 store 都有命名空间的设计。嵌套模块也可以成为自己的 store。互相依赖的 store 可以直接导入其他 store。\n\n你的 Vuex 模块如何重构为 Pinia store，完全取决于你，不过这里有一个示例：\n\n```bash\n# Vuex 示例(假设是命名模块)。\nsrc\n└── store\n    ├── index.js           # 初始化 Vuex，导入模块\n    └── modules\n        ├── module1.js     # 命名模块 'module1'\n        └── nested\n            ├── index.js   # 命名模块 'nested'，导入 module2 与 module3\n            ├── module2.js # 命名模块 'nested/module2'\n            └── module3.js # 命名模块 'nested/module3'\n\n# Pinia 示例，注意 ID 与之前的命名模块相匹配\nsrc\n└── stores\n    ├── index.js          # (可选) 初始化 Pinia，不必导入 store\n    ├── module1.js        # 'module1' id\n    ├── nested-module2.js # 'nested/module2' id\n    ├── nested-module3.js # 'nested/module3' id\n    └── nested.js         # 'nested' id\n```\n\n这为 store 创建了一个扁平的结构，但也保留了和之前等价的 `id` 命名方式。如果你在根 store (在 Vuex 的 `store/index.js` 文件中)中有一些 state/getter/action/mutation，你可以创建一个名为 `root` 的 store，来保存它们。\n\nPinia 的目录一般被命名为 `stores` 而不是 `store`。这是为了强调 Pinia 可以使用多个 store，而不是 Vuex 的单一 store。\n\n对于大型项目，你可能希望逐个模块进行转换，而不是一次性全部转换。其实在迁移过程中，你可以同时使用 Pinia 和 Vuex。这样也完全可以正常工作，这也是将 Pinia 目录命名为 `stores` 的另一个原因。\n\n## 转换单个模块 %{#converting-a-single-module}%\n\n下面有一个完整的例子，介绍了将 Vuex 模块转换为 Pinia store 的完整过程，请看下面的逐步指南。Pinia 的例子使用了一个 option store，因为其结构与 Vuex 最为相似。\n\n```ts\n// 'auth/user' 命名空间中的 Vuex 模块\nimport { Module } from 'vuex'\nimport { api } from '@/api'\nimport { RootState } from '@/types' // 如果需要使用 Vuex 的类型便需要引入\n\ninterface State {\n  firstName: string\n  lastName: string\n  userId: number | null\n}\n\nconst storeModule: Module<State, RootState> = {\n  namespaced: true,\n  state: {\n    firstName: '',\n    lastName: '',\n    userId: null,\n  },\n  getters: {\n    firstName: (state) => state.firstName,\n    fullName: (state) => `${state.firstName} ${state.lastName}`,\n    loggedIn: (state) => state.userId !== null,\n    // 与其他模块的一些状态相结合\n    fullUserDetails: (state, getters, rootState, rootGetters) => {\n      return {\n        ...state,\n        fullName: getters.fullName,\n        // 读取另一个名为 `auth` 模块的 state\n        ...rootState.auth.preferences,\n        // 读取嵌套于 `auth` 模块的 `email` 模块的 getter\n        ...rootGetters['auth/email'].details,\n      }\n    },\n  },\n  actions: {\n    async loadUser({ state, commit }, id: number) {\n      if (state.userId !== null) throw new Error('Already logged in')\n      const res = await api.user.load(id)\n      commit('updateUser', res)\n    },\n  },\n  mutations: {\n    updateUser(state, payload) {\n      state.firstName = payload.firstName\n      state.lastName = payload.lastName\n      state.userId = payload.userId\n    },\n    clearUser(state) {\n      state.firstName = ''\n      state.lastName = ''\n      state.userId = null\n    },\n  },\n}\n\nexport default storeModule\n```\n\n```ts\n// Pinia Store\nimport { defineStore } from 'pinia'\nimport { useAuthPreferencesStore } from './auth-preferences'\nimport { useAuthEmailStore } from './auth-email'\nimport vuexStore from '@/store' // 逐步转换，见 fullUserDetails\n\ninterface State {\n  firstName: string\n  lastName: string\n  userId: number | null\n}\n\nexport const useAuthUserStore = defineStore('auth/user', {\n  // 转换为函数\n  state: (): State => ({\n    firstName: '',\n    lastName: '',\n    userId: null,\n  }),\n  getters: {\n    // 不在需要 firstName getter，移除\n    fullName: (state) => `${state.firstName} ${state.lastName}`,\n    loggedIn: (state) => state.userId !== null,\n    // 由于使用了 `this`，必须定义一个返回类型\n    fullUserDetails(state): FullUserDetails {\n      // 导入其他 store\n      const authPreferencesStore = useAuthPreferencesStore()\n      const authEmailStore = useAuthEmailStore()\n      return {\n        ...state,\n        // `this` 上的其他 getter\n        fullName: this.fullName,\n        ...authPreferencesStore.$state,\n        ...authEmailStore.details,\n      }\n\n      // 如果其他模块仍在 Vuex 中，可替代为\n      // return {\n      //   ...state,\n      //   fullName: this.fullName,\n      //   ...vuexStore.state.auth.preferences,\n      //   ...vuexStore.getters['auth/email'].details\n      // }\n    },\n  },\n  actions: {\n    //没有作为第一个参数的上下文，用 `this` 代替\n    async loadUser(id: number) {\n      if (this.userId !== null) throw new Error('Already logged in')\n      const res = await api.user.load(id)\n      this.updateUser(res)\n    },\n    // mutation 现在可以成为 action 了，不再用 `state` 作为第一个参数，而是用 `this`。\n    updateUser(payload) {\n      this.firstName = payload.firstName\n      this.lastName = payload.lastName\n      this.userId = payload.userId\n    },\n    // 使用 `$reset` 可以轻松重置 state\n    clearUser() {\n      this.$reset()\n    },\n  },\n})\n```\n\n让我们把上述内容分解成几个步骤：\n\n1. 为 store 添加一个必要的 `id`，你可以让它与之前的命名保持相同。\n2. 如果 `state` 不是一个函数的话 将它转换为一个函数。\n3. 转换 `getters`\n   1. 删除任何返回同名 state 的 getters (例如： `firstName: (state) => state.firstName`)，这些都不是必需的，因为你可以直接从 store 实例中访问任何状态。\n   2. 如果你需要访问其他的 getter，可通过 `this` 访问它们，而不是第二个参数。记住，如果你使用 `this`，而且你不得不使用一个普通函数，而不是一个箭头函数，那么由于 TS 的限制，你需要指定一个返回类型，更多细节请阅读[这篇文档](../core-concepts/getters.md#accessing-other-getters)\n   3. 如果使用 `rootState` 或 `rootGetters` 参数，可以直接导入其他 store 来替代它们，或者如果它们仍然存在于 Vuex ，则直接从 Vuex 中访问它们。\n4. 转换 `actions`\n   1. 从每个 action 中删除第一个 `context` 参数。所有的东西都应该直接从 `this` 中访问。\n   2. 如果使用其他 store，要么直接导入，要么与 getters 一样，在 Vuex 上访问。\n5. 转换 `mutations`\n   1. Mutation 已经被弃用了。它们可以被转换为 `action`，或者你可以在你的组件中直接赋值给 store (例如：`userStore.firstName = 'First'`)\n   2. 如果你想将它转换为 action，删除第一个 `state` 参数，用 `this` 代替任何赋值操作中的 `state`。\n   3. 一个常见的 mutation 是将 state 重置为初始 state。而这就是 store 的 `$reset` 方法的内置功能。注意，这个功能只存在于 option stores。\n\n正如你所看到的，你的大部分代码都可以被重复使用。如果有什么遗漏，类型安全也应该可以帮助你确定需要修改的地方。\n\n## 组件内的使用 %{#usage-inside-components}%\n\n现在你的 Vuex 模块已经被转换为 Pinia store，但其他使用该模块的组件或文件也需要更新。\n\n如果你以前使用的是 Vuex 的 `map` 辅助函数，可以看看[不使用 setup() 的用法指南](./options-api.md)，因为这些辅助函数大多都是可以复用的。\n\n如果你以前使用的是 `useStore`，那么就直接导入新 store 并访问其上的 state。比如说：\n\n```ts\n// Vuex\nimport { defineComponent, computed } from 'vue'\nimport { useStore } from 'vuex'\n\nexport default defineComponent({\n  setup() {\n    const store = useStore()\n\n    const firstName = computed(() => store.state.auth.user.firstName)\n    const fullName = computed(() => store.getters['auth/user/fullName'])\n\n    return {\n      firstName,\n      fullName,\n    }\n  },\n})\n```\n\n```ts\n// Pinia\nimport { defineComponent, computed } from 'vue'\nimport { useAuthUserStore } from '@/stores/auth-user'\n\nexport default defineComponent({\n  setup() {\n    const authUserStore = useAuthUserStore()\n\n    const firstName = computed(() => authUserStore.firstName)\n    const fullName = computed(() => authUserStore.fullName)\n\n    return {\n      // 你也可以在你的组件中通过返回 store 来访问整个 store\n      authUserStore,\n      firstName,\n      fullName,\n    }\n  },\n})\n```\n\n## 组件外的使用 %{#usage-outside-components}%\n\n只要你注意**不在函数外使用 store**，单独更新组件外的用法应该很简单。下面是一个在 Vue Router 导航守卫中使用 store 的例子：\n\n```ts\n// Vuex\nimport vuexStore from '@/store'\n\nrouter.beforeEach((to, from, next) => {\n  if (vuexStore.getters['auth/user/loggedIn']) next()\n  else next('/login')\n})\n```\n\n```ts\n// Pinia\nimport { useAuthUserStore } from '@/stores/auth-user'\n\nrouter.beforeEach((to, from, next) => {\n  // 必须在函数内部使用\n  const authUserStore = useAuthUserStore()\n  if (authUserStore.loggedIn) next()\n  else next('/login')\n})\n```\n\n更多细节可在[这里](../core-concepts/outside-component-usage.md)找到。\n\n## Vuex 高级用法 %{#advanced-vuex-usage}%\n\n如果你的 Vuex store 使用了它所提供的一些更高级的功能，也有一些关于如何在 Pinia 中实现同样效果的指导。其中一些要点已经包含在这个[对比总结](../introduction.md#comparison-with-vuex-3-x-4-x)里了。\n\n### 动态模块 %{#dynamic-modules}%\n\n在 Pinia 中不需要动态注册模块。store 设计之初就是动态的，只有在需要时才会被注册。如果一个 store 从未被使用过，它就永远不会被 “注册”。\n\n### 热更新 %{#hot-module-replacement}%\n\n支持 HMR，但需要一些修改，见[HMR 指南](./hot-module-replacement.md)。\n\n### 插件 %{#plugins}%\n\n如果你使用的是一个公共的 Vuex 插件，那么请检查是否有一个 Pinia 版的替代品。如果没有，你就需要自己写一个，或者评估一下是否还有必要使用这个插件。\n\n如果你已经写了一个自己的插件，那么你完全可以更新它来适配 pinia，参考[插件指南](../core-concepts/plugins.md)。\n"
  },
  {
    "path": "packages/docs/zh/cookbook/options-api.md",
    "content": "# 不使用 `setup()` 的用法 %{#usage-without-setup}%\n\n<RuleKitLink />\n\n即使你没有使用组合式 API，也可以使用 Pinia(如果你使用 Vue 2，你仍然需要安装 `@vue/composition-api` 插件)。虽然我们推荐你试着学习一下组合式 API，但对你和你的团队来说目前可能还不是时候，你可能正在迁移一个应用，或者有其他原因。你可以试试下面几个函数：\n\n- [mapStores](#giving-access-to-the-whole-store)\n- [mapState](../core-concepts/state.md#usage-with-the-options-api)\n- [mapWritableState](../core-concepts/state.md#modifiable-state)\n- ⚠️ [mapGetters](../core-concepts/getters.md#without-setup) (只是为了迁移方便，请用 `mapState()` 代替)\n- [mapActions](../core-concepts/actions.md#without-setup)\n\n## 给予整个 store 的访问权 %{#giving-access-to-the-whole-store}%\n\n如果你需要访问 store 里的大部分内容，映射 store 的每一个属性可能太麻烦。你可以试试用 `mapStores()` 来访问整个 store：\n\n```js\nimport { mapStores } from 'pinia'\n\n// 给出具有以下 id 的两个 store\nconst useUserStore = defineStore('user', {\n  // ...\n})\nconst useCartStore = defineStore('cart', {\n  // ...\n})\n\nexport default {\n  computed: {\n    // 注意，我们不是在传递一个数组，而是一个接一个的 store。\n    // 可以 id+'Store' 的形式访问每个 store 。\n    ...mapStores(useCartStore, useUserStore),\n  },\n\n  methods: {\n    async buyStuff() {\n      // 可以在任何地方使用他们！\n      if (this.userStore.isAuthenticated()) {\n        await this.cartStore.buy()\n        this.$router.push('/purchased')\n      }\n    },\n  },\n}\n```\n\n默认情况下，Pinia 会在每个 store 的 `id` 后面加上 `\"Store\"` 的后缀。你可以通过调用 `setMapStoreSuffix()` 来自定义：\n\n```js\nimport { createPinia, setMapStoreSuffix } from 'pinia'\n\n// 完全删除后缀：this.user, this.cart\nsetMapStoreSuffix('')\n// this.user_store, this.cart_store (没关系，我不会批评你的)\nsetMapStoreSuffix('_store')\nexport const pinia = createPinia()\n```\n\n## TypeScript %{#typescript}%\n\n默认情况下，所有映射辅助函数都支持自动补全，你不需要做任何事情。如果你调用 `setMapStoreSuffix()` 修改了 `\"Store\"` 的后缀，你还需要在 TS 文件或 `global.d.ts` 文件的某个地方添加它。最方便的地方就是你调用 `setMapStoreSuffix()` 的地方：\n\n```ts\nimport { createPinia, setMapStoreSuffix } from 'pinia'\n\nsetMapStoreSuffix('') // 完全删除后缀\nexport const pinia = createPinia()\n\ndeclare module 'pinia' {\n  export interface MapStoresCustomization {\n    // 设置成和上面一样的值\n    suffix: ''\n  }\n}\n```\n\n:::warning\n如果你使用的是 TypeScript 声明文件(如 `global.d.ts`)，请确保在文件顶部 `import 'pinia'`，以暴露所有现有类型。\n:::\n"
  },
  {
    "path": "packages/docs/zh/cookbook/testing.md",
    "content": "# store 测试 %{#testing-stores}%\n\n<RuleKitLink />\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/65f9a9c10bfab01f414c25dc\"\n  title=\"Watch a free video of Mastering Pinia about testing stores\"\n/>\n\n从设计上来说，许多地方都会使用 store，所以可能比正常情况更难测试。但幸运的是，这不一定是真的。在测试 store 时，我们需要注意三件事：\n\n- `pinia` 实例：没有它，store 不能正常工作\n- `actions`：大多数时候，它们包含了 store 最复杂的逻辑。如果它们默认就可以被 mocked，那不是很好吗？\n- 插件：如果你依赖插件，你也必须为测试安装它们\n\n根据测试的内容和方式，我们需要以不同的方式来处理这三个问题：\n\n- [store 测试](#testing-stores)\n  - [对 store 单元测试](#unit-testing-a-store)\n  - [对组件单元测试](#unit-testing-components)\n    - [初始 state](#initial-state)\n    - [自定义 action 的行为](#customizing-behavior-of-actions)\n    - [指定 createSpy 函数](#specifying-the-createspy-function)\n    - [Mocking getters](#mocking-getters)\n    - [Pinia 插件](#pinia-plugins)\n  - [端到端测试](#e2e-tests)\n  - [对组件单元测试(Vue 2)](#unit-test-components-vue-2)\n\n## 对 store 进行单元测试 %{#unit-testing-a-store}%\n\n要对一个 store 进行单元测试，最重要的是创建一个 `pinia` 实例：\n\n```js\n// stores/counter.spec.ts\nimport { setActivePinia, createPinia } from 'pinia'\nimport { useCounter } from '../src/stores/counter'\n\ndescribe('Counter Store', () => {\n  beforeEach(() => {\n    // 创建一个新 pinia，并使其处于激活状态，这样它就会被任何 useStore() 调用自动接收\n    // 而不需要手动传递：\n    // `useStore(pinia)`\n    setActivePinia(createPinia())\n  })\n\n  it('increments', () => {\n    const counter = useCounter()\n    expect(counter.n).toBe(0)\n    counter.increment()\n    expect(counter.n).toBe(1)\n  })\n\n  it('increments by amount', () => {\n    const counter = useCounter()\n    counter.increment(10)\n    expect(counter.n).toBe(10)\n  })\n})\n```\n\n如果你有使用任何 store 的插件，有一件重要的事情需要了解：**在 `pinia` 被安装在一个应用之后，插件才会被使用**。可以通过创建一个空的或假的应用来解决这个问题：\n\n```js\nimport { setActivePinia, createPinia } from 'pinia'\nimport { createApp } from 'vue'\nimport { somePlugin } from '../src/stores/plugin'\n\n// 和前面一样的代码...\n\n// 测试前你不需要创建应用\nconst app = createApp({})\nbeforeEach(() => {\n  const pinia = createPinia().use(somePlugin)\n  app.use(pinia)\n  setActivePinia(pinia)\n})\n```\n\n## 对组件单元测试 %{#unit-testing-components}%\n\n这可以通过 `createTestingPinia()` 实现，它会返回一个仅用于帮助对组件单元测试的 pinia 实例。\n\n从安装 `@pinia/testing` 开始：\n\n```shell\nnpm i -D @pinia/testing\n```\n\n确保挂载组件时，在你的测试中创建一个用于测试的 pinia 实例：\n\n```js\nimport { mount } from '@vue/test-utils'\nimport { createTestingPinia } from '@pinia/testing'\n// 引入任何你想要测试的 store\nimport { useSomeStore } from '@/stores/myStore'\n\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [createTestingPinia()],\n  },\n})\n\nconst store = useSomeStore() // // 使用 pinia 的测试实例!\n\n// 可直接操作 state\nstore.name = 'my new name'\n// 也可以通过 patch 来完成\nstore.$patch({ name: 'new name' })\nexpect(store.name).toBe('new name')\n\n// action 默认是存根的(stubbed)，意味着它们默认不执行其代码。\n// 请看下面的内容来定制这一行为。\nstore.someAction()\n\nexpect(store.someAction).toHaveBeenCalledTimes(1)\nexpect(store.someAction).toHaveBeenLastCalledWith()\n```\n\n请注意，如果你使用的是 Vue 2，`@vue/test-utils` 需要一个[轻微不同的配置](#unit-test-components-vue-2)。\n\n### 初始 State %{#initial-state}%\n\n在创建测试 Pinia 时，你可以通过传递一个 `initialState` 对象来设置**所有 store 的初始状态**。这个对象将被 pinia 的测试实例用于创建 store 时 _patch_ store。比方说，你想初始化这个 store 的状态：\n\n```ts\nimport { defineStore } from 'pinia'\n\nconst useCounterStore = defineStore('counter', {\n  state: () => ({ n: 0 }),\n  // ...\n})\n```\n\n由于 store 的名字是 _\"counter\"_，所以你需要传递相应的对象给 `initialState`：\n\n```ts\n// 在测试中的某处\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        initialState: {\n          counter: { n: 20 }, //从 20 开始计数，而不是 0\n        },\n      }),\n    ],\n  },\n})\n\nconst store = useSomeStore() // 使用 pinia 的测试实例!\nstore.n // 20\n```\n\n### 自定义 action 的行为 %{#customizing-behavior-of-actions}%\n\n除非另有指示，`createTestingPinia` 会存根 (stub) 出所有的 store action。这样可以让你独立测试你的组件和 store。\n\n如果你想恢复这种行为，并在测试中正常执行 action，请在调用 `createTestingPinia` 时指定 `stubActions: false`：\n\n```js\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [createTestingPinia({ stubActions: false })],\n  },\n})\n\nconst store = useSomeStore()\n\n// 现在，这个调用将由 store 定义的实现执行。\nstore.someAction()\n\n// ...但它仍然被一个 spy 包装着，所以你可以检查调用\nexpect(store.someAction).toHaveBeenCalledTimes(1)\n```\n\n### 选择性 action 存根 %{#selective-action-stubbing}%\n\n有时你可能只想存根特定的 action，而让其他 action 正常执行。你可以通过向 `stubActions` 选项传递一个 action 名称数组来实现：\n\n```js\n// 只存根 'increment' 和 'reset' action\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        stubActions: ['increment', 'reset'],\n      }),\n    ],\n  },\n})\n\nconst store = useSomeStore()\n\n// 这些 action 将被存根（不执行）\nstore.increment() // 存根\nstore.reset() // 存根\n\n// 其他 action 将正常执行但仍被监听\nstore.fetchData() // 正常执行\nexpect(store.fetchData).toHaveBeenCalledTimes(1)\n```\n\n对于更复杂的场景，你可以传递一个函数，该函数接收 action 名称和 store 实例，并返回是否应该存根该 action：\n\n```js\n// 基于自定义逻辑存根 action\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        stubActions: (actionName, store) => {\n          // 存根所有以 'set' 开头的 action\n          if (actionName.startsWith('set')) return true\n\n          // 根据初始 store 状态存根 action\n          if (store.isPremium) return false\n\n          return true\n        },\n      }),\n    ],\n  },\n})\n\nconst store = useSomeStore()\n\n// 以 'set' 开头的 action 被存根\nstore.setValue(42) // 存根\n\n// 其他 action 可能根据初始 store 状态执行\nstore.fetchData() // 根据初始 store.isPremium 执行或存根\n```\n\n::: tip\n\n- 空数组 `[]` 表示不存根任何 action（与 `false` 相同）\n- 函数在 store 设置时被评估一次，接收处于初始状态的 store 实例\n\n:::\n\n你也可以在创建 store 后手动模拟特定的 action：\n\n```ts\nconst store = useSomeStore()\nvi.spyOn(store, 'increment').mockImplementation(() => {})\n// 或者如果使用带有存根 action 的测试 pinia\nstore.increment.mockImplementation(() => {})\n```\n\n### Mocking the returned value of an action\n\nActions are automatically spied but type-wise, they are still the regular actions. In order to get the correct type, we must implement a custom type-wrapper that applies the `Mock` type to each action. **This type depends on the testing framework you are using**. Here is an example with Vitest:\n\n```ts\nimport type { Mock } from 'vitest'\nimport type { Store, StoreDefinition } from 'pinia'\n\nfunction mockedStore<TStoreDef extends () => unknown>(\n  useStore: TStoreDef\n): TStoreDef extends StoreDefinition<\n  infer Id,\n  infer State,\n  infer Getters,\n  infer Actions\n>\n  ? Store<\n      Id,\n      State,\n      Getters,\n      {\n        [K in keyof Actions]: Actions[K] extends (\n          ...args: infer Args\n        ) => infer ReturnT\n          ? // 👇 depends on your testing framework\n            Mock<Args, ReturnT>\n          : Actions[K]\n      }\n    >\n  : ReturnType<TStoreDef> {\n  return useStore() as any\n}\n```\n\nThis can be used in tests to get a correctly typed store:\n\n```ts\nimport { mockedStore } from './mockedStore'\nimport { useSomeStore } from '@/stores/myStore'\n\nconst store = mockedStore(useSomeStore)\n// typed!\nstore.someAction.mockResolvedValue('some value')\n```\n\n### 指定 createSpy 函数 %{#specifying-the-createspy-function}%\n\n当使用 Jest，或 vitest 且设置 `globals: true` 时，`createTestingPinia` 会自动使用现有测试框架 (`jest.fn` 或 `vitest.fn`) 的 spy 函数存根 (stub) action。如果你使用的是不同的框架，你需要提供一个 [createSpy](/zh/api/interfaces/pinia_testing.TestingOptions.html#createspy) 选项：\n\n```js\nimport sinon from 'sinon'\n\ncreateTestingPinia({\n  createSpy: sinon.spy, // 使用 sinon's spy 包装 action\n})\n```\n\n你可以在[测试包的测试源码](https://github.com/vuejs/pinia/blob/v3/packages/testing/src/testing.spec.ts)中找到更多的例子。\n\n### Mocking getters %{#mocking-getters}%\n\n默认情况下，任何 getter 都会像常规用法一样进行计算，但你可以通过将 getter 设置为任何你想要的值来手动强制计算：\n\n```ts\nimport { defineStore } from 'pinia'\nimport { createTestingPinia } from '@pinia/testing'\n\nconst useCounter = defineStore('counter', {\n  state: () => ({ n: 1 }),\n  getters: {\n    double: (state) => state.n * 2,\n  },\n})\n\nconst pinia = createTestingPinia()\nconst counter = useCounter(pinia)\n\ncounter.double = 3 // 🪄 getter 仅在测试中可被重写\n\n// 设置为 undefined，以重置默认行为\n// @ts-expect-error: usually it's a number\ncounter.double = undefined\ncounter.double // 2 (=1 x 2)\n```\n\n### Pinia 插件 %{#pinia-plugins}%\n\n如果你有使用任何 pinia 插件，确保在调用 `createTestingPinia()` 时传入它们，这样它们就会被正确加载。**不要使用 `testingPinia.use(MyPlugin)`** 来加载它们，而应该像正常的 pinia 那样：\n\n```js\nimport { createTestingPinia } from '@pinia/testing'\nimport { somePlugin } from '../src/stores/plugin'\n\n// 某些测试\nconst wrapper = mount(Counter, {\n  global: {\n    plugins: [\n      createTestingPinia({\n        stubActions: false,\n        plugins: [somePlugin],\n      }),\n    ],\n  },\n})\n```\n\n## 端到端测试 %{#e2e-tests}%\n\n对于 pinia，你不需要为端到端测试修改任何代码，这就是端到端测试的含义！也许你想测试 HTTP 请求，但这已经超出了本指南的范围😄。\n\n## 对组件单元测试(Vue 2) %{#unit-test-components-vue-2}%\n\n当你使用的是 [Vue Test Utils 1](https://v1.test-utils.vuejs.org/zh/) 时，请将 Pinia 安装在 `localVue` 上：\n\n```js\nimport { PiniaVuePlugin } from 'pinia'\nimport { createLocalVue, mount } from '@vue/test-utils'\nimport { createTestingPinia } from '@pinia/testing'\n\nconst localVue = createLocalVue()\nlocalVue.use(PiniaVuePlugin)\n\nconst wrapper = mount(Counter, {\n  localVue,\n  pinia: createTestingPinia(),\n})\n\nconst store = useSomeStore() // 使用 pinia 的测试实例！\n```\n"
  },
  {
    "path": "packages/docs/zh/cookbook/vscode-snippets.md",
    "content": "# VS Code 代码片段\n\n<RuleKitLink />\n\n有一些代码片段可以让你在 VS Code 中更轻松地使用 Pinia。\n\n通过 <kbd>⇧</kbd> <kbd>⌘</kbd> <kbd>P</kbd> / <kbd>⇧</kbd> <kbd>⌃</kbd> <kbd>P</kbd> 然后输入 `Snippets: Configure User Snippets` 就可以管理用户代码片段。\n\n```json\n{\n  \"Pinia Options Store Boilerplate\": {\n    \"scope\": \"javascript,typescript\",\n    \"prefix\": \"pinia-options\",\n    \"body\": [\n      \"import { defineStore, acceptHMRUpdate } from 'pinia'\",\n      \"\",\n      \"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', {\",\n      \" state: () => ({\",\n      \"   $0\",\n      \" }),\",\n      \" getters: {},\",\n      \" actions: {},\",\n      \"})\",\n      \"\",\n      \"if (import.meta.hot) {\",\n      \" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))\",\n      \"}\",\n      \"\"\n    ],\n    \"description\": \"Bootstrap the code needed for a Vue.js Pinia Options Store file\"\n  },\n  \"Pinia Setup Store Boilerplate\": {\n    \"scope\": \"javascript,typescript\",\n    \"prefix\": \"pinia-setup\",\n    \"body\": [\n      \"import { defineStore, acceptHMRUpdate } from 'pinia'\",\n      \"\",\n      \"export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', () => {\",\n      \" $0\",\n      \" return {}\",\n      \"})\",\n      \"\",\n      \"if (import.meta.hot) {\",\n      \" import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))\",\n      \"}\",\n      \"\"\n    ],\n    \"description\": \"Bootstrap the code needed for a Vue.js Pinia Setup Store file\"\n  }\n}\n```\n"
  },
  {
    "path": "packages/docs/zh/core-concepts/actions.md",
    "content": "# Action %{#actions}%\n\n<RuleKitLink />\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/synchronous-and-asynchronous-actions-in-pinia\"\n  title=\"Learn all about actions in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/the-3-pillars-of-pinia-actions\"\n  title=\"Learn all about actions in Pinia\"\n/>\n\nAction 相当于组件中的 [method](https://cn.vuejs.org/api/options-state.html#methods)。它们可以通过 `defineStore()` 中的 `actions` 属性来定义，**并且它们也是定义业务逻辑的完美选择。**\n\n```js\nexport const useCounterStore = defineStore('main', {\n  state: () => ({\n    count: 0,\n  }),\n  actions: {\n    increment() {\n      this.count++\n    },\n    randomizeCounter() {\n      this.count = Math.round(100 * Math.random())\n    },\n  },\n})\n```\n\n类似 [getter](./getters.md)，action 也可通过 `this` 访问**整个 store 实例**，并支持**完整的类型标注(以及自动补全✨)**。**不同的是，`action` 可以是异步的**，你可以在它们里面 `await` 调用任何 API，以及其他 action！下面是一个使用 [Mande](https://github.com/posva/mande) 的例子。请注意，你使用什么库并不重要，只要你得到的是一个`Promise`。你甚至可以 (在浏览器中) 使用原生 `fetch` 函数：\n\n```js\nimport { mande } from 'mande'\n\nconst api = mande('/api/users')\n\nexport const useUsers = defineStore('users', {\n  state: () => ({\n    userData: null,\n    // ...\n  }),\n\n  actions: {\n    async registerUser(login, password) {\n      try {\n        this.userData = await api.post({ login, password })\n        showTooltip(`Welcome back ${this.userData.name}!`)\n      } catch (error) {\n        showTooltip(error)\n        // 让表单组件显示错误\n        return error\n      }\n    },\n  },\n})\n```\n\n你也完全可以自由地设置任何你想要的参数以及返回任何结果。当调用 action 时，一切类型也都是可以被自动推断出来的。\n\nAction 可以像函数或者通常意义上的方法一样被调用：\n\n```vue\n<script setup>\nconst store = useCounterStore()\n// 将 action 作为 store 的方法进行调用\nstore.randomizeCounter()\n</script>\n<template>\n  <!-- 即使在模板中也可以 -->\n  <button @click=\"store.randomizeCounter()\">Randomize</button>\n</template>\n```\n\n## 访问其他 store 的 action %{#accessing-other-stores-actions}%\n\n想要使用另一个 store 的话，那你直接在 _action_ 中调用就好了：\n\n```js\nimport { useAuthStore } from './auth-store'\n\nexport const useSettingsStore = defineStore('settings', {\n  state: () => ({\n    preferences: null,\n    // ...\n  }),\n  actions: {\n    async fetchUserPreferences() {\n      const auth = useAuthStore()\n      if (auth.isAuthenticated) {\n        this.preferences = await fetchPreferences()\n      } else {\n        throw new Error('User must be authenticated')\n      }\n    },\n  },\n})\n```\n\n## 使用选项式 API 的用法 %{#usage-with-the-options-api}%\n\n<VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-pinia-actions-in-the-options-api\"\n  title=\"Access Pinia Getters via the Options API\"\n/>\n\n在下面的例子中，你可以假设相关的 store 已经创建了：\n\n```js\n// 示例文件路径：\n// ./src/stores/counter.js\n\nimport { defineStore } from 'pinia'\n\nconst useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n```\n\n### 使用 `setup()` %{#with-setup}%\n\n虽然并不是每个开发者都会使用组合式 API，但 `setup()` 钩子依旧可以使 Pinia 在选项式 API 中更易用。并且不需要额外的映射辅助函数!\n\n```vue\n<script>\nimport { useCounterStore } from '../stores/counter'\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore()\n    return { counterStore }\n  },\n  methods: {\n    incrementAndPrint() {\n      this.counterStore.increment()\n      console.log('New Count:', this.counterStore.count)\n    },\n  },\n})\n</script>\n```\n\n### 不使用 `setup()` %{#without-setup}%\n\n如果你不喜欢使用组合式 API，你也可以使用 `mapActions()` 辅助函数将 action 属性映射为你组件中的方法。\n\n```js\nimport { mapActions } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  methods: {\n    // 访问组件内的 this.increment()\n    // 与从 store.increment() 调用相同\n    ...mapActions(useCounterStore, ['increment'])\n    // 与上述相同，但将其注册为this.myOwnName()\n    ...mapActions(useCounterStore, { myOwnName: 'increment' }),\n  },\n}\n```\n\n## 订阅 action %{#subscribing-to-actions}%\n\n你可以通过 `store.$onAction()` 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。`after` 表示在 promise 解决之后，允许你在 action 解决后执行一个回调函数。同样地，`onError` 允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用，类似于[Vue docs 中的这个提示](https://cn.vuejs.org/guide/best-practices/production-deployment#tracking-runtime-errors)。\n\n这里有一个例子，在运行 action 之前以及 action resolve/reject 之后打印日志记录。\n\n```js\nconst unsubscribe = someStore.$onAction(\n  ({\n    name, // action 名称\n    store, // store 实例，类似 `someStore`\n    args, // 传递给 action 的参数数组\n    after, // 在 action 返回或解决后的钩子\n    onError, // action 抛出或拒绝的钩子\n  }) => {\n    // 为这个特定的 action 调用提供一个共享变量\n    const startTime = Date.now()\n    // 这将在执行 \"store \"的 action 之前触发。\n    console.log(`Start \"${name}\" with params [${args.join(', ')}].`)\n\n    // 这将在 action 成功并完全运行后触发。\n    // 它等待着任何返回的 promise\n    after((result) => {\n      console.log(\n        `Finished \"${name}\" after ${\n          Date.now() - startTime\n        }ms.\\nResult: ${result}.`\n      )\n    })\n\n    // 如果 action 抛出或返回一个拒绝的 promise，这将触发\n    onError((error) => {\n      console.warn(\n        `Failed \"${name}\" after ${Date.now() - startTime}ms.\\nError: ${error}.`\n      )\n    })\n  }\n)\n\n// 手动删除监听器\nunsubscribe()\n```\n\n默认情况下，*action 订阅器*会被绑定到添加它们的组件上(如果 store 在组件的 `setup()` 内)。这意味着，当该组件被卸载时，它们将被自动删除。如果你想在组件卸载后依旧保留它们，请将 `true` 作为第二个参数传递给 _action 订阅器_，以便将其从当前组件中分离：\n\n```vue\n<script setup>\nconst someStore = useSomeStore()\n// 此订阅器即便在组件卸载之后仍会被保留\nsomeStore.$onAction(callback, true)\n</script>\n```\n"
  },
  {
    "path": "packages/docs/zh/core-concepts/getters.md",
    "content": "# Getter %{#getters}%\n\n<RuleKitLink />\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/getters-in-pinia\"\n  title=\"Learn all about getters in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/the-3-pillars-of-pinia-getters\"\n  title=\"Learn all about getters in Pinia\"\n/>\n\nGetter 完全等同于 store 的 state 的[计算值](https://cn.vuejs.org/guide/essentials/computed.html)。可以通过 `defineStore()` 中的 `getters` 属性来定义它们。**推荐**使用箭头函数，并且它将接收 `state` 作为第一个参数：\n\n```js\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    doubleCount: (state) => state.count * 2,\n  },\n})\n```\n\n大多数时候，getter 仅依赖 state。不过，有时它们也可能会使用其他 getter。因此，即使在使用常规函数定义 getter 时，我们也可以通过 `this` 访问到**整个 store 实例**，**但(在 TypeScript 中)必须定义返回类型**。这是为了避免 TypeScript 的已知缺陷，**不过这不影响用箭头函数定义的 getter，也不会影响不使用 `this` 的 getter**。\n\n```ts\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    // 自动推断出返回类型是一个 number\n    doubleCount(state) {\n      return state.count * 2\n    },\n    // 返回类型**必须**明确设置\n    doublePlusOne(): number {\n      // 整个 store 的 自动补全和类型标注 ✨\n      return this.doubleCount + 1\n    },\n  },\n})\n```\n\n然后你可以直接访问 store 实例上的 getter 了：\n\n```vue\n<script setup>\nimport { useCounterStore } from './counterStore'\n\nconst store = useCounterStore()\n</script>\n\n<template>\n  <p>Double count is {{ store.doubleCount }}</p>\n</template>\n```\n\n## 访问其他 getter %{#accessing-other-getters}%\n\n与计算属性一样，你也可以组合多个 getter。通过 `this`，你可以访问到其他任何 getter。在这种情况下，**你需要为这个 getter 指定一个返回值的类型**。\n\n::: code-group\n\n```ts [counterStore.ts]\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    doubleCount(state) {\n      return state.count * 2\n    },\n    doubleCountPlusOne(): number {\n      return this.doubleCount + 1\n    },\n  },\n})\n```\n\n```js [counterStore.js]\n// 你可以在 JavaScript 中使用 JSDoc (https://jsdoc.app/tags-returns.html)\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    // 类型是自动推断出来的，因为我们没有使用 `this`\n    doubleCount: (state) => state.count * 2,\n    // 这里我们需要自己添加类型(在 JS 中使用 JSDoc)\n    // 可以用 this 来引用 getter\n    /**\n     * 返回 count 的值乘以 2 加 1\n     *\n     * @returns {number}\n     */\n    doubleCountPlusOne() {\n      // 自动补全 ✨\n      return this.doubleCount + 1\n    },\n  },\n})\n```\n\n:::\n\n## 向 getter 传递参数 %{#passing-arguments-to-getters}%\n\n_Getter_ 只是幕后的**计算**属性，所以不可以向它们传递任何参数。不过，你可以从 _getter_ 返回一个函数，该函数可以接受任意参数：\n\n```js\nexport const useUserListStore = defineStore('userList', {\n  getters: {\n    getUserById: (state) => {\n      return (userId) => state.users.find((user) => user.id === userId)\n    },\n  },\n})\n```\n\n并在组件中使用：\n\n```vue\n<script setup>\nimport { useUserListStore } from './store'\nconst userList = useUserListStore()\nconst { getUserById } = storeToRefs(userList)\n// 请注意，你需要使用 `getUserById.value` 来访问\n// <script setup> 中的函数\n</script>\n\n<template>\n  <p>User 2: {{ getUserById(2) }}</p>\n</template>\n```\n\n请注意，当你这样做时，**getter 将不再被缓存**。它们只是一个被你调用的函数。不过，你可以在 getter 本身中缓存一些结果，虽然这种做法并不常见，但有证明表明它的性能会更好：\n\n```js\nexport const useUserListStore = defineStore('userList', {\n  getters: {\n    getActiveUserById(state) {\n      const activeUsers = state.users.filter((user) => user.active)\n      return (userId) => activeUsers.find((user) => user.id === userId)\n    },\n  },\n})\n```\n\n## 访问其他 store 的 getter %{#accessing-other-stores-getters}%\n\n想要使用另一个 store 的 getter 的话，那就直接在 _getter_ 内使用就好：\n\n```js\nimport { useOtherStore } from './other-store'\n\nexport const useStore = defineStore('main', {\n  state: () => ({\n    // ...\n  }),\n  getters: {\n    otherGetter(state) {\n      const otherStore = useOtherStore()\n      return state.localData + otherStore.data\n    },\n  },\n})\n```\n\n## 使用 `setup()` 时的用法 %{#usage-with-setup}%\n\n作为 store 的一个属性，你可以直接访问任何 getter(与 state 属性完全一样)：\n\n```vue\n<script setup>\nconst store = useCounterStore()\nstore.count = 3\nstore.doubleCount // 6\n</script>\n```\n\n## 使用选项式 API 的用法 %{#usage-with-the-options-api}%\n\n<VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-pinia-getters-in-the-options-api\"\n  title=\"Access Pinia Getters via the Options API\"\n/>\n\n在下面的例子中，你可以假设相关的 store 已经创建了：\n\n```js\n// 示例文件路径：\n// ./src/stores/counter.js\n\nimport { defineStore } from 'pinia'\n\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n  getters: {\n    doubleCount(state) {\n      return state.count * 2\n    },\n  },\n})\n```\n\n### 使用 `setup()` %{#with-setup}%\n\n虽然并不是每个开发者都会使用组合式 API，但 `setup()` 钩子依旧可以使 Pinia 在选项式 API 中更易用。并且不需要额外的映射辅助函数！\n\n```vue\n<script>\nimport { useCounterStore } from '../stores/counter'\n\nexport default defineComponent({\n  setup() {\n    const counterStore = useCounterStore()\n\n    return { counterStore }\n  },\n  computed: {\n    quadrupleCounter() {\n      return this.counterStore.doubleCount * 2\n    },\n  },\n})\n</script>\n```\n\n这在将组件从选项式 API 迁移到组合式 API 时很有用，但**应该只是一个迁移步骤**。始终尽量不要在同一组件中混合两种 API 样式。\n\n### 不使用 `setup()` %{#without-setup}%\n\n你可以使用[前一节的 state](./state.md#options-api) 中的 `mapState()` 函数来将其映射为 getters：\n\n```js\nimport { mapState } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  computed: {\n    // 允许在组件中访问 this.doubleCount\n    // 与从 store.doubleCount 中读取的相同\n    ...mapState(useCounterStore, ['doubleCount']),\n    // 与上述相同，但将其注册为 this.myOwnName\n    ...mapState(useCounterStore, {\n      myOwnName: 'doubleCount',\n      // 你也可以写一个函数来获得对 store 的访问权\n      double: (store) => store.doubleCount,\n    }),\n  },\n}\n```\n"
  },
  {
    "path": "packages/docs/zh/core-concepts/index.md",
    "content": "# 定义 Store %{#defining-a-store}%\n\n<RuleKitLink />\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/define-your-first-pinia-store\"\n  title=\"Learn how to define and use stores in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/651ecff2e4c322668b0a17af\"\n  mp-link=\"https://masteringpinia.com/lessons/quick-start-with-pinia\"\n  title=\"Get started with Pinia\"\n/>\n\n在深入研究核心概念之前，我们得知道 Store 是用 `defineStore()` 定义的，它的第一个参数要求是一个**独一无二的**名字：\n\n```js\nimport { defineStore } from 'pinia'\n\n//  `defineStore()` 的返回值的命名是自由的\n// 但最好含有 store 的名字，且以 `use` 开头，以 `Store` 结尾。\n// (比如 `useUserStore`，`useCartStore`，`useProductStore`)\n// 第一个参数是你的应用中 Store 的唯一 ID。\nexport const useAlertsStore = defineStore('alerts', {\n  // 其他配置...\n})\n```\n\n这个**名字** ，也被用作 _id_ ，是必须传入的， Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法，将返回的函数命名为 _use..._ 是一个符合组合式函数风格的约定。\n\n`defineStore()` 的第二个参数可接受两类值：Setup 函数或 Option 对象。\n\n## Option Store %{#option-stores}%\n\n与 Vue 的选项式 API 类似，我们也可以传入一个带有 `state`、`actions` 与 `getters` 属性的 Option 对象\n\n```js {2-10}\nexport const useCounterStore = defineStore('counter', {\n  state: () => ({ count: 0, name: 'Eduardo' }),\n  getters: {\n    doubleCount: (state) => state.count * 2,\n  },\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n```\n\n你可以认为 `state` 是 store 的数据 (`data`)，`getters` 是 store 的计算属性 (`computed`)，而 `actions` 则是方法 (`methods`)。\n\n为方便上手使用，Option Store 应尽可能直观简单。\n\n## Setup Store %{#setup-stores}%\n\n也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 [setup 函数](https://cn.vuejs.org/api/composition-api-setup.html) 相似，我们可以传入一个函数，该函数定义了一些响应式属性和方法，并且返回一个带有我们想暴露出去的属性和方法的对象。\n\n```js\nexport const useCounterStore = defineStore('counter', () => {\n  const count = ref(0)\n  const name = ref('Eduardo')\n  const doubleCount = computed(() => count.value * 2)\n  function increment() {\n    count.value++\n  }\n\n  return { count, name, doubleCount, increment }\n})\n```\n\n在 _Setup Store_ 中：\n\n- `ref()` 就是 `state` 属性\n- `computed()` 就是 `getters`\n- `function()` 就是 `actions`\n\n注意，要让 pinia 正确识别 `state`，你**必须**在 setup store 中返回 **`state` 的所有属性**。这意味着，你不能在 store 中使用**私有**属性。不完整返回会影响 [SSR](../cookbook/composables.md) ，开发工具和其他插件的正常运行。\n\nSetup store 比 [Option Store](#option-stores) 带来了更多的灵活性，因为你可以在一个 store 内创建侦听器，并自由地使用任何[组合式函数](https://cn.vuejs.org/guide/reusability/composables.html#composables)。不过，请记住，使用组合式函数会让 SSR 变得更加复杂。\n\nSetup store 也可以依赖于全局**提供**的属性，比如路由。任何[应用层面提供](https://vuejs.org/api/application.html#app-provide)的属性都可以在 store 中使用 `inject()` 访问，就像在组件中一样：\n\n```ts\nimport { inject } from 'vue'\nimport { useRoute } from 'vue-router'\nimport { defineStore } from 'pinia'\n\nexport const useSearchFilters = defineStore('search-filters', () => {\n  const route = useRoute()\n  // 这里假定 `app.provide('appProvided', 'value')` 已经调用过\n  const appProvided = inject('appProvided')\n\n  // ...\n\n  return {\n    // ...\n  }\n})\n```\n\n:::warning\n不要返回像 `route` 或 `appProvided` (上例中)之类的属性，因为它们不属于 store，而且你可以在组件中直接用 `useRoute()` 和 `inject('appProvided')` 访问。\n:::\n\n## 你应该选用哪种语法？ %{#what-syntax-should-i-pick}%\n\n和[在 Vue 中如何选择组合式 API 与选项式 API](https://cn.vuejs.org/guide/introduction.html#which-to-choose) 一样，选择你觉得最舒服的那一个就好。两种语法都有各自的优势和劣势。Option Store 更容易使用，而 Setup Store 更灵活和强大。如果你想深入了解两者之间的区别，请查看 Mastering Pinia 中的 [Option Stores vs Setup Stores 章节](https://masteringpinia.com/lessons/when-to-choose-one-syntax-over-the-other)。\n\n## 使用 Store %{#using-the-store}%\n\n虽然我们前面定义了一个 store，但在我们使用 `<script setup>` 调用 `useStore()`(或者使用 `setup()` 函数，**像所有的组件那样**) 之前，store 实例是不会被创建的：\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\n// 在组件内部的任何地方均可以访问变量 `store` ✨\nconst store = useCounterStore()\n</script>\n```\n\n:::tip\n如果你还不会使用 `setup` 组件，[你也可以通过**映射辅助函数**来使用 Pinia](../cookbook/options-api.md)。\n:::\n\n你可以定义任意多的 store，但为了让使用 pinia 的益处最大化(比如允许构建工具自动进行代码分割以及 TypeScript 推断)，**你应该在不同的文件中去定义 store**。\n\n一旦 store 被实例化，你可以直接访问在 store 的 `state`、`getters` 和 `actions` 中定义的任何属性。我们将在后续章节继续了解这些细节，目前自动补全将帮助你使用相关属性。\n\n请注意，`store` 是一个用 `reactive` 包装的对象，这意味着不需要在 getters 后面写 `.value`。就像 `setup` 中的 `props` 一样，**我们不能对它进行解构**：\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\nimport { computed } from 'vue'\n\nconst store = useCounterStore()\n// ❌ 下面这部分代码不会生效，因为它的响应式被破坏了\n// 与 reactive 相同: https://vuejs.org/guide/essentials/reactivity-fundamentals.html#limitations-of-reactive\nconst { name, doubleCount } = store // [!code warning]\nname // 将会一直是 \"Eduardo\" // [!code warning]\ndoubleCount // 将会一直是 0 // [!code warning]\nsetTimeout(() => {\n  store.increment()\n}, 1000)\n// ✅ 而这一部分代码就会维持响应式\n// 💡 在这里你也可以直接使用 `store.doubleCount`\nconst doubleValue = computed(() => store.doubleCount)\n</script>\n```\n\n## 从 Store 解构\n\n为了从 store 中提取属性时保持其响应性，你需要使用 `storeToRefs()`。它将为每一个响应式属性创建引用。当你只使用 store 的状态而不调用任何 action 时，它会非常有用。请注意，你可以直接从 store 中解构 action，因为它们也被绑定到 store 上：\n\n```vue\n<script setup>\nimport { storeToRefs } from 'pinia'\nconst store = useCounterStore()\n// `name` 和 `doubleCount` 都是响应式引用\n// 下面的代码同样会提取那些来自插件的属性的响应式引用\n// 但是会跳过所有的 action 或者非响应式（非 ref 或者 非 reactive）的属性\nconst { name, doubleCount } = storeToRefs(store)\n// 名为 increment 的 action 可以被解构\nconst { increment } = store\n</script>\n```\n"
  },
  {
    "path": "packages/docs/zh/core-concepts/outside-component-usage.md",
    "content": "# 在组件外使用 store %{#using-a-store-outside-of-a-component}%\n\n<RuleKitLink />\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/651ed1ec4c2f339c6860fd06\"\n  mp-link=\"https://masteringpinia.com/lessons/how-does-usestore-work\"\n  title=\"Using stores outside of components\"\n/>\n\nPinia store 依靠 `pinia` 实例在所有调用中共享同一个 store 实例。大多数时候，只需调用你定义的 `useStore()` 函数，完全开箱即用。例如，在 `setup()` 中，你不需要再做任何事情。但在组件之外，情况就有点不同了。\n实际上，`useStore()` 给你的 `app` 自动注入了 `pinia` 实例。这意味着，如果 `pinia` 实例不能自动注入，你必须手动提供给 `useStore()` 函数。\n你可以根据不同的应用，以不同的方式解决这个问题。\n\n## 单页面应用 %{#single-page-applications}%\n\n如果你不做任何 SSR(服务器端渲染)，在用 `app.use(pinia)` 安装 pinia 插件后，对 `useStore()` 的任何调用都会正常执行：\n\n```js\nimport { useUserStore } from '@/stores/user'\nimport { createPinia } from 'pinia'\nimport { createApp } from 'vue'\nimport App from './App.vue'\n\n// ❌  失败，因为它是在创建 pinia 之前被调用的\nconst userStore = useUserStore()\n\nconst pinia = createPinia()\nconst app = createApp(App)\napp.use(pinia)\n\n// ✅ 成功，因为 pinia 实例现在激活了\nconst userStore = useUserStore()\n```\n\n为确保 pinia 实例被激活，最简单的方法就是将 `useStore()` 的调用放在 pinia 安装后才会执行的函数中。\n\n让我们来看看这个在 Vue Router 的导航守卫中使用 store 的例子。\n\n```js\nimport { createRouter } from 'vue-router'\nconst router = createRouter({\n  // ...\n})\n\n// ❌ 由于引入顺序的问题，这将失败\nconst store = useStore()\n\nrouter.beforeEach((to, from, next) => {\n  // 我们想要在这里使用 store\n  if (store.isLoggedIn) next()\n  else next('/login')\n})\n\nrouter.beforeEach((to) => {\n  // ✅ 这样做是可行的，因为路由器是在其被安装之后开始导航的，\n  // 而此时 Pinia 也已经被安装。\n  const store = useStore()\n\n  if (to.meta.requiresAuth && !store.isLoggedIn) return '/login'\n})\n```\n\n## 服务端渲染应用 %{#ssr-apps}%\n\n当处理服务端渲染时，你将必须把 `pinia` 实例传递给 `useStore()`。这可以防止 pinia 在不同的应用实例之间共享全局状态。\n\n在[SSR 指南](../ssr/index.md)中有一整节专门讨论这个问题，这里只是一个简短的解释。\n"
  },
  {
    "path": "packages/docs/zh/core-concepts/plugins.md",
    "content": "# 插件 %{#plugins}%\n\n<RuleKitLink />\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/What-is-a-pinia-plugin\"\n  title=\"Learn all about Pinia plugins\"\n/>\n\n由于有了底层 API 的支持，Pinia store 现在完全支持扩展。以下是你可以扩展的内容：\n\n- 为 store 添加新的属性\n- 定义 store 时增加新的选项\n- 为 store 增加新的方法\n- 包装现有的方法\n- 改变甚至取消 action\n- 实现副作用，如[本地存储](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)\n- **仅**应用插件于特定 store\n\n插件是通过 `pinia.use()` 添加到 pinia 实例的。最简单的例子是通过返回一个对象将一个静态属性添加到所有 store。\n\n```js\nimport { createPinia } from 'pinia'\n\n// 创建的每个 store 中都会添加一个名为 `secret` 的属性。\n// 在安装此插件后，插件可以保存在不同的文件中\nfunction SecretPiniaPlugin() {\n  return { secret: 'the cake is a lie' }\n}\n\nconst pinia = createPinia()\n// 将该插件交给 Pinia\npinia.use(SecretPiniaPlugin)\n\n// 在另一个文件中\nconst store = useStore()\nstore.secret // 'the cake is a lie'\n```\n\n这对添加全局对象很有用，如路由器、modal 或 toast 管理器。\n\n## 简介 %{#introduction}%\n\nPinia 插件是一个函数，可以选择性地返回要添加到 store 的属性。它接收一个可选参数，即 _context_。\n\n```js\nexport function myPiniaPlugin(context) {\n  context.pinia // 用 `createPinia()` 创建的 pinia。\n  context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。\n  context.store // 该插件想扩展的 store\n  context.options // 定义传给 `defineStore()` 的 store 的可选对象。\n  // ...\n}\n```\n\n然后用 `pinia.use()` 将这个函数传给 `pinia`：\n\n```js\npinia.use(myPiniaPlugin)\n```\n\n插件只会应用于**在 `pinia` 传递给应用后**创建的 store，否则它们不会生效。\n\n## 扩展 Store %{#augmenting-a-store}%\n\n你可以直接通过在一个插件中返回包含特定属性的对象来为每个 store 都添加上特定属性：\n\n```js\npinia.use(() => ({ hello: 'world' }))\n```\n\n你也可以直接在 `store` 上设置该属性，但**可以的话，请使用返回对象的方法，这样它们就能被 devtools 自动追踪到**：\n\n```js\npinia.use(({ store }) => {\n  store.hello = 'world'\n})\n```\n\n任何由插件*返回的*属性都会被 devtools 自动追踪，所以如果你想在 devtools 中调试 `hello` 属性，为了使 devtools 能追踪到 `hello`，请确保**在 dev 模式下**将其添加到 `store._customProperties` 中：\n\n```js\n// 上文示例\npinia.use(({ store }) => {\n  store.hello = 'world'\n  // 确保你的构建工具能处理这个问题，webpack 和 vite 在默认情况下应该能处理。\n  if (process.env.NODE_ENV === 'development') {\n    // 添加你在 store 中设置的键值\n    store._customProperties.add('hello')\n  }\n})\n```\n\n值得注意的是，每个 store 都被 [`reactive`](https://cn.vuejs.org/api/reactivity-core.html#reactive)包装过，所以可以自动解包任何它所包含的 Ref(`ref()`、`computed()`...)。\n\n```js\nconst sharedRef = ref('shared')\npinia.use(({ store }) => {\n  // 每个 store 都有单独的 `hello` 属性\n  store.hello = ref('secret')\n  // 它会被自动解包\n  store.hello // 'secret'\n\n  // 所有的 store 都在共享 `shared` 属性的值\n  store.shared = sharedRef\n  store.shared // 'shared'\n})\n```\n\n这就是在没有 `.value` 的情况下你依旧可以访问所有计算属性的原因，也是它们为什么是响应式的原因。\n\n### 添加新的 state %{#adding-new-state}%\n\n如果你想给 store 添加新的 state 属性或者在服务端渲染的激活过程中使用的属性，**你必须同时在两个地方添加它**。。\n\n- 在 `store` 上，然后你才可以用 `store.myState` 访问它。\n- 在 `store.$state` 上，然后你才可以在 devtools 中使用它，并且，**在 SSR 时被正确序列化(serialized)**。\n\n除此之外，你肯定也会使用 `ref()`(或其他响应式 API)，以便在不同的读取中共享相同的值：\n\n```js\nimport { toRef, ref } from 'vue'\n\npinia.use(({ store }) => {\n  // 为了正确地处理 SSR，我们需要确保我们没有重写任何一个\n  // 现有的值\n  if (!Object.hasOwn(store.$state, 'hasError')) {\n    // 在插件中定义 hasError，因此每个 store 都有各自的\n    // hasError 状态\n    const hasError = ref(false)\n    // 在 `$state` 上设置变量，允许它在 SSR 期间被序列化。\n    store.$state.hasError = hasError\n  }\n  // 我们需要将 ref 从 state 转移到 store\n  // 这样的话,两种方式：store.hasError 和 store.$state.hasError 都可以访问\n  // 并且共享的是同一个变量\n  // 查看 https://cn.vuejs.org/api/reactivity-utilities.html#toref\n  store.hasError = toRef(store.$state, 'hasError')\n\n  // 在这种情况下，最好不要返回 `hasError`\n  // 因为它将被显示在 devtools 的 `state` 部分\n  // 如果我们返回它，devtools 将显示两次。\n})\n```\n\n需要注意的是，在一个插件中， state 变更或添加(包括调用 `store.$patch()`)都是发生在 store 被激活之前，**因此不会触发任何订阅函数**。\n\n:::warning\n如果你使用的是 **Vue 2**，Pinia 与 Vue 一样，受限于[相同的响应式限制](https://v2.cn.vuejs.org/v2/guide/reactivity.html#检测变化的注意事项)。在创建新的 state 属性时,如 `secret` 和 `hasError`，你需要使用 `Vue.set()` (Vue 2.7) 或者 `@vue/composition-api` 的 `set()` (Vue < 2.7)。\n\n```js\nimport { set, toRef } from '@vue/composition-api'\npinia.use(({ store }) => {\n  if (!Object.hasOwn(store.$state, 'secret')) {\n    const secretRef = ref('secret')\n    // 如果这些数据是要在 SSR 过程中使用的\n    // 你应该将其设置在 `$state' 属性上\n    // 这样它就会被序列化并在激活过程中被接收\n    set(store.$state, 'secret', secretRef)\n    // 直接在 store 里设置，这样你就可以访问它了。\n    // 两种方式都可以：`store.$state.secret` / `store.secret`。\n    set(store, 'secret', secretRef)\n    store.secret // 'secret'\n  }\n})\n```\n\n:::\n\n#### 重置插件中添加的 state\n\n默认情况下，`$reset()` 不会重置插件添加的 state，但你可以重写它来重置你添加的 state：\n\n```js\nimport { toRef, ref } from 'vue'\n\npinia.use(({ store }) => {\n  // 和上面的代码一样，只是为了参考\n  if (!Object.hasOwn(store.$state, 'hasError')) {\n    const hasError = ref(false)\n    store.$state.hasError = hasError\n  }\n  store.hasError = toRef(store.$state, 'hasError')\n\n  // 确认将上下文 (`this`) 设置为 store\n  const originalReset = store.$reset.bind(store)\n\n  // 覆写其 $reset 函数\n  return {\n    $reset() {\n      originalReset()\n      store.hasError = false\n    },\n  }\n})\n```\n\n## 添加新的外部属性 %{#adding-new-external-properties}%\n\n当添加外部属性、第三方库的类实例或非响应式的简单值时，你应该先用 `markRaw()` 来包装一下它，再将它传给 pinia。下面是一个在每个 store 中添加路由器的例子：\n\n```js\nimport { markRaw } from 'vue'\n// 根据你的路由器的位置来调整\nimport { router } from './router'\n\npinia.use(({ store }) => {\n  store.router = markRaw(router)\n})\n```\n\n## 在插件中调用 `$subscribe` %{#calling-subscribe-inside-plugins}%\n\n你也可以在插件中使用 [store.$subscribe](./state.md#subscribing-to-the-state) 和 [store.$onAction](./actions.md#subscribing-to-actions) 。\n\n```ts\npinia.use(({ store }) => {\n  store.$subscribe(() => {\n    // 响应 store 变化\n  })\n  store.$onAction(() => {\n    // 响应 store actions\n  })\n})\n```\n\n## 添加新的选项 %{#adding-new-options}%\n\n在定义 store 时，可以创建新的选项，以便在插件中使用它们。例如，你可以创建一个 `debounce` 选项，允许你让任何 action 实现防抖。\n\n```js\ndefineStore('search', {\n  actions: {\n    searchContacts() {\n      // ...\n    },\n  },\n\n  // 这将在后面被一个插件读取\n  debounce: {\n    // 让 action searchContacts 防抖 300ms\n    searchContacts: 300,\n  },\n})\n```\n\n然后，该插件可以读取该选项来包装 action，并替换原始 action：\n\n```js\n// 使用任意防抖库\nimport debounce from 'lodash/debounce'\n\npinia.use(({ options, store }) => {\n  if (options.debounce) {\n    // 我们正在用新的 action 来覆盖这些 action\n    return Object.keys(options.debounce).reduce((debouncedActions, action) => {\n      debouncedActions[action] = debounce(\n        store[action],\n        options.debounce[action]\n      )\n      return debouncedActions\n    }, {})\n  }\n})\n```\n\n注意，在使用 setup 语法时，自定义选项作为第 3 个参数传递：\n\n```js\ndefineStore(\n  'search',\n  () => {\n    // ...\n  },\n  {\n    // 这将在后面被一个插件读取\n    debounce: {\n      // 让 action searchContacts 防抖 300ms\n      searchContacts: 300,\n    },\n  }\n)\n```\n\n## TypeScript\n\n上述一切功能都有类型支持，所以你永远不需要使用 `any` 或 `@ts-ignore`。\n\n### 标注插件类型 %{#typing-plugins}%\n\n一个 Pinia 插件可按如下方式实现类型标注：\n\n```ts\nimport { PiniaPluginContext } from 'pinia'\n\nexport function myPiniaPlugin(context: PiniaPluginContext) {\n  // ...\n}\n```\n\n### 为新的 store 属性添加类型 %{#typing-new-store-properties}%\n\n当在 store 中添加新的属性时，你也应该扩展 `PiniaCustomProperties` 接口。\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface PiniaCustomProperties {\n    // 通过使用一个 setter，我们可以允许字符串和引用。\n    set hello(value: string | Ref<string>)\n    get hello(): string\n\n    // 你也可以定义更简单的值\n    simpleNumber: number\n\n    // 添加路由(#adding-new-external-properties)\n    router: Router\n  }\n}\n```\n\n然后，就可以安全地写入和读取它了：\n\n```ts\npinia.use(({ store }) => {\n  store.hello = 'Hola'\n  store.hello = ref('Hola')\n\n  store.simpleNumber = Math.random()\n  // @ts-expect-error: we haven't typed this correctly\n  store.simpleNumber = ref(Math.random())\n})\n```\n\n`PiniaCustomProperties` 是一个通用类型，允许你引用 store 的属性。思考一下这个例子，如果把初始选项复制成 `$options`(这只对 option store 有效)，如何标注类型：\n\n```ts\npinia.use(({ options }) => ({ $options: options }))\n```\n\n我们可以通过使用 `PiniaCustomProperties` 的4种通用类型来标注类型：\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface PiniaCustomProperties<Id, S, G, A> {\n    $options: {\n      id: Id\n      state?: () => S\n      getters?: G\n      actions?: A\n    }\n  }\n}\n```\n\n:::tip\n当在泛型中扩展类型时，它们的名字必须**与源代码中完全一样**。`Id` 不能被命名为 `id` 或 `I` ，`S` 不能被命名为 `State`。下面是每个字母代表的含义：\n\n- S: State\n- G: Getters\n- A: Actions\n- SS: Setup Store / Store\n\n:::\n\n### 为新的 state 添加类型 %{#typing-new-state}%\n\n当添加新的 state 属性(包括 `store` 和 `store.$state` )时，你需要将类型添加到 `PiniaCustomStateProperties` 中。与 `PiniaCustomProperties` 不同的是，它只接收 `State` 泛型：\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface PiniaCustomStateProperties<S> {\n    hello: string\n  }\n}\n```\n\n### 为新的定义选项添加类型 %{#typing-new-creation-options}%\n\n当为 `defineStore()` 创建新选项时，你应该扩展 `DefineStoreOptionsBase`。与 `PiniaCustomProperties` 不同的是，它只暴露了两个泛型：State 和 Store 类型，允许你限制定义选项的可用类型。例如，你可以使用 action 的名称：\n\n```ts\nimport 'pinia'\n\ndeclare module 'pinia' {\n  export interface DefineStoreOptionsBase<S, Store> {\n    // 任意 action 都允许定义一个防抖的毫秒数\n    debounce?: Partial<Record<keyof StoreActions<Store>, number>>\n  }\n}\n```\n\n:::tip\n还有一个可以从一个 store 类型中提取 _getter_ 的 `StoreGetters` 类型。你也可以且**只可以**通过扩展 `DefineStoreOptions` 或 `DefineSetupStoreOptions` 类型来扩展 _setup store_ 或 _option store_ 的选项。\n:::\n\n## Nuxt %{#nuxt}%\n\n当[在 Nuxt 中使用 pinia](../ssr/nuxt.md) 时，你必须先创建一个 [Nuxt 插件](https://nuxt.com/docs/guide/directory-structure/plugins)。这样你才能访问到 `pinia` 实例：\n\n```ts{14-16}\n// plugins/myPiniaPlugin.js\nimport { PiniaPluginContext } from 'pinia'\nimport { Plugin } from '@nuxt/types'\n\nfunction MyPiniaPlugin({ store }: PiniaPluginContext) {\n  store.$subscribe((mutation) => {\n    // 响应 store 变更\n    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)\n  })\n\n  // 请注意，如果你使用的是 TS，则必须添加类型。\n  return { creationTime: new Date() }\n}\n\nconst myPlugin: Plugin = ({ $pinia }) => {\n  $pinia.use(MyPiniaPlugin)\n}\n\nexport default myPlugin\n```\n\n::: info\n\n注意上面的例子使用的是 TypeScript。如果你使用的是 `.js` 文件，你必须删除类型标注 `PiniaPluginContext` 和 `Plugin` 以及它们的导入语句。\n\n:::\n\n### Nuxt 2\n\n如果你使用的是 Nuxt 2，其类型会稍有不同：\n\n```ts{3,15-17}\n// plugins/myPiniaPlugin.ts\nimport { PiniaPluginContext } from 'pinia'\nimport { Plugin } from '@nuxt/types'\n\nfunction MyPiniaPlugin({ store }: PiniaPluginContext) {\n  store.$subscribe((mutation) => {\n    // 响应 store 变更\n    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)\n  })\n\n  // 请注意，如果你使用的是 TS，则必须添加类型。\n  return { creationTime: new Date() }\n}\n\nconst myPlugin: Plugin = ({ $pinia }) => {\n  $pinia.use(MyPiniaPlugin)\n}\n\nexport default myPlugin\n```\n"
  },
  {
    "path": "packages/docs/zh/core-concepts/state.md",
    "content": "# State %{#state}%\n\n<RuleKitLink />\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-state-from-a-pinia-store\"\n  title=\"Learn all about state in Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/the-3-pillars-of-pinia-state\"\n  title=\"Learn all about state in Pinia\"\n/>\n\n在大多数情况下，state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中，state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。\n\n```js\nimport { defineStore } from 'pinia'\n\nconst useStore = defineStore('storeId', {\n  // 为了完整类型推理，推荐使用箭头函数\n  state: () => {\n    return {\n      // 所有这些属性都将自动推断出它们的类型\n      count: 0,\n      name: 'Eduardo',\n      isAdmin: true,\n      items: [],\n      hasChanged: true,\n    }\n  },\n})\n```\n\n:::tip\n如果你使用的是 Vue 2，你在 `state` 中创建的数据与 Vue 实例中的 `data` 遵循同样的规则，即 state 对象必须是清晰的，当你想向其**添加新属性**时，你需要调用 `Vue.set()` 。**参考：[Vue#data](https://v2.cn.vuejs.org/v2/api/#data)**。\n:::\n\n## TypeScript %{#typescript}%\n\n你并不需要做太多努力就能使你的 state 兼容 TS。确保启用了 strict，或者至少启用了 noImplicitThis，Pinia 将自动推断您的状态类型！ 但是，在某些情况下，您应该帮助它进行一些转换：\n\n```ts\nconst useStore = defineStore('storeId', {\n  state: () => {\n    return {\n      // 用于初始化空列表\n      userList: [] as UserInfo[],\n      // 用于尚未加载的数据\n      user: null as UserInfo | null,\n    }\n  },\n})\n\ninterface UserInfo {\n  name: string\n  age: number\n}\n```\n\n如果你愿意，你可以用一个接口定义 state，并添加 `state()` 的返回值的类型。\n\n```ts\ninterface State {\n  userList: UserInfo[]\n  user: UserInfo | null\n}\n\nconst useStore = defineStore('storeId', {\n  state: (): State => {\n    return {\n      userList: [],\n      user: null,\n    }\n  },\n})\n\ninterface UserInfo {\n  name: string\n  age: number\n}\n```\n\n## 访问 `state` %{#accessing-the-state}%\n\n默认情况下，你可以通过 `store` 实例访问 state，直接对其进行读写。\n\n```js\nconst store = useStore()\n\nstore.count++\n```\n\n注意，新的属性**如果没有在 `state()` 中被定义**，则不能被添加。它必须包含初始状态。例如：如果 `secondCount` 没有在 `state()` 中定义，我们无法执行 `store.secondCount = 2`。\n\n## 重置 state %{#resetting-the-state}%\n\n使用[选项式 API](/zh/core-concepts/index.md#option-stores) 时，你可以通过调用 store 的 `$reset()` 方法将 state 重置为初始值。\n\n```js\nconst store = useStore()\n\nstore.$reset()\n```\n\n在 `$reset()` 内部，会调用 `state()` 函数来创建一个新的状态对象，并用它替换当前状态。\n\n在 [Setup Stores](/core-concepts/index.md#setup-stores) 中，您需要创建自己的 `$reset()` 方法：\n\n```ts\nexport const useCounterStore = defineStore('counter', () => {\n  const count = ref(0)\n\n  function $reset() {\n    count.value = 0\n  }\n\n  return { count, $reset }\n})\n```\n\n### 使用选项式 API 的用法 %{#usage-with-the-options-api}%\n\n<VueSchoolLink\n  href=\"https://vueschool.io/lessons/access-pinia-state-in-the-options-api\"\n  title=\"Access Pinia State via the Options API\"\n/>\n\n在下面的例子中，你可以假设相关 store 已经创建了：\n\n```js\n// 示例文件路径：\n// ./src/stores/counter.js\n\nimport { defineStore } from 'pinia'\n\nconst useCounterStore = defineStore('counter', {\n  state: () => ({\n    count: 0,\n  }),\n})\n```\n\n如果你不能使用组合式 API，但你可以使用 `computed`，`methods`，...，那你可以使用 `mapState()` 辅助函数将 state 属性映射为只读的计算属性：\n\n```js\nimport { mapState } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  computed: {\n    // 可以访问组件中的 this.count\n    // 与从 store.count 中读取的数据相同\n    ...mapState(useCounterStore, ['count'])\n    // 与上述相同，但将其注册为 this.myOwnName\n    ...mapState(useCounterStore, {\n      myOwnName: 'count',\n      // 你也可以写一个函数来获得对 store 的访问权\n      double: store => store.count * 2,\n      // 它可以访问 `this`，但它没有标注类型...\n      magicValue(store) {\n        return store.someGetter + this.count + this.double\n      },\n    }),\n  },\n}\n```\n\n#### 可修改的 state %{#modifiable-state}%\n\n如果你想修改这些 state 属性 (例如，如果你有一个表单)，你可以使用 `mapWritableState()` 作为代替。但注意你不能像 `mapState()` 那样传递一个函数：\n\n```js\nimport { mapWritableState } from 'pinia'\nimport { useCounterStore } from '../stores/counter'\n\nexport default {\n  computed: {\n    // 可以访问组件中的 this.count，并允许设置它。\n    // this.count++\n    // 与从 store.count 中读取的数据相同\n    ...mapWritableState(useCounterStore, ['count'])\n    // 与上述相同，但将其注册为 this.myOwnName\n    ...mapWritableState(useCounterStore, {\n      myOwnName: 'count',\n    }),\n  },\n}\n```\n\n:::tip\n对于像数组这样的集合，你并不一定需要使用 `mapWritableState()`，`mapState()` 也允许你调用集合上的方法，除非你想用 `cartItems = []` 替换整个数组。\n:::\n\n## 变更 state %{#mutating-the-state}%\n\n<!-- TODO: disable this with `strictMode` -->\n\n除了用 `store.count++` 直接改变 store，你还可以调用 `$patch` 方法。它允许你用一个 `state` 的补丁对象在同一时间更改多个属性：\n\n```js\nstore.$patch({\n  count: store.count + 1,\n  age: 120,\n  name: 'DIO',\n})\n```\n\n不过，用这种语法的话，有些变更真的很难实现或者很耗时：任何集合的修改（例如，向数组中添加、移除一个元素或是做 `splice` 操作）都需要你创建一个新的集合。因此，`$patch` 方法也接受一个函数来组合这种难以用补丁对象实现的变更。\n\n```js\nstore.$patch((state) => {\n  state.items.push({ name: 'shoes', quantity: 1 })\n  state.hasChanged = true\n})\n```\n\n<!-- TODO: disable this with `strictMode`, `{ noDirectPatch: true }` -->\n\n两种变更 store 方法的主要区别是，`$patch()` 允许你将多个变更归入 devtools 的同一个条目中。同时请注意，**直接修改 `state`，`$patch()` 也会出现在 devtools 中**，而且可以进行 time travel (在 Vue 3 中还没有)。\n\n## 替换 `state` %{#replacing-the-state}%\n\n你**不能完全替换掉** store 的 state，因为那样会破坏其响应性。但是，你可以 _patch_ 它。\n\n```js\n// 这实际上并没有替换`$state`\nstore.$state = { count: 24 }\n// 在它内部调用 `$patch()`：\nstore.$patch({ count: 24 })\n```\n\n你也可以通过变更 `pinia` 实例的 `state` 来设置整个应用的初始 state。这常用于 [SSR 中的激活过程](../ssr/#state-hydration)。\n\n```js\npinia.state.value = {}\n```\n\n## 订阅 state %{#subscribing-to-the-state}%\n\n类似于 Vuex 的 [subscribe 方法](https://vuex.vuejs.org/zh/api/index.html#subscribe)，你可以通过 store 的 `$subscribe()` 方法侦听 state 及其变化。比起普通的 `watch()`，使用 `$subscribe()` 的好处是 _subscriptions_ 在 _patch_ 后只触发一次 (例如，当使用上面的函数版本时)。\n\n```js\ncartStore.$subscribe((mutation, state) => {\n  // import { MutationType } from 'pinia'\n  mutation.type // 'direct' | 'patch object' | 'patch function'\n  // 和 cartStore.$id 一样\n  mutation.storeId // 'cart'\n  // 只有 mutation.type === 'patch object'的情况下才可用\n  mutation.payload // 传递给 cartStore.$patch() 的补丁对象。\n\n  // 每当状态发生变化时，将整个 state 持久化到本地存储。\n  localStorage.setItem('cart', JSON.stringify(state))\n})\n```\n\n### 刷新时机 %{#flush-timing}%\n\n在底层实现上，`$subscribe()` 使用了 Vue 的 `watch()` 函数。你可以传入与 `watch()` 相同的选项。当你想要在 **每次** state 变化后立即触发订阅时很有用：\n\n```ts{4}\ncartStore.$subscribe((mutation, state) => {\n  // 每当状态发生变化时，将整个 state 持久化到本地存储\n  localStorage.setItem('cart', JSON.stringify(state))\n}, { flush: 'sync' })\n```\n\n### 取消订阅 %{#detaching-subscriptions}%\n\n默认情况下，_state subscription_ 会被绑定到添加它们的组件上 (如果 store 在组件的 `setup()` 里面)。这意味着，当该组件被卸载时，它们将被自动删除。如果你想在组件卸载后依旧保留它们，请将 `{ detached: true }` 作为第二个参数，以将 _state subscription_ 从当前组件中*分离*：\n\n```vue\n<script setup>\nconst someStore = useSomeStore()\n// 此订阅器即便在组件卸载之后仍会被保留\nsomeStore.$subscribe(callback, { detached: true })\n</script>\n```\n\n:::tip\n你可以在 `pinia` 实例上使用 `watch()` 函数侦听整个 state。\n\n```js\nwatch(\n  pinia.state,\n  (state) => {\n    // 每当状态发生变化时，将整个 state 持久化到本地存储。\n    localStorage.setItem('piniaState', JSON.stringify(state))\n  },\n  { deep: true }\n)\n```\n\n:::\n"
  },
  {
    "path": "packages/docs/zh/getting-started.md",
    "content": "# 开始\n\n<RuleKitLink />\n\n## 安装 %{#installation}%\n\n<VueMasteryLogoLink for=\"pinia-cheat-sheet\">\n</VueMasteryLogoLink>\n\n用你喜欢的包管理器安装 `pinia`：\n\n```bash\nyarn add pinia\n# 或者使用 npm\nnpm install pinia\n```\n\n:::tip\n如果你的应用使用的 Vue 版本低于 2.7，你还需要安装组合式 API 包：`@vue/composition-api`。如果你使用的是 Nuxt，你应该参考[这篇指南](/ssr/nuxt.md)。\n:::\n\n如果你正在使用 Vue CLI，你可以试试这个[**非官方插件**](https://github.com/wobsoriano/vue-cli-plugin-pinia)。\n\n创建一个 pinia 实例 (根 store) 并将其传递给应用：\n\n```js {2,5-6,8}\nimport { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\n\nconst pinia = createPinia()\nconst app = createApp(App)\n\napp.use(pinia)\napp.mount('#app')\n```\n\n如果你使用的是 Vue 2，你还需要安装一个插件，并在应用的根部注入创建的 `pinia`：\n\n```js {1,3-4,12}\nimport { createPinia, PiniaVuePlugin } from 'pinia'\n\nVue.use(PiniaVuePlugin)\nconst pinia = createPinia()\n\nnew Vue({\n  el: '#app',\n  // 其他配置...\n  // ...\n  // 请注意，同一个`pinia'实例\n  // 可以在同一个页面的多个 Vue 应用中使用。\n  pinia,\n})\n```\n\n这样才能提供 devtools 的支持。在 Vue 3 中，一些功能仍然不被支持，如 time traveling 和编辑，这是因为 vue-devtools 还没有相关的 API，但 devtools 也有很多针对 Vue 3 的专属功能，而且就开发者的体验来说，Vue 3 整体上要好得多。在 Vue 2 中，Pinia 使用的是 Vuex 的现有接口 (因此不能与 Vuex 一起使用) 。\n\n## Store 是什么？%{#what-is-a-store}%\n\nStore (如 Pinia) 是一个保存状态和业务逻辑的实体，它并不与你的组件树绑定。换句话说，**它承载着全局状态**。它有点像一个永远存在的组件，每个组件都可以读取和写入它。它有**三个概念**，[state](./core-concepts/state.md)、[getter](./core-concepts/getters.md) 和 [action](./core-concepts/actions.md)，我们可以假设这些概念相当于组件中的 `data`、 `computed` 和 `methods`。\n\n## 应该在什么时候使用 Store? %{#when-should-i-use-a-store}%\n\n一个 Store 应该包含可以在整个应用中访问的数据。这包括在许多地方使用的数据，例如显示在导航栏中的用户信息，以及需要通过页面保存的数据，例如一个非常复杂的多步骤表单。\n\n另一方面，你应该避免在 Store 中引入那些原本可以在组件中保存的本地数据，例如，一个元素在页面中的可见性。\n\n并非所有的应用都需要访问全局状态，但如果你的应用确实需要一个全局状态，那 Pinia 将使你的开发过程更轻松。\n\n## 什么时候**不**应该使用 Store\n\n有的时候我们会过度使用 store。如果觉得应用程序的 store 过多，你可能需要重新考虑使用 store 的目的。例如其中一些逻辑应该只是组合式函数，或者应该只是组件的本地状态。这在 Mastering Pinia 的 [(不要) 滥用 store](https://masteringpinia.com/lessons/not-overusing-stores) 课程中有详细介绍。\n"
  },
  {
    "path": "packages/docs/zh/index.md",
    "content": "---\nlayout: home\n\ntitle: Pinia\ntitleTemplate: The intuitive store for Vue.js\n\nhero:\n  name: Pinia\n  text: \"符合直觉的 \\nVue.js 状态管理库\"\n  tagline: \"类型安全、可扩展性以及模块化设计。\\n甚至让你忘记正在使用的是一个状态库。\"\n  image:\n    src: /logo.svg\n    alt: Pinia\n  actions:\n    - theme: brand\n      text: 开始使用\n      link: /zh/introduction\n    - theme: alt\n      text: Demo 演示\n      link: https://stackblitz.com/github/piniajs/example-vue-3-vite\n    - theme: cta rulekit\n      text: RuleKit\n      link: https://rulekit.dev?from=pinia\n    - theme: cta mastering-pinia\n      text: ' '\n      link: https://masteringpinia.com\n    - theme: cta vueschool\n      text: 观看视频介绍\n      link: https://vueschool.io/lessons/introduction-to-pinia?friend=vuerouter&utm_source=pinia&utm_medium=link&utm_campaign=homepage\n    - theme: cta vue-mastery\n      text: 获取 Pinia 速查表\n      link: https://www.vuemastery.com/pinia?coupon=PINIA-DOCS&via=eduardo\n\nfeatures:\n  - title: 💡 所见即所得\n    details: 与组件类似的 Store。其 API 的设计旨在让你编写出更易组织的 store。\n  - title: 🔑 类型安全\n    details: 类型可自动推断，即使在 JavaScript 中亦可为你提供自动补全功能！\n  - title: ⚙️ 开发工具支持\n    details: 不管是 Vue 2 还是 Vue 3，支持 Vue devtools 钩子的 Pinia 都能给你更好的开发体验。\n  - title: 🔌 可扩展性\n    details: 可通过事务、同步本地存储等方式扩展 Pinia，以响应 store 的变更以及 action。\n  - title: 🏗 模块化设计\n    details: 可构建多个 Store 并允许你的打包工具自动拆分它们。\n  - title: 📦 极致轻量化\n    details: Pinia 大小只有 1kb 左右，你甚至可能忘记它的存在！\n---\n\n<script setup>\nimport HomeSponsors from '../.vitepress/theme/components/HomeSponsors.vue'\n</script>\n\n<HomeSponsors />\n"
  },
  {
    "path": "packages/docs/zh/introduction.md",
    "content": "# 简介 %{#introduction}%\n\n<!-- <VueSchoolLink\n  href=\"https://vueschool.io/lessons/introduction-to-pinia\"\n  title=\"Get started with Pinia\"\n/> -->\n\n<MasteringPiniaLink\n  href=\"https://play.gumlet.io/embed/651ecf274c2f339c6860e36b\"\n  mp-link=\"https://masteringpinia.com/lessons/the-what-and-why-of-state-management-and-stores\"\n  title=\"Create your own Pinia from scratch\"\n/>\n\nPinia [起始](https://github.com/vuejs/pinia/commit/06aeef54e2cad66696063c62829dac74e15fd19e)于 2019 年 11 月左右的一次实验，其目的是设计一个拥有[组合式 API](https://github.com/vuejs/composition-api) 的 Vue 状态管理库。从那时起，我们就倾向于同时支持 Vue 2 和 Vue 3，并且不强制要求开发者使用组合式 API，我们的初心至今没有改变。除了**安装**和 **SSR** 两章之外，其余章节中提到的 API 均支持 Vue 2 和 Vue 3。虽然本文档主要是面向 Vue 3 的用户，但在必要时会标注出 Vue 2 的内容，因此 Vue 2 和 Vue 3 的用户都可以阅读本文档。\n\n## 为什么你应该使用 Pinia？%{#why-should-i-use-pinia}%\n\nPinia 是 Vue 的专属状态管理库，它允许你跨组件或页面共享状态。如果你熟悉组合式 API 的话，你可能会认为可以通过一行简单的 `export const state = reactive({})` 来共享一个全局状态。对于单页应用来说确实可以，但如果应用在服务器端渲染，这可能会使你的应用暴露出一些[安全漏洞](https://cn.vuejs.org/guide/scaling-up/ssr#cross-request-state-pollution)。 而如果使用 Pinia，即使在小型单页应用中，你也可以获得如下功能：\n\n- 测试工具集\n- 插件：可通过插件扩展 Pinia 功能\n- 为 JS 开发者提供适当的 TypeScript 支持以及**自动补全**功能。\n- 支持服务端渲染\n- Devtools 支持\n  - 追踪 actions、mutations 的时间线\n  - 在组件中展示它们所用到的 Store\n  - 让调试更容易的 Time travel\n- 热更新\n  - 不必重载页面即可修改 Store\n  - 开发时可保持当前的 State\n\n如果你仍然有疑问，请查看[**官方**的 Mastering Pinia 课程](https://masteringpinia.com)。在一开始，我们会介绍如何创建自己的 `defineStore()` 函数，然后我们会转向官方的 Pinia API。\n\n<RuleKitLink />\n\n<VueMasteryLogoLink for=\"pinia-cheat-sheet\">\n</VueMasteryLogoLink>\n\n## 基础示例 %{#basic-example}%\n\n下面就是 pinia API 的基本用法 (为继续阅读本简介请确保你已阅读过了[开始](./getting-started.md)章节)。你可以先创建一个 Store：\n\n```js\n// stores/counter.js\nimport { defineStore } from 'pinia'\n\nexport const useCounterStore = defineStore('counter', {\n  state: () => {\n    return { count: 0 }\n  },\n  // 也可以这样定义\n  // state: () => ({ count: 0 })\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n```\n\n然后你就可以在一个组件中*使用*该 store 了：\n\n```vue\n<script setup>\nimport { useCounterStore } from '@/stores/counter'\n\nconst counter = useCounterStore()\n\ncounter.count++\n// 自动补全！ ✨\ncounter.$patch({ count: counter.count + 1 })\n// 或使用 action 代替\ncounter.increment()\n</script>\n\n<template>\n  <!-- 直接从 store 中访问 state -->\n  <div>Current Count: {{ counter.count }}</div>\n</template>\n```\n\n[在演练场中试一试](https://play.pinia.vuejs.org/#eNqNVM1O3DAQfpVpVGkXQWIQLYfVgqCIQ3toq9JjLsEZWNPEtuzJstUqb9IH6HP1STq2k/2hFeKyG49nvvnmsz+vsytri2WH2Sybe+mUJfBInb0otWqtcQRr6Dxem04TulsyDqGHe2damBRCpnDx6CelLrU02hMMQTh/Xjg9SEmpJv4fHpZaCHhStICqIyNNaxskZTT8+fV7m/zWViQX03UCn409Eggcwgn0DM5IxnFXpR+g0lDJCKSYFFb1Fkxp6bBFTYHQXKSxeWBeEHL/ipBXAPM3eQ5XUqL3QAsET7wDtXIoqfmZREjxoEqep6JaLS+uO+cYH+L0M1gPvDeE+34uQl5ov2mZHWVJ8rytLEtqNB/KOmCWw4YvMwYLkRCzSqsqRMpMxO8CfZvfOfPk45GU2dGYesknLGpckjGNzyurUtmCyPqZELLWnF9jo5au0EhC21b8U3N5VrwvTkSj7gQ3EkrXuNpvwxV5je1r0MfUy+Pi5F1xFlGXpwNoG1ADaF/qnmUhzzfrXj08EyVcFtWg+2LDOe+LUzWNefoUY+Q63FCUC5Q//hN/9KvE+qtDlm+JO2NR5R6Q0vbN7Wdc8fdmszV113D2C5vf0JumCxxT2odO10x7Jy+y/RjPmO/ud3+zItR+HCoQjWrE/Cjz9Qujb+meFqc7Km7NyhJuzF3jvdK4b+x4m6KjcRXTkrGfvwPnu8XTyYA/OUpUoltmMD2A84uRnOOnxWnuOtj4OHAbB2P3cripoWq8gTt2WkTntR+29yC3jwGjsJFh8LvfSLHj8zEEbFjlt29PiKTu4bc/yPq/puS2IQ==)\n\n为实现更多高级用法，你甚至可以使用一个函数 (与组件 `setup()` 类似) 来定义一个 Store：\n\n```js\nexport const useCounterStore = defineStore('counter', () => {\n  const count = ref(0)\n  function increment() {\n    count.value++\n  }\n\n  return { count, increment }\n})\n```\n\n[在演练场中试一试](https://play.pinia.vuejs.org/#eNqNVEFu2zAQ/MpWKGAHscQGaXMwnCBpkEN7aIumR10Uah0zlUiCXCkuDP2kD+i7+pIuSVt20iLoSeJydnZ2yOUmu7K26DvM5tnCS6csgUfq7EWpVWuNI9hA5/HadJrQ3ZJxCAMsnWlhUgiZwsWDn5S61NJoT7ANwvnzxOlRAqWc+D0+LrUQ8KhoBVVHRprWNkjKaPj989ce/NpWJFfTTSKf72okEjiGExiYnJmM46pK30OloZKRSLEorOo9mdLSYYuagqCFSG1zw7wg5PoVIa8AFq/yHK6kRO+BVgieeAdq5VBS8yOZkOLBlTxPSbXqL64755gfYvdz2Gx1j4KHYSECLpQfS2azLFmet5VlS43mQ9kEznK74cuMyUIkxKzSqgqRMhPxv0Df5nfOPPp4JGU220Ev+YRFjT0Z0/i8siqlrYisnwsha834GhvVu0IjCW1b8VfO5VnxrjgRjboTXEgoXeP6aRnOyGts/4d9B718U5y8Lc4ia3+6JW0DayAdSj2wLeT5Zi3V/TNTwmVRDbrPNpzzU3OqpjGPH2OMXIejRLlC+f0f8Qe/Tqq/OGT7ejxoiyp3j5S2b24/4Zr/x83W1F3D6Bc2v6I3TRc0Jtj7Ttcs+wAX1X6IZ8x395u/WRNqv2sqCI1uRHy0+fqF1vdyT4vTAxf3w8oWjsPtcDkONBPzHI9bNS6VxqczHy9aHHZcR1ia+edPxPlh8nSyLT2ZwfQIzi+S1oPXgvGsY/qG5xFg2end4I5zuusuoou+ajoMT0fsLXwcv1lOs+YImO1TY/NH2fAHelGuuQ==)\n\n如果你还不熟悉 setup() 函数和组合式 API，别担心，Pinia 也提供了一组类似 Vuex 的 [映射 state 的辅助函数](https://vuex.vuejs.org/zh/guide/state.html#mapstate-辅助函数)。你可以用和之前一样的方式来定义 Store，然后通过 `mapStores()`、`mapState()` 或 `mapActions()` 访问：\n\n```js {22,24,28}\nconst useCounterStore = defineStore('counter', {\n  state: () => ({ count: 0 }),\n  getters: {\n    double: (state) => state.count * 2,\n  },\n  actions: {\n    increment() {\n      this.count++\n    },\n  },\n})\n\nconst useUserStore = defineStore('user', {\n  // ...\n})\n\nexport default defineComponent({\n  computed: {\n    // 其他计算属性\n    // ...\n    // 允许访问 this.counterStore 和 this.userStore\n    ...mapStores(useCounterStore, useUserStore)\n    // 允许读取 this.count 和 this.double\n    ...mapState(useCounterStore, ['count', 'double']),\n  },\n  methods: {\n    // 允许读取 this.increment()\n    ...mapActions(useCounterStore, ['increment']),\n  },\n})\n```\n\n[在演练场中试一试](https://play.pinia.vuejs.org/#eNqdVcFy0zAQ/RWNL0lpIrUUesikmRTooRyAoXDCHBxrm6i1JY8kp5nJ+N9ZS7bsOIFhekmk1b7dt0/a9T66LQq6LSGaRXOTalHYRSxFXihtyZ5weBQSPircS5CWVORRq5yMEDDqueVJ8WCVBjPxy8SCW92mVihpAqwQUiR9YGkweCktaIcPjpSl3kyfzMD/pzl2RnPjGUvYOV9knpSZ++9XMN7HkpAUt6UFPiNuSwhjRNkN6HBCCq0K0FaACR6U0rBeiy0YkqQpGEOsInYjDG04e3aJ5N5ak3MmD8YoQa7xoP7JQYFnk0E6DQk/mbNLxlW5ygaZ8DaOE/0aOeRoQkYeM/rt81XuNwe7Udz0BTpZspCphrwW9qyftLn4U2kDop+wQvSchfeHGwt5kSFz3BEy52K7cIGQ0B4vqQvZCFBVc1Y7Be9Prijn7us7dFmV1ipJlmkm0uebOAqs4mhx367nzLshZM4CoWgS+fc4xULx1SmJveNkwjDuwMRREC6O3KOvLXHE3JqCyacrrV78q42j5p7jaIl9xThsrVKZmSaF8LCNtYWZMZZyif4cMrHVVIJlssjZEWZ5Td/TS5aJFcNETEgOu8M0iJhyyP8neuu6vKCX7+i1i7q9aoLmdVR3hXiDKIs1qZKPYj0Qpe4pkYH+WrhHcSBOkmXq5bOzWV1CoJhuIH0+YX8yO8/6G7YP6C30yrKJXgNeYH189/AFdrgOh7niJTbGvw6/g1FZWXP0bh9KyZF2z8+xvXd3LOT6h7nbWZCmLaom2nWQk7meO38rvaN7Ra96KnaTDyUcTOLDwdeO0zD0UH5jj4bqTR889n0PGjvfUTH1fJiR8Rm5WZBx01wzckEq357IEb27SeC7CQEO6FBu1TTiG/K2N0YSPwcCuDcuWhPpzbHzc2/z4HYwoCbNgH+9IN1XY6BGHbmVop3xLmn1B2TmaJo=)\n\n你将会在核心概念部分了解到更多关于每个**映射辅助函数**的信息。\n\n## 官方课程\n\nPinia 的官方课程是 [Mastering Pinia](https://masteringpinia.com)。由 Pinia 的作者编写，它涵盖了从基础知识到诸如插件、测试和服务器端渲染之类的高级主题。这是学习 Pinia 和掌握它的最佳方式。\n\n## 为什么取名 _Pinia_？%{#why-pinia}%\n\nPinia (发音为 `/piːnjʌ/`，类似英文中的 “peenya”) 是最接近有效包名 piña (西班牙语中的 _pineapple_，即“菠萝”) 的词。 菠萝花实际上是一组各自独立的花朵，它们结合在一起，由此形成一个多重的水果。 与 Store 类似，每一个都是独立诞生的，但最终它们都是相互联系的。 它(菠萝)也是一种原产于南美洲的美味热带水果。\n\n## 更真实的示例 %{#a-more-realistic-example}%\n\n这是一个更完整的 Pinia API 示例，在 JavaScript 中也使用了类型提示。对于某些开发者来说，可能足以在不进一步阅读的情况下直接开始阅读本节内容，但我们仍然建议你先继续阅读文档的其余部分，甚至跳过此示例，在阅读完所有**核心概念**之后再回来。\n\n```js\nimport { defineStore } from 'pinia'\n\nexport const useTodos = defineStore('todos', {\n  state: () => ({\n    /** @type {{ text: string, id: number, isFinished: boolean }[]} */\n    todos: [],\n    /** @type {'all' | 'finished' | 'unfinished'} */\n    filter: 'all',\n    // 类型将自动推断为 number\n    nextId: 0,\n  }),\n  getters: {\n    finishedTodos(state) {\n      // 自动补全！ ✨\n      return state.todos.filter((todo) => todo.isFinished)\n    },\n    unfinishedTodos(state) {\n      return state.todos.filter((todo) => !todo.isFinished)\n    },\n    /**\n     * @returns {{ text: string, id: number, isFinished: boolean }[]}\n     */\n    filteredTodos(state) {\n      if (this.filter === 'finished') {\n        // 调用其他带有自动补全的 getters ✨\n        return this.finishedTodos\n      } else if (this.filter === 'unfinished') {\n        return this.unfinishedTodos\n      }\n      return this.todos\n    },\n  },\n  actions: {\n    // 接受任何数量的参数，返回一个 Promise 或不返回\n    addTodo(text) {\n      // 你可以直接变更该状态\n      this.todos.push({ text, id: this.nextId++, isFinished: false })\n    },\n  },\n})\n```\n\n[在演练场中试一试](https://play.pinia.vuejs.org/#eNqtVs1y2zYQfpU1L5QdmUzGbQ4cyWO3k86kh7STuKcwB4pcWohJgIMfWRqVb9IH6HP1SboA+Cu7nkzbiygQu99++Haxy2Nw2zTRzmCQBCuVS9ZoUKhNc51yVjdCajiCxBJaKKWoISTTcLKltJB4Jz5iqQaThnGWTY2MIpNCjBZRrO06+qrILOW54EqDe/XJ4sF6cFmc99tHKFmlUS67JxY95nrKYjHCkGvvzPHRWt/hXpM5nWcRhm67NDzXTHDICoe3OIdjygFYCYuziVe0yyqD3SYQgjaS3AFaiwIT8lGP9NTbGj55S3xCUoFwVrFPAElPC411U2UaaQWwqrINVtcrxhujYXdZiwKrdRp4KdIA9KFBWsusYIKWDpnWWVWlwTXcVtUq9hD/Ba2kxKotFhbyp+7//4Fr+BT5t2E1w95K/zR+baMxilEKSQhWfmB8XhoUIXnAQ7cdMYvuXcn5lKM3Uf2xRrL5FvOHjdhPnI9Hl+9I23JqKXMOMa6YZxh3FDs5/PYHfATLKumsT+NP6mKMbQPQ6oZO0UhUKkJOx7N59TXWcZrptDFaUz0nBVPZpsKCrKeFbOHyiuUPM5TbgsT2noSyiofiC5aBv8aXddbQfRWcGoW7BGm3QTIn/bVIA3f37Zs0iN3/CFV9uZHiUaEk/zRY9qY31EriAndaiEpdZg3zblutG5XEcV5wsidx2E5GHHXMmzp+4nPzNvo+ekPSb2IKFDNe4H4ehjwuC6y/Bb03vXkdvfkueutQd1cdaG1RuxvfkixaUWsp2f2JKLmoG1ah/KWxbWUuDt1G8fize6elwYGiK7Fn3n9VVHWW9a+UfJQ7nBxLZ/IeKZt2+92nDy6zwyYVlanI+oXNj6hEZSxHb/aD4QXRntg5tu9djhm/v1Pv9hq56g9liTo1nL2T+ccXjj7SvYqupip2c4AEHMZFgdQA0E+C05mSctw7M9/Xh8mynnotQgcbLn18pamSE6DWvr6GRUcpvriAG3vN3G0mhRKyk3TQJbAiAW7qjZ01Y0dIYENFhxmH9vOXFi5ij+MiJfD5S6fbBDckBUP4HcK+n7nF2OzCEcX3rQScS48UuzYAj6yqYIOQGS3qTLOcbA7U7EqU1OmIQEfWe5E++j2Rfe1Q2nP3IOkJnmh2h+8Z+BHr9BlGmwtsY9lKrtCm8gz++uPPftePPi9q5NPn2S/c6HUinzRTN/j6UgEYFXg+/rdEOHs5BGWhQ6NseDz17xLdw8wS9U/M7VeD3rKeL6zXNNyHdE8Mncg2kSD0lgy7BFGu9fZE/Kn2gzZdkImKvUkLWCl8nsmk9GZcpqAnyRlgT5LjbF1upsL738x9UY3VZuuJHyCrheEaRAnUC0xNo0wte7gMGrrmjIgLCVxo79h/SdmszevzIAzJx6FgEnNN16E2NhVEC33d9LYjz6gxarvwJeBT7/b8fXn1al4BZWZFbGdVZX/b86D9GztAvyY=)\n\n## 对比 Vuex %{#comparison-with-vuex}%\n\nPinia 起源于一次探索 Vuex 下一个迭代的实验，因此结合了 Vuex 5 核心团队讨论中的许多想法。最后，我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分功能，所以决定将其作为新的推荐方案来代替 Vuex。\n\n与 Vuex 相比，Pinia 不仅提供了一个更简单的 API，也提供了符合组合式 API 风格的 API，最重要的是，搭配 TypeScript 一起使用时有非常可靠的类型推断支持。\n\n### RFC %{#rfcs}%\n\n最初，Pinia 没有经过任何 RFC 的流程。我基于自己开发应用的经验，同时通过阅读其他人的代码，为使用 Pinia 的用户工作，以及在 Discord 上回答问题等方式验证了一些想法。\n这些经历使我产出了这样一个可用的解决方案，并适应了各种场景和应用规模。我会一直在保持其核心 API 不变的情况下发布新版本，同时不断优化本库。\n\n现在 Pinia 已经成为推荐的状态管理解决方案，它和 Vue 生态系统中的其他核心库一样，都要经过 RFC 流程，它的 API 也已经进入稳定状态。\n\n### 对比 Vuex 3.x/4.x %{#comparison-with-vuex-3-x-4-x}%\n\n> Vuex 3.x 只适配 Vue 2，而 Vuex 4.x 是适配 Vue 3 的。\n\nPinia API 与 Vuex(<=4) 也有很多不同，即：\n\n- _mutation_ 已被弃用。它们经常被认为是**极其冗余的**。它们初衷是带来 devtools 的集成方案，但这已不再是一个问题了。\n- 无需要创建自定义的复杂包装器来支持 TypeScript，一切都可标注类型，API 的设计方式是尽可能地利用 TS 类型推理。\n- 无过多的魔法字符串注入，只需要导入函数并调用它们，然后享受自动补全的乐趣就好。\n- 无需要动态添加 Store，它们默认都是动态的，甚至你可能都不会注意到这点。注意，你仍然可以在任何时候手动使用一个 Store 来注册它，但因为它是自动的，所以你不需要担心它。\n- 不再有嵌套结构的**模块**。你仍然可以通过导入和使用另一个 Store 来隐含地嵌套 stores 空间。虽然 Pinia 从设计上提供的是一个扁平的结构，但仍然能够在 Store 之间进行交叉组合。**你甚至可以让 Stores 有循环依赖关系**。\n- 不再有**可命名的模块**。考虑到 Store 的扁平架构，Store 的命名取决于它们的定义方式，你甚至可以说所有 Store 都应该命名。\n\n关于如何将现有 Vuex(<=4) 的项目转化为使用 Pinia 的更多详细说明，请参阅 [Vuex 迁移指南](./cookbook/migration-vuex.md)。\n"
  },
  {
    "path": "packages/docs/zh/ssr/index.md",
    "content": "# 服务端渲染 (SSR) %{#server-side-rendering-ssr}%\n\n<RuleKitLink />\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/ssr-friendly-state\"\n  title=\"Learn about SSR best practices\"\n/>\n\n:::tip\n如果你使用的是 **Nuxt**，你需要阅读的是[**这些说明文档**](./nuxt.md)。\n:::\n\n只要你只在 `setup` 函数、`getter` 和 `action` 的顶部调用你定义的 `useStore()` 函数，那么使用 Pinia 创建 store 对于 SSR 来说应该是开箱即用的：\n\n```vue\n<script setup>\n// 这是可行的，\n// 因为 pinia 知道在 `setup` 中运行的是什么程序。\nconst main = useMainStore()\n</script>\n```\n\n## 在 `setup()` 外部使用 store %{#using-the-store-outside-of-setup}%\n\n如果你需要在其他地方使用 store，你需要将[原本被传递给应用](#install-the-plugin) 的 `pinia` 实例传递给 `useStore()` 函数：\n\n```js\nconst pinia = createPinia()\nconst app = createApp(App)\n\napp.use(router)\napp.use(pinia)\n\nrouter.beforeEach((to) => {\n  // ✅这会正常工作，因为它确保了正确的 store 被用于\n  // 当前正在运行的应用\n  const main = useMainStore(pinia)\n\n  if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'\n})\n```\n\nPinia 会将自己作为 `$pinia` 添加到你的应用中，所以你可以在 `serverPrefetch()` 等函数中使用它。\n\n```js\nexport default {\n  serverPrefetch() {\n    const store = useStore(this.$pinia)\n  },\n}\n```\n\n## State 激活 %{#state-hydration}%\n\n为了激活初始 state，你需要确保 rootState 包含在 HTML 中的某个地方，以便 Pinia 稍后能够接收到它。根据你服务端所渲染的内容，**为了安全你应该转义 state**。我们推荐 Nuxt 目前使用的 [@nuxt/devalue](https://github.com/nuxt-contrib/devalue)：\n\n```js\nimport devalue from '@nuxt/devalue'\nimport { createPinia } from 'pinia'\n// 检索服务端的 rootState\nconst pinia = createPinia()\nconst app = createApp(App)\napp.use(router)\napp.use(pinia)\n\n// 渲染页面后，rootState 被建立，\n// 可以直接在 `pinia.state.value`上读取。\n\n// 序列化，转义(如果 state 的内容可以被用户改变，这点就非常重要，几乎都是这样的)\n// 并将其放置在页面的某处\n// 例如，作为一个全局变量。\ndevalue(pinia.state.value)\n```\n\n根据你服务端所渲染的内容，你将设置一个**初始状态**变量，该变量将在 HTML 中被序列化。你还应该保护自己免受 XSS 攻击。例如，在 [vite-ssr](https://github.com/frandiox/vite-ssr)中你可以使用[`transformState` 选项](https://github.com/frandiox/vite-ssr#state-serialization) 以及 `@nuxt/devalue`：\n\n```js\nimport devalue from '@nuxt/devalue'\n\nexport default viteSSR(\n  App,\n  {\n    routes,\n    transformState(state) {\n      return import.meta.env.SSR ? devalue(state) : state\n    },\n  },\n  ({ initialState }) => {\n    // ...\n    if (import.meta.env.SSR) {\n      // 序列化并设置为 window.__INITIAL_STATE__\n      initialState.pinia = pinia.state.value\n    } else {\n      // 在客户端，我们恢复 state\n      pinia.state.value = initialState.pinia\n    }\n  }\n)\n```\n\n你可以根据你的需要使用 `@nuxt/devalue` 的[其他替代品](https://github.com/nuxt-contrib/devalue#see-also)，例如，你也可以用 `JSON.stringify()`/`JSON.parse()` 来序列化和解析你的 state，**这样你可以把性能提高很多。**\n\n也可以根据你的环境调整这个策略。但确保在客户端调用任何 `useStore()` 函数之前，激活 pinia 的 state。例如，如果我们将 state 序列化为一个 `<script>` 标签，并在客户端通过 `window.__pinia` 全局访问它，我们可以这样写：\n\n```js\nconst pinia = createPinia()\nconst app = createApp(App)\napp.use(pinia)\n\n// 必须由用户设置\nif (isClient) {\n  pinia.state.value = JSON.parse(window.__pinia)\n}\n```\n"
  },
  {
    "path": "packages/docs/zh/ssr/nuxt.md",
    "content": "# Nuxt %{#nuxt}%\n\n<MasteringPiniaLink\n  href=\"https://masteringpinia.com/lessons/ssr-friendly-state\"\n  title=\"Learn about SSR best practices\"\n/>\n\n搭配 [Nuxt](https://nuxt.com/) 的 Pinia 更易用，因为 Nuxt 处理了很多与**服务器端渲染**有关的事情。例如，**你不需要关心序列化或 XSS 攻击**。Pinia 既支持 Nuxt Bridge 和 Nuxt 3，也支持纯 Nuxt 2，[见下文](#nuxt-2-without-bridge)。\n\n<RuleKitLink />\n\n## 安装 %{#installation}%\n\n```bash\nyarn add pinia @pinia/nuxt\n# 或者使用 npm\nnpm install pinia @pinia/nuxt\n```\n\n:::tip\n如果你正在使用 npm，你可能会遇到 _ERESOLVE unable to resolve dependency tree_ 错误。如果那样的话，将以下内容添加到 `package.json` 中：\n\n```js\n\"overrides\": {\n  \"vue\": \"latest\"\n}\n```\n\n:::\n\n我们提供了一个 _module_ 来为你处理一切，你只需要在 `nuxt.config.js` 文件的 `modules` 中添加它。\n\n```js\n// nuxt.config.js\nexport default defineNuxtConfig({\n  // ... 其他配置\n  modules: [\n    // ...\n    '@pinia/nuxt',\n  ],\n})\n```\n\n这样配置就完成了，正常使用 store 就好啦!\n\n## 在 `setup()` 外部使用 store %{#using-the-store-outside-of-setup}%\n\n如果你想在 `setup()` 外部使用一个 store，记得把 `pinia` 对象传给 `useStore()`。我们会把它添加到[上下文](https://nuxtjs.org/docs/2.x/internals-glossary/context)中，然后你就可以在 `asyncData()` 和 `fetch()` 中访问它了：\n\n```js\nimport { useStore } from '~/stores/myStore'\n\nexport default {\n  asyncData({ $pinia }) {\n    const store = useStore($pinia)\n  },\n}\n```\n\n与 `onServerPrefetch()` 一样，如果你想在 `asyncData()` 中调用一个存储动作，你不需要做任何特别的事情。\n\n```vue\n<script setup>\nconst store = useStore()\nconst { data } = await useAsyncData('user', () => store.fetchUser())\n</script>\n```\n\n## 自动引入 %{#auto-imports}%\n\n默认情况下，`@pinia/nuxt` 会暴露一个自动引入的方法：`usePinia()`，它类似于 `getActivePinia()`，但在 Nuxt 中效果更好。你可以添加自动引入来减轻你的开发工作：\n\n```js\n// nuxt.config.js\nexport default defineNuxtConfig({\n  // ... 其他配置\n  modules: [\n    // ...\n    [\n      '@pinia/nuxt',\n      {\n        autoImports: [\n          // 自动引入 `defineStore()`\n          'defineStore',\n          // 自动引入 `defineStore()` 并重命名为 `definePiniaStore()`\n          ['defineStore', 'definePiniaStore'],\n        ],\n      },\n    ],\n  ],\n})\n```\n\n## 纯 Nuxt 2 %{#nuxt-2-without-bridge}%\n\n`@pinia/nuxt` v0.2.1 之前的版本中，Pinia 都支持 Nuxt 2。请确保在安装 `pinia` 的同时也安装 [`@nuxtjs/composition-api`](https://composition-api.nuxtjs.org/)：\n\n```bash\nyarn add pinia @pinia/nuxt@0.2.1 @nuxtjs/composition-api\n# 使用 npm\nnpm install pinia @pinia/nuxt@0.2.1 @nuxtjs/composition-api\n```\n\n我们提供了一个 _module_ 来为你处理一切工作，你只需要在 `nuxt.config.js` 文件的 `buildModules` 中添加它。\n\n```js\n// nuxt.config.js\nexport default {\n  // ... 其他配置\n  buildModules: [\n    // 仅支持 Nuxt 2:\n    // https://composition-api.nuxtjs.org/getting-started/setup#quick-start\n    '@nuxtjs/composition-api/module',\n    '@pinia/nuxt',\n  ],\n}\n```\n\n### TypeScript %{#typescript}%\n\n如果你使用的是 Nuxt 2 (`@pinia/nuxt` < 0.3.0) 搭配 TypeScript，并且有 `jsconfig.json`，你应该为 `context.pinia` 引入类型：\n\n```json\n{\n  \"types\": [\n    // ...\n    \"@pinia/nuxt\"\n  ]\n}\n```\n\n这也将确保你可以使用自动补全😉。\n\n## Pinia 搭配 Vuex 使用 %{#using-pinia-alongside-vuex}%\n\n建议**避免同时使用 Pinia 和 Vuex**，但如果你确实需要同时使用，你需要告诉 Pinia 不要禁用它：\n\n```js\n// nuxt.config.js\nexport default {\n  buildModules: [\n    '@nuxtjs/composition-api/module',\n    ['@pinia/nuxt', { disableVuex: false }],\n  ],\n  // ... 其他配置\n}\n```\n"
  },
  {
    "path": "packages/nuxt/.gitignore",
    "content": ".nuxt\n.output\n"
  },
  {
    "path": "packages/nuxt/CHANGELOG.md",
    "content": "### [0.11.3](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.11.2...@pinia/nuxt@0.11.3) (2025-11-05)\n\nThis version fixes auto imports from layers which means that if you were writing something like this in options:\n\n```ts\npinia: {\n  storesDirs: ['./app/stores/**'],\n}\n```\n\nYou now need to write\n\n```ts\npinia: {\n  storesDirs: ['./stores/**'],\n}\n```\n\nThis is because _app_ is itself a layer and now auto imports work across layers.\n\n### Features\n\n- **nuxt:** automatic HMR code (vite only) ([#2954](https://github.com/vuejs/pinia/issues/2954)) ([0e9e7e7](https://github.com/vuejs/pinia/commit/0e9e7e7c9b658c31aa4da7d85311bc932d0b23dd))\n\n### Bug Fixes\n\n- **nuxt:** resolve auto-imports in layers ([#3035](https://github.com/vuejs/pinia/issues/3035)) ([e25e525](https://github.com/vuejs/pinia/commit/e25e525ad84adb47db0cedc736292140886a376f))\n- **nuxt:** unset global pinia after render ([#2915](https://github.com/vuejs/pinia/issues/2915)) ([ad2fa45](https://github.com/vuejs/pinia/commit/ad2fa452ec8efeb82b8b6b2c4b984669d9cf3245))\n\n### [0.11.2](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.11.1...@pinia/nuxt@0.11.2) (2025-07-18)\n\n### Bug Fixes\n\n- **nuxt:** mark compatibility for nuxt v4 stable ([#3009](https://github.com/vuejs/pinia/issues/3009)) ([c87a249](https://github.com/vuejs/pinia/commit/c87a249517242f547967acea1de7749b80d6fe67))\n\n### [0.11.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.11.0...@pinia/nuxt@0.11.1) (2025-06-04)\n\n### Features\n\n- Support Nuxt 4 ([#2978](https://github.com/vuejs/pinia/issues/2978)) ([925e93c](https://github.com/vuejs/pinia/commit/925e93c6180b8d2d1a1ddac29914990851e2e5ef))\n\n## [0.11.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.10.1...@pinia/nuxt@0.11.0) (2025-04-09)\n\n### Features\n\n- **nuxt:** move to esm-only ([#2947](https://github.com/vuejs/pinia/issues/2947)) ([4865716](https://github.com/vuejs/pinia/commit/4865716011cde049aef05e029d2720ac93483389))\n- fix `obj.hasOwnProperty` in `shouldHydrate`\n\n### [0.10.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.10.0...@pinia/nuxt@0.10.1) (2025-02-12)\n\n### Bug Fixes\n\n- avoid including devtools code in builds ([d3b24a3](https://github.com/vuejs/pinia/commit/d3b24a3d6a4b5af82c8ef7e66e4cecd890c30fdd)), closes [#2910](https://github.com/vuejs/pinia/issues/2910)\n- **nuxt:** usePinia composable type ([#2890](https://github.com/vuejs/pinia/issues/2890)) ([a32fb3b](https://github.com/vuejs/pinia/commit/a32fb3b9d61c58b56716931fef351fae1b479ff8))\n\n## [0.10.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.9.0...@pinia/nuxt@0.10.0) (2025-02-11)\n\nThis version requires Vue 3.0 and Pinia 3.0. See the [migration guide](https://pinia.vuejs.org/cookbook/migration-v2-v3.html) for more information.\n\n## [0.9.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.8.0...@pinia/nuxt@0.9.0) (2024-12-04)\n\nThis version requires at least Vue 2.7. On January 2025, Pinia 3.0 and `@pinia/nuxt` 1.0 will drop support for Vue 2 (which already reached EOL last year). If you need support or help migrating, you can [book help with Eduardo (@posva)](https://cal.com/posva/consultancy).\n\n## [0.8.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.7.0...@pinia/nuxt@0.8.0) (2024-11-28)\n\nThis release partially fixes a bug in production of:\n\n> vueDemi2.effectScope is not a function\n\nThe remaining part in in [nypm](https://github.com/unjs/nypm/pull/165). In the meantime, you will have to manually add `pinia` with your favorite package manager:\n\n```sh\npnpm i pinia\n```\n\n### Bug Fixes\n\n- allow module to install pinia alongside ([#2846](https://github.com/vuejs/pinia/issues/2846)) ([3e8ed69](https://github.com/vuejs/pinia/commit/3e8ed69addc30954b887241faed8778048e5d20e))\n\n# [0.7.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.6.1...@pinia/nuxt@0.7.0) (2024-11-03)\n\n### Bug Fixes\n\n- dedupe pinia ([#2821](https://github.com/vuejs/pinia/issues/2821)) ([90d8eb9](https://github.com/vuejs/pinia/commit/90d8eb900071964388c54d579ffc84c8ef01c191))\n\n## [0.6.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.6.0...@pinia/nuxt@0.6.1) (2024-10-31)\n\n### Bug Fixes\n\n- upgrade minimum version for nuxt kit ([3dab0a6](https://github.com/vuejs/pinia/commit/3dab0a6a43f00d7a52d62d53748c7d5e0cb061ea)), closes [#2814](https://github.com/vuejs/pinia/issues/2814)\n\n# [0.6.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.5...@pinia/nuxt@0.6.0) (2024-10-29)\n\n### Bug Fixes\n\n- avoid broken alias ([65031ee](https://github.com/vuejs/pinia/commit/65031ee77ed46a34bc2359223e24c7944e840819))\n- **nuxt:** ensure payload plugin declaration is generated ([#2806](https://github.com/vuejs/pinia/issues/2806)) ([99ab76c](https://github.com/vuejs/pinia/commit/99ab76c685405a93f7fc41b335fe5710e9b7fee8))\n\n### Features\n\n- **nuxt:** do not serialize skipHydrate properties ([e645fc1](https://github.com/vuejs/pinia/commit/e645fc12ea115a2d2cc395ad83e4cc3df350c4ea))\n\n## [0.5.5](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.4...@pinia/nuxt@0.5.5) (2024-09-30)\n\nNo changes in this release\n\n## [0.5.4](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.3...@pinia/nuxt@0.5.4) (2024-08-21)\n\n### Bug Fixes\n\n- use correct pinia version range ([c34c0b6](https://github.com/vuejs/pinia/commit/c34c0b67fd635ae7b8d8dab5557ec5bdfc6d2741)), closes [vuejs/pinia#2748](https://github.com/vuejs/pinia/issues/2748)\n\n## [0.5.3](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.2...@pinia/nuxt@0.5.3) (2024-08-06)\n\nThis release contain no code changes.\n\n## [0.5.2](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.2-beta.0...@pinia/nuxt@0.5.2) (2024-07-26)\n\n### Bug Fixes\n\n- **types:** require unwrapped state in patch ([c38fa0d](https://github.com/vuejs/pinia/commit/c38fa0dbae5629509bc7d1ffc999b5aedfc3d3b7))\n\n## [0.5.2-beta.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.1...@pinia/nuxt@0.5.2-beta.0) (2024-04-17)\n\n### Bug Fixes\n\n- opt in to `import.meta.*` properties ([#2622](https://github.com/vuejs/pinia/issues/2622)) ([0a94c3c](https://github.com/vuejs/pinia/commit/0a94c3c8c917a29c8e58cded33aa6f3e073f3577))\n- **types:** use declare module vue ([8a6ce86](https://github.com/vuejs/pinia/commit/8a6ce86db83b6315c067c8a98c898b3c74efe62e))\n\n## [0.5.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.1...@pinia/nuxt@0.5.1) (2024-04-04)\n\n### Bug Fixes\n\n- opt in to `import.meta.*` properties ([#2622](https://github.com/vuejs/pinia/issues/2622)) ([0a94c3c](https://github.com/vuejs/pinia/commit/0a94c3c8c917a29c8e58cded33aa6f3e073f3577))\n- **types:** use declare module vue ([8a6ce86](https://github.com/vuejs/pinia/commit/8a6ce86db83b6315c067c8a98c898b3c74efe62e))\n\n## [0.5.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.5.0...@pinia/nuxt@0.5.1) (2023-10-16)\n\n### Bug Fixes\n\n- **nuxt:** use srcDir by default for storesDirs ([dd90708](https://github.com/vuejs/pinia/commit/dd907089a688742d609bfd24c182cb7b6d6df375)), closes [#2447](https://github.com/vuejs/pinia/issues/2447)\n\n### Features\n\n- Add storeToRefs to the auto imports in @pinia/nuxt [#1876](https://github.com/vuejs/pinia/issues/1876) ([#2427](https://github.com/vuejs/pinia/issues/2427)) ([f19f368](https://github.com/vuejs/pinia/commit/f19f368a963be68940432bb0d4e0f5d74306c2d9))\n\n# [0.5.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.11...@pinia/nuxt@0.5.0) (2023-10-13)\n\n### Features\n\n- **imports:** add storesDirs auto import ([70a95ba](https://github.com/vuejs/pinia/commit/70a95ba9b1d6a55aeea72088dfedd478aa4db766)), closes [#1604](https://github.com/vuejs/pinia/issues/1604)\n\n### BREAKING CHANGES\n\n- **imports:** the option `autoImports` has been removed as it offered  \n  no value compared to the existing `imports` option in Nuxt. Instead, we\n  are automatically adding `defineStore()`, and `acceptHMRUpdate()` to the\n  list of auto imported functions. We are also adding the `./stores` dirs\n  to auto imports now, so if you were manually adding that option, it can\n  be removed.\n\n## [0.4.11](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.10...@pinia/nuxt@0.4.11) (2023-05-17)\n\nThis release only contains build related changes\n\n## [0.4.10](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.9...@pinia/nuxt@0.4.10) (2023-05-08)\n\nThis release only contains build related changes\n\n## [0.4.9](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.8...@pinia/nuxt@0.4.9) (2023-04-20)\n\n### Bug Fixes\n\n- **nuxt:** add workaround to preserve type output ([#2147](https://github.com/vuejs/pinia/issues/2147)) ([65debf9](https://github.com/vuejs/pinia/commit/65debf9be0567159b932fcd0fc445a8a2bdbaa4d))\n- **types:** typescript 5.0 acceptHMRUpdate error ([#2098](https://github.com/vuejs/pinia/issues/2098)) ([#2152](https://github.com/vuejs/pinia/issues/2152)) ([1469971](https://github.com/vuejs/pinia/commit/146997196f87abc691340fd46ae758a0865b8a73))\n\n## [0.4.8](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.7...@pinia/nuxt@0.4.8) (2023-04-07)\n\n### Bug Fixes\n\n- support \"types\" condition in \"exports\" field ([#2078](https://github.com/vuejs/pinia/issues/2078)) ([66d3a5e](https://github.com/vuejs/pinia/commit/66d3a5edd03f28f52daf35449db8c5f660c70b01))\n\n## [0.4.7](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.6...@pinia/nuxt@0.4.7) (2023-02-20)\n\nNo changes in this release\n\n## [0.4.6](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.5...@pinia/nuxt@0.4.6) (2022-11-27)\n\n### Bug Fixes\n\n- **nuxt:** Avoid recursive import from Nuxt #imports ([#1823](https://github.com/vuejs/pinia/issues/1823)) ([e1c0a19](https://github.com/vuejs/pinia/commit/e1c0a19abca2b8574c81f6f6e3d1b324924ba68d))\n- **nuxt:** use #app imports ([6cf7e48](https://github.com/vuejs/pinia/commit/6cf7e48264c575d705aeb41c978817a48e55978d))\n\n## [0.4.5](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.4...@pinia/nuxt@0.4.5) (2022-11-21)\n\n### Bug Fixes\n\n- **nuxt:** ensure pinia plugin is added before router ([3e4e63c](https://github.com/vuejs/pinia/commit/3e4e63c1f4749ee09b045a771c546de33dd4c405))\n\n## [0.4.4](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.3...@pinia/nuxt@0.4.4) (2022-11-17)\n\n### Bug Fixes\n\n- **nuxt:** nuxt v3 compatibility ([#1808](https://github.com/vuejs/pinia/issues/1808)) ([aa12958](https://github.com/vuejs/pinia/commit/aa129582306c03a186e4ba2009225d3b95feb1d9))\n\n## [0.4.3](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.2...@pinia/nuxt@0.4.3) (2022-10-08)\n\n- Update nuxt version and usage to rc.11\n\n## [0.4.2](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.1...@pinia/nuxt@0.4.2) (2022-09-06)\n\n### Bug Fixes\n\n- **nuxt:** compatibility with `^3.0.0-rc.9` ([#1623](https://github.com/vuejs/pinia/issues/1623)) ([9864beb](https://github.com/vuejs/pinia/commit/9864beb9b4b12e4fa60db521b2943cb86dbedbef))\n\n## [0.4.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.4.0...@pinia/nuxt@0.4.1) (2022-08-18)\n\n### Features\n\n- **nuxt:** automatically add pinia types ([#1551](https://github.com/vuejs/pinia/issues/1551)) ([5ebfcd8](https://github.com/vuejs/pinia/commit/5ebfcd8bbaaaacba7d03c66ac67775448fb02363))\n\n# [0.4.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.3.1...@pinia/nuxt@0.4.0) (2022-08-10)\n\n### Bug Fixes\n\n- **nuxt:** fully resolve pinia path ([#1537](https://github.com/vuejs/pinia/issues/1537)) ([9c3b521](https://github.com/vuejs/pinia/commit/9c3b5213663555b1c21f8ca5ef22b386a96ed916))\n\n## [0.3.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.3.0...@pinia/nuxt@0.3.1) (2022-07-25)\n\n- doc generation changes\n\n# [0.3.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.2.1...@pinia/nuxt@0.3.0) (2022-07-13)\n\n**Please read the release notes if you are upgrading from a previous version.**\n\n### Bug Fixes\n\n- **nuxt:** correct type for `$nuxt` ([4f1a514](https://github.com/vuejs/pinia/commit/4f1a5149a189d2f36e3c57cb5bf79eafb6544856))\n\n### Features\n\n- **nuxt:** add `autoImports` option in module ([42be2fc](https://github.com/vuejs/pinia/commit/42be2fc22aa99353821d9595061ca991d42127ff))\n- **nuxt:** deprecate old `$nuxt` context ([3e3041a](https://github.com/vuejs/pinia/commit/3e3041a84d2a1c7c4e6e62ac6c54ade949a1be94))\n- **nuxt:** remove wrong `$nuxt` in Nuxt 3 ([67e5417](https://github.com/vuejs/pinia/commit/67e5417708d1ade18f42c16f6f0085e3787d06bf))\n- usePinia composable ([c7debd6](https://github.com/vuejs/pinia/commit/c7debd692cf2034968dbaf7a72c39e621a3c5511))\n\n### BREAKING CHANGES\n\n- **nuxt:** `$nuxt` usage in stores defaults to type `any` unless\n  you install the `@nuxt/types` package. This is because that package is\n  quite heavy and can cause conflicts in projects not requiring it. Note\n  `$nuxt` is deprecated and shouldn't be used (cf the other breaking\n  changes notes).\n- **nuxt:** Starting on this version, `@pinia/nuxt` only works with\n  Nuxt 2 + Bridge and Nuxt 3, it no longer works with Nuxt 2 only. This is\n  necessary to have one single plugin that works well with the different\n  versions of Nuxt. If you aren't using bridge with Nuxt 2, check out the\n  [migration guide](https://v3.nuxtjs.org/bridge/overview) or pin your\n  `@pinia/nuxt` dependency in your:\n\n  ```diff\n  -    \"@pinia/nuxt\": \"^0.2.1\",\n  +    \"@pinia/nuxt\": \"0.2.1\",\n  ```\n\n  The `$nuxt` context usage should be replaced with globals like\n  `$fetch()` and `useNuxtApp()`. You can find more information about this\n  in Nuxt documentation:\n  - <https://v3.nuxtjs.org/bridge/bridge-composition-api/>\n  - <https://v3.nuxtjs.org/bridge/overview>\n\n- **nuxt:** in Nuxt 3, `$nuxt` is no longer available in stores.\n  This is because it was removed in Nuxt 3 and it is no longer the\n  _context_ as it used to be. Most of the features used there, like\n  `$fetch` are now globally available and therefore remove the need of it.\n  You can also use\n  [`useNuxtApp()`](https://v3.nuxtjs.org/bridge/bridge-composition-api/)\n  when necessary.\n\n## [0.2.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.2.0...@pinia/nuxt@0.2.1) (2022-07-12)\n\n### Bug Fixes\n\n- **nuxt:** add back the nuxtState ([0f68174](https://github.com/vuejs/pinia/commit/0f6817459959c28d53130ac74f8da137a5f26860)), closes [#1447](https://github.com/vuejs/pinia/issues/1447)\n- **nuxt:** use context.payload ([46775cf](https://github.com/vuejs/pinia/commit/46775cf77785102921ad233f63febf2f05102977)), closes [#1442](https://github.com/vuejs/pinia/issues/1442)\n\n# [0.2.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.9...@pinia/nuxt@0.2.0) (2022-07-11)\n\n### Bug Fixes\n\n- **nuxt:** plugin injection on latest Nuxt 3 context ([#1433](https://github.com/vuejs/pinia/issues/1433)) ([bd0c52f](https://github.com/vuejs/pinia/commit/bd0c52f75645a49226f0473d4b0d3ad1a65635c7))\n\n## [0.1.9](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.8...@pinia/nuxt@0.1.9) (2022-05-05)\n\nUpdate build tools\n\n## [0.1.8](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.7...@pinia/nuxt@0.1.8) (2021-12-24)\n\nOverride the 0.1.7 version for convenience but no code changes since 0.1.6.\n\n## [0.1.7](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.6...@pinia/nuxt@0.1.7) (2021-12-20)\n\nNo code updates in this release\n\n## [0.1.6](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.5...@pinia/nuxt@0.1.6) (2021-12-01)\n\nUpgrade dependencies\n\n## [0.1.5](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.4...@pinia/nuxt@0.1.5) (2021-11-11)\n\n### Performance Improvements\n\n- install plugins only once ([1b8e363](https://github.com/vuejs/pinia/commit/1b8e36355d52eb0934364bf622846e3bceced590))\n\n## [0.1.4](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.3...@pinia/nuxt@0.1.4) (2021-11-10)\n\n### Bug Fixes\n\n- workaround prod build bug ([e1ebadb](https://github.com/vuejs/pinia/commit/e1ebadb4892608b02d370f18296a6bae256acd05))\n\n## [0.1.3](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.2...@pinia/nuxt@0.1.3) (2021-11-10)\n\n### Bug Fixes\n\n- install vue composition api for vue 2 ([1287916](https://github.com/vuejs/pinia/commit/12879168d7e6f252f67431f1df02a9002642281e))\n\n## [0.1.2](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.1...@pinia/nuxt@0.1.2) (2021-11-04)\n\n### Bug Fixes\n\n- **nuxt:** correct build export ([b2229cc](https://github.com/vuejs/pinia/commit/b2229cc8b35ed3be453fded8a52366406b1963db))\n\n## [0.1.1](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.1.0...@pinia/nuxt@0.1.1) (2021-11-03)\n\n### Bug Fixes\n\n- **nuxt:** migrate to unbuild ([#765](https://github.com/vuejs/pinia/issues/765)) ([e8ccb71](https://github.com/vuejs/pinia/commit/e8ccb71e3ad5d1d5e55e1418e7c9f981f64c71ff))\n- **nuxt:** use `@nuxt/kit` ([#764](https://github.com/vuejs/pinia/issues/764)) ([3d6d3cb](https://github.com/vuejs/pinia/commit/3d6d3cb95e1e9adbbe22234a892c138d2a18c767))\n\n# [0.1.0](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.9...@pinia/nuxt@0.1.0) (2021-10-25)\n\n- Adapt paths of dist files\n\n## [0.0.9](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.8...@pinia/nuxt@0.0.9) (2021-10-21)\n\n### Bug Fixes\n\n- correct deps in nuxt plugin ([8020f99](https://github.com/vuejs/pinia/commit/8020f996e2b3c32685c88d0652b23a86bdcba507))\n- correct peer deps and deps ([c83677a](https://github.com/vuejs/pinia/commit/c83677a9cf7a1cb20b2e6fed529f3c5500062648))\n- should work with nuxt ([03159a4](https://github.com/vuejs/pinia/commit/03159a440de0eb2b17c3c3bd30dc64223fe90648))\n\n## [0.0.8](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.7...@pinia/nuxt@0.0.8) (2021-10-20)\n\n### Bug Fixes\n\n- expose the plugin in mjs ([7a18af7](https://github.com/vuejs/pinia/commit/7a18af7dd7d40b491f01f7da4873733d56ded431))\n\n## [0.0.7](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.6...@pinia/nuxt@0.0.7) (2021-10-19)\n\n### Bug Fixes\n\n- **nuxt3:** Rename `app` to `vueApp` ([#731](https://github.com/vuejs/pinia/issues/731)) ([f78c289](https://github.com/vuejs/pinia/commit/f78c289c8b6c2f8f7657dc46290b022df65945d0))\n- transpile pinia for nuxt 2 ([7bf2e4a](https://github.com/vuejs/pinia/commit/7bf2e4a986d707dd3864a3dfc8933df0a683251e))\n\n## [0.0.6](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.5...@pinia/nuxt@0.0.6) (2021-10-14)\n\n### Bug Fixes\n\n- link to correct file [skip ci] ([c44b771](https://github.com/vuejs/pinia/commit/c44b771b6e3f538df6c9b2aab1beca7a681f7d9a))\n\n## [0.0.5](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.4...@pinia/nuxt@0.0.5) (2021-10-14)\n\n### Bug Fixes\n\n- **nuxt:** inject pinia after installing the plugin ([32dfdec](https://github.com/vuejs/pinia/commit/32dfdeca3d6413570d113f02314f57df9fecc42e))\n- **nuxt:** inject state for nuxt 3 ([14999ce](https://github.com/vuejs/pinia/commit/14999ceefe2a326dbfb720eedd0889a2ae9e4169))\n- **nuxt:** point to the correct dist file ([438b16d](https://github.com/vuejs/pinia/commit/438b16dfd3dd5052be437d2e7382f9f4f497eea3))\n\n### Features\n\n- **nuxt:** deprecate context.pinia in favor of context.$pinia ([7dbc8e8](https://github.com/vuejs/pinia/commit/7dbc8e864afcf2636f1c9aede0480c941ab2b4ef))\n\n## [0.0.4](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.3...@pinia/nuxt@0.0.4) (2021-10-12)\n\n### Bug Fixes\n\n- **nuxt:** use latest nuxt kit ([4892546](https://github.com/vuejs/pinia/commit/4892546395654772561be7d33101dc52b03ccdeb)), closes [#717](https://github.com/vuejs/pinia/issues/717)\n\n## [0.0.3](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.2...@pinia/nuxt@0.0.3) (2021-10-03)\n\n### Bug Fixes\n\n- export the module version in mjs ([cffc313](https://github.com/vuejs/pinia/commit/cffc3134ec4d44c7a0a1492d942d44dc5d838df1))\n- **nuxt:** correct build ([435b294](https://github.com/vuejs/pinia/commit/435b2948f2290407c03b9652c7a754192e3e912f))\n\n## [0.0.2](https://github.com/vuejs/pinia/compare/@pinia/nuxt@0.0.1...@pinia/nuxt@0.0.2) (2021-09-03)\n\n### Features\n\n- add typedoc ([b98e23d](https://github.com/vuejs/pinia/commit/b98e23d5588925c6a0094a92067a3cc5784e965d))\n\n## 0.0.1 (2021-08-19)\n\n### Features\n\n- expose getActivePinia ([8b8d0c1](https://github.com/vuejs/pinia/commit/8b8d0c17958e3b4e2d9bc809c78a28931d1b00f0))\n- **nuxt:** nuxt plugin working with Vue 2 ([2b3aa5f](https://github.com/vuejs/pinia/commit/2b3aa5f4c1d83bd1955727656c8403b4e02f4b16))\n"
  },
  {
    "path": "packages/nuxt/README.md",
    "content": "# `@pinia/nuxt`\n\n> Nuxt module for Pinia\n\n## Automatic Installation\n\nUse `nuxi` to automatically add this module to your Nuxt project:\n\n```shell\nnpx nuxi@latest module add pinia\n```\n\n## Manual Installation\n\nAdd dependencies to your Nuxt project:\n\n```shell\nnpm i pinia @pinia/nuxt\n```\n\nEnable the `@pinia/nuxt` module in `nuxt.config.ts`:\n\n```js\nexport default defineNuxtConfig({\n  modules: ['@pinia/nuxt'],\n})\n```\n\n## Configuring the Module\n\nBy default, this module adds `stores` folder to auto imports, in which you can organize code related to Pinia stores in one place.\n\n> [!TIP]\n> In the new directory structure introduced since Nuxt 4, this directory is `app/stores`.\n\nYou can customize this behaviour using the `pinia` property in `nuxt.config.ts`:\n\n```js\nexport default defineNuxtConfig({\n  modules: ['@pinia/nuxt'],\n  // configure the module using `pinia` property\n  pinia: {\n    /**\n     * Automatically add stores dirs to the auto imports. This is the same as\n     * directly adding the dirs to the `imports.dirs` option. If you want to\n     * also import nested stores, you can use the glob pattern `./stores/**`\n     * (on Nuxt 3) or `app/stores/**` (on Nuxt 4+)\n     *\n     * @default `['stores']`\n     */\n    storesDirs: [],\n  },\n})\n```\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n"
  },
  {
    "path": "packages/nuxt/package.json",
    "content": "{\n  \"name\": \"@pinia/nuxt\",\n  \"version\": \"0.11.3\",\n  \"description\": \"Nuxt Module for pinia\",\n  \"keywords\": [\n    \"nuxt\",\n    \"pinia\",\n    \"store\",\n    \"vue\",\n    \"vuex\"\n  ],\n  \"homepage\": \"https://pinia.vuejs.org/ssr/nuxt.html\",\n  \"bugs\": {\n    \"url\": \"https://github.com/vuejs/pinia/issues\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Eduardo San Martin Morote\",\n    \"email\": \"posva13@gmail.com\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/vuejs/pinia.git\"\n  },\n  \"funding\": \"https://github.com/sponsors/posva\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"main\": \"./dist/module.mjs\",\n  \"types\": \"./dist/module.d.mts\",\n  \"exports\": {\n    \".\": \"./dist/module.mjs\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"pnpm run dev:prepare && nuxt-module-build build\",\n    \"dev\": \"nuxi dev playground\",\n    \"dev:build\": \"nuxi build playground\",\n    \"dev:prepare\": \"nuxt-module-build build --stub . && nuxi prepare playground\",\n    \"test:types\": \"pnpm dev:prepare && nuxi typecheck\",\n    \"changelog\": \"conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l @pinia/nuxt -r 1\"\n  },\n  \"dependencies\": {\n    \"@nuxt/kit\": \"^4.3.0\"\n  },\n  \"devDependencies\": {\n    \"@nuxt/module-builder\": \"1.0.2\",\n    \"@nuxt/schema\": \"^4.3.0\",\n    \"@nuxt/test-utils\": \"^3.23.0\",\n    \"nuxt\": \"^4.3.0\",\n    \"pinia\": \"workspace:^\",\n    \"typescript\": \"^5.9.3\",\n    \"vue-tsc\": \"^3.2.4\"\n  },\n  \"peerDependencies\": {\n    \"pinia\": \"workspace:^\"\n  }\n}\n"
  },
  {
    "path": "packages/nuxt/playground/app.vue",
    "content": "<template>\n  <NuxtPage />\n</template>\n"
  },
  {
    "path": "packages/nuxt/playground/domain/one/stores/testStore.ts",
    "content": "export const useTestStore = defineStore('test', () => {\n  // console.log('I was defined within a store directory')\n  return {}\n})\n"
  },
  {
    "path": "packages/nuxt/playground/fake-main.js",
    "content": ""
  },
  {
    "path": "packages/nuxt/playground/layers/layer-domain/nuxt.config.ts",
    "content": "export default defineNuxtConfig({})\n"
  },
  {
    "path": "packages/nuxt/playground/layers/layer-domain/stores/basic.ts",
    "content": "export const useBasicStore = defineStore('layer-basic', () => {\n  const count = ref(0)\n\n  return { count }\n})\n"
  },
  {
    "path": "packages/nuxt/playground/nuxt.config.ts",
    "content": "import { fileURLToPath } from 'node:url'\nimport { defineNuxtConfig } from 'nuxt/config'\nimport piniaModule from '../src/module'\n\nexport default defineNuxtConfig({\n  devtools: { enabled: true },\n  alias: {\n    pinia: fileURLToPath(new URL('../../pinia/src/index.ts', import.meta.url)),\n  },\n\n  modules: [piniaModule],\n\n  telemetry: {\n    enabled: false,\n  },\n\n  pinia: {\n    storesDirs: ['./stores/**', './domain/*/stores'],\n  },\n\n  vite: {\n    define: {\n      __DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'),\n      __USE_DEVTOOLS__: true,\n      __TEST__: false,\n    },\n  },\n\n  compatibilityDate: '2024-09-26',\n})\n"
  },
  {
    "path": "packages/nuxt/playground/package.json",
    "content": "{\n  \"name\": \"pinia-nuxt-playground\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"main\": \"fake-main.js\"\n}\n"
  },
  {
    "path": "packages/nuxt/playground/pages/index.vue",
    "content": "<script lang=\"ts\" setup>\n// import {useCounter }from '~/stores/counter'\n\nconst counter = useCounter()\n\nuseTestStore() // ~/domain/one/stores/testStore.ts\nuseSomeStoreStore() // ~/stores/nested/some-stores.ts\nconst layerStore = useBasicStore() // ~~/layers/layer-domain/stores/basic.ts\n\n// await useAsyncData('counter', () => counter.asyncIncrement().then(() => true))\n\nif (import.meta.server) {\n  counter.increment()\n}\n</script>\n\n<template>\n  <div>\n    <p>Count: {{ counter.count }} x 2 = {{ counter.double }}</p>\n    <button @click=\"counter.increment()\">+</button>\n    <pre>{{ counter.$state }}</pre>\n\n    <hr />\n\n    <p>Layer store: {{ layerStore.count }}</p>\n  </div>\n</template>\n"
  },
  {
    "path": "packages/nuxt/playground/pages/skip-hydrate.vue",
    "content": "<script lang=\"ts\" setup>\nconst store = useWithSkipHydrateStore()\nconst skipHydrateState = computed(() => {\n  return store.skipped?.text === 'I should not be serialized or hydrated'\n    ? 'skipHydrate-wrapped state is correct'\n    : 'skipHydrate-wrapped state is incorrect'\n})\n</script>\n\n<template>\n  <h2>skipHydrate() test</h2>\n  <p>{{ skipHydrateState }}</p>\n</template>\n"
  },
  {
    "path": "packages/nuxt/playground/stores/counter.ts",
    "content": "const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))\n\nexport const useCounter = defineStore('counter', {\n  state: () => ({\n    count: 100,\n  }),\n  actions: {\n    increment() {\n      this.count += 1\n    },\n\n    async asyncIncrement() {\n      console.log('asyncIncrement called')\n      await sleep(300)\n      this.count++\n      return true\n    },\n  },\n  getters: {\n    double: (state) => state.count * 2,\n  },\n})\n"
  },
  {
    "path": "packages/nuxt/playground/stores/nested/some-store.ts",
    "content": "export const useSomeStoreStore = defineStore('some-store', () => {\n  // console.log('I was defined within a nested store directory')\n  return {}\n})\n"
  },
  {
    "path": "packages/nuxt/playground/stores/with-skip-hydrate.ts",
    "content": "import { skipHydrate } from 'pinia'\n\nexport const useWithSkipHydrateStore = defineStore('with-skip-hydrate', () => {\n  const skipped = skipHydrate(\n    ref({\n      text: 'I should not be serialized or hydrated',\n    })\n  )\n  return { skipped }\n})\n"
  },
  {
    "path": "packages/nuxt/playground/tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "packages/nuxt/shims.d.ts",
    "content": "declare namespace NodeJS {\n  export interface Process {\n    server: boolean\n  }\n}\n"
  },
  {
    "path": "packages/nuxt/src/auto-hmr-plugin.ts",
    "content": "import type { VariableDeclarator } from 'estree'\nimport type { Plugin } from 'vite'\n\nfunction getStoreDeclaration(nodes?: VariableDeclarator[]) {\n  return nodes?.find(\n    (x) =>\n      x.init?.type === 'CallExpression' &&\n      x.init.callee.type === 'Identifier' &&\n      x.init.callee.name === 'defineStore'\n  )\n}\n\nfunction nameFromDeclaration(node?: VariableDeclarator) {\n  return node?.id.type === 'Identifier' && node.id.name\n}\n\nexport function autoRegisterHMRPlugin(rootDir: string) {\n  return {\n    name: 'pinia:auto-hmr-registration',\n\n    transform(code, id) {\n      if (id.startsWith('\\x00')) return\n      if (!id.startsWith(rootDir)) return\n      if (!code.includes('defineStore') || code.includes('acceptHMRUpdate')) {\n        return\n      }\n\n      const ast = this.parse(code)\n\n      // walk top-level nodes\n      for (const n of ast.body) {\n        if (\n          n.type === 'VariableDeclaration' ||\n          n.type === 'ExportNamedDeclaration'\n        ) {\n          // find export or variable declaration that uses `defineStore`\n          const storeDeclaration = getStoreDeclaration(\n            n.type === 'VariableDeclaration'\n              ? n.declarations\n              : n.declaration?.type === 'VariableDeclaration'\n                ? n.declaration?.declarations\n                : undefined\n          )\n\n          // retrieve the variable name\n          const storeName = nameFromDeclaration(storeDeclaration)\n          if (storeName) {\n            // append HMR code\n            return {\n              code: [\n                `import { acceptHMRUpdate } from 'pinia'`,\n                code,\n                'if (import.meta.hot) {',\n                `  import.meta.hot.accept(acceptHMRUpdate(${storeName}, import.meta.hot))`,\n                '}',\n              ].join('\\n'),\n            }\n          }\n        }\n      }\n    },\n  } satisfies Plugin\n}\n"
  },
  {
    "path": "packages/nuxt/src/module.ts",
    "content": "/**\n * @module @pinia/nuxt\n */\nimport {\n  defineNuxtModule,\n  addPlugin,\n  addImports,\n  createResolver,\n  addImportsDir,\n  getLayerDirectories,\n  addVitePlugin,\n} from '@nuxt/kit'\nimport type { NuxtModule } from '@nuxt/schema'\nimport { fileURLToPath } from 'node:url'\nimport { autoRegisterHMRPlugin } from './auto-hmr-plugin'\n\nexport interface ModuleOptions {\n  /**\n   * Automatically add stores dirs to the auto imports. This is the same as\n   * directly adding the dirs to the `imports.dirs` option. If you want to\n   * also import nested stores, you can use the glob pattern `stores/**`\n   * (on Nuxt 3) or `app/stores/**` (on Nuxt 4+)\n   *\n   * @default `['stores']`\n   */\n  storesDirs?: string[]\n}\n\nconst module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({\n  meta: {\n    name: 'pinia',\n    configKey: 'pinia',\n    compatibility: {\n      nuxt: '^3.15.0 || ^4.0.0',\n    },\n  },\n  defaults: {},\n  setup(options, nuxt) {\n    // configure transpilation\n    const { resolve } = createResolver(import.meta.url)\n    const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))\n\n    // Transpile runtime\n    nuxt.options.build.transpile.push(resolve(runtimeDir))\n\n    // avoids having multiple copies of pinia\n    nuxt.options.vite.optimizeDeps ??= {}\n    nuxt.options.vite.optimizeDeps.exclude ??= []\n    if (!nuxt.options.vite.optimizeDeps.exclude.includes('pinia')) {\n      nuxt.options.vite.optimizeDeps.exclude.push('pinia')\n    }\n\n    nuxt.hook('prepare:types', ({ references }) => {\n      references.push({ types: '@pinia/nuxt' })\n    })\n\n    // Add runtime plugin before the router plugin\n    // https://github.com/nuxt/framework/issues/9130\n    nuxt.hook('modules:done', () => {\n      addPlugin(resolve(runtimeDir, 'plugin.vue3'))\n      addPlugin(resolve(runtimeDir, 'payload-plugin'))\n    })\n\n    // Add auto imports\n    const composables = resolve(runtimeDir, 'composables')\n    addImports([\n      { from: composables, name: 'defineStore' },\n      { from: composables, name: 'acceptHMRUpdate' },\n      { from: composables, name: 'usePinia' },\n      { from: composables, name: 'storeToRefs' },\n    ])\n\n    if (!options.storesDirs) {\n      // resolve it against the src dir which is the root by default\n      options.storesDirs = [resolve(nuxt.options.srcDir, 'stores')]\n    }\n\n    if (options.storesDirs) {\n      const layers = getLayerDirectories(nuxt)\n\n      for (const storeDir of options.storesDirs) {\n        for (const layer of layers) {\n          addImportsDir(resolve(layer.app, storeDir))\n        }\n      }\n    }\n\n    // Register automatic hmr code plugin - dev mode only\n    if (nuxt.options.dev) {\n      addVitePlugin(autoRegisterHMRPlugin(resolve(nuxt.options.rootDir)))\n    }\n  },\n})\n\nexport default module\n"
  },
  {
    "path": "packages/nuxt/src/runtime/composables.ts",
    "content": "import { useNuxtApp } from '#app'\nimport type { Pinia } from 'pinia'\nexport * from 'pinia'\n\nexport const usePinia = () => useNuxtApp().$pinia as Pinia\n"
  },
  {
    "path": "packages/nuxt/src/runtime/payload-plugin.ts",
    "content": "import {\n  definePayloadPlugin,\n  definePayloadReducer,\n  definePayloadReviver,\n} from '#imports'\nimport {} from 'nuxt/app'\nimport { shouldHydrate } from 'pinia'\n\n/**\n * Removes properties marked with `skipHydrate()` to avoid sending unused data to the client.\n */\nconst payloadPlugin = definePayloadPlugin(() => {\n  definePayloadReducer(\n    'skipHydrate',\n    // We need to return something truthy to be treated as a match\n    (data: unknown) => !shouldHydrate(data) && 1\n  )\n  definePayloadReviver('skipHydrate', (_data: 1) => undefined)\n})\n\nexport default payloadPlugin\n"
  },
  {
    "path": "packages/nuxt/src/runtime/plugin.vue3.ts",
    "content": "import { createPinia, setActivePinia } from 'pinia'\nimport type { Pinia } from 'pinia'\nimport { defineNuxtPlugin, useNuxtApp, type Plugin } from '#app'\nimport { toRaw } from 'vue'\n\nconst plugin: Plugin<{ pinia: Pinia }> = defineNuxtPlugin({\n  name: 'pinia',\n  setup(nuxtApp) {\n    const pinia = createPinia()\n    nuxtApp.vueApp.use(pinia)\n    setActivePinia(pinia)\n\n    if (nuxtApp.payload && nuxtApp.payload.pinia) {\n      pinia.state.value = nuxtApp.payload.pinia as any\n    }\n\n    // Inject $pinia\n    return {\n      provide: {\n        pinia,\n      },\n    }\n  },\n  hooks: {\n    'app:rendered'() {\n      const nuxtApp = useNuxtApp()\n      nuxtApp.payload.pinia = toRaw(nuxtApp.$pinia as Pinia).state.value\n      // clear up the reference to pinia on server to avoid holding onto the variable\n      setActivePinia(undefined)\n    },\n  },\n})\n\nexport default plugin\n"
  },
  {
    "path": "packages/nuxt/test/nuxt.spec.ts",
    "content": "import { fileURLToPath } from 'node:url'\nimport { describe, it, expect } from 'vitest'\nimport { setup, $fetch } from '@nuxt/test-utils/e2e'\n\ndescribe('Nuxt', async () => {\n  await setup({\n    server: true,\n    rootDir: fileURLToPath(new URL('../playground', import.meta.url)),\n    nuxtConfig: {\n      hooks: {\n        'vite:extendConfig'(config, { isClient }) {\n          config.define!.__BROWSER__ = isClient\n        },\n      },\n      vite: {\n        define: {\n          __DEV__: 'false',\n          __TEST__: 'true',\n          __FEATURE_PROD_DEVTOOLS__: 'false',\n          __USE_DEVTOOLS__: 'false',\n        },\n      },\n    },\n  })\n\n  it('works on ssr', async () => {\n    const html = await $fetch('/')\n    expect(html).toContain('Count: 101')\n  })\n\n  it('drops state that is marked with skipHydrate', async () => {\n    const html = await $fetch('/skip-hydrate')\n    expect(html).not.toContain('I should not be serialized or hydrated')\n    expect(html).toContain('skipHydrate-wrapped state is correct')\n  })\n\n  it('can auto import from layers', async () => {\n    expect(await $fetch('/')).toContain('Layer store: 0')\n  })\n})\n"
  },
  {
    "path": "packages/nuxt/tsconfig.json",
    "content": "{\n  \"extends\": \"./playground/.nuxt/tsconfig.json\",\n  \"include\": [\n    \"./shims.d.ts\",\n    // missing in the playground\n    \"./src\"\n  ]\n}\n"
  },
  {
    "path": "packages/online-playground/README.md",
    "content": "# SFC Playground\n\nThis is continuously deployed at [https://play.vuejs.org](https://play.vuejs.org).\n\n## Run Locally in Dev\n\nIn repo root:\n\n```sh\npnpm dev-sfc\n```\n\n## Build for Prod\n\nIn repo root\n\n```sh\npnpm build-sfc-playground\n```\n"
  },
  {
    "path": "packages/online-playground/deploy-check.sh",
    "content": "#!/bin/bash\n\n# check for netlify to skip deploy\n# needed because we cannot use && in netlify.toml\n\n# exit 0 will skip the build while exit 1 will build\n\ngit diff --quiet 'HEAD^' HEAD . && git diff --quiet 'HEAD^' HEAD ../pinia\n"
  },
  {
    "path": "packages/online-playground/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link rel=\"icon\" type=\"image/svg\" href=\"/logo.svg\" />\n    <title>Pinia Playground</title>\n    <script>\n      // process shim for old versions of @vue/compiler-sfc dependency\n      window.process = { env: {} }\n      const savedPreferDark = localStorage.getItem(\n        'vue-sfc-playground-prefer-dark'\n      )\n      if (\n        savedPreferDark === 'true' ||\n        (!savedPreferDark &&\n          window.matchMedia('(prefers-color-scheme: dark)').matches)\n      ) {\n        document.documentElement.classList.add('dark')\n      }\n    </script>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/online-playground/netlify.toml",
    "content": "[build]\ncommand = \"pnpm run build\"\nignore = \"./deploy-check.sh\"\npublish = \"dist\"\n"
  },
  {
    "path": "packages/online-playground/package.json",
    "content": "{\n  \"name\": \"@pinia/playground\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"pnpm -C ../pinia run build && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@vue/repl\": \"^3.0.0\",\n    \"file-saver\": \"^2.0.5\",\n    \"jszip\": \"^3.10.1\",\n    \"pinia\": \"workspace:*\",\n    \"vue\": \"^3.5.22\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^6.0.4\",\n    \"execa\": \"^9.6.0\",\n    \"vite\": \"^7.3.1\"\n  }\n}\n"
  },
  {
    "path": "packages/online-playground/shims-vue.d.ts",
    "content": "declare module '*.vue' {\n  import { DefineComponent } from 'vue'\n  const component: DefineComponent<{}, {}, any>\n  export default component\n}\n"
  },
  {
    "path": "packages/online-playground/src/App.vue",
    "content": "<script setup lang=\"ts\">\nimport Header from './Header.vue'\nimport { Repl, ReplStore, SFCOptions, ReplProps } from '@vue/repl'\nimport Monaco from '@vue/repl/monaco-editor'\nimport { ref, watchEffect, onMounted, provide } from 'vue'\nimport { AppVue, PiniaVersionKey, counterTs } from './defaults'\n\nconst setVH = () => {\n  document.documentElement.style.setProperty('--vh', window.innerHeight + `px`)\n}\nwindow.addEventListener('resize', setVH)\nsetVH()\n\nconst useDevMode = ref(false)\n\nlet hash = location.hash.slice(1)\nif (hash.startsWith('__DEV__')) {\n  hash = hash.slice(7)\n  useDevMode.value = true\n}\n\n// TODO: we should fetch the latest version and set it by default here\nconst store = new ReplStore({\n  serializedState: hash,\n  defaultVueRuntimeURL:\n    'https://cdn.jsdelivr.net/npm/@vue/runtime-dom@3.4.21/dist/runtime-dom.esm-browser.js',\n  defaultVueServerRendererURL:\n    'https://cdn.jsdelivr.net/npm/@vue/server-renderer@3.4.21/dist/server-renderer.esm-browser.js',\n})\n\nconst previewOptions: ReplProps['previewOptions'] = {\n  customCode: {\n    importCode: `import { createPinia } from 'pinia'`,\n    useCode: `app.use(createPinia())`,\n  },\n}\n\n// enable experimental features\nconst sfcOptions: SFCOptions = {\n  script: {\n    inlineTemplate: !useDevMode.value,\n    isProd: !useDevMode.value,\n  },\n  style: {\n    isProd: !useDevMode.value,\n  },\n  template: {\n    isProd: !useDevMode.value,\n  },\n}\n\nconst piniaVersion = ref(`latest`)\nprovide(PiniaVersionKey, piniaVersion)\n\n// FIXME: cannot get autocompletion and auto import to work like it does for Vue\n// watchEffect(() => {\n//   store.state.dependencyVersion ??= {}\n//   store.state.dependencyVersion.pinia ??=\n//     piniaVersion.value === 'latest' ? '^2.1.0' : piniaVersion.value\n// })\n\n// FIXME: use a CDN that can fix the sub deps: https://play.pinia.vuejs.org/#eNp9VMFy2jAQ/RWNL8AEJJikOTCQuu3kkB7aTtOjL8ZeQMReaaQ1YYbxv3ctg0MI5GR79+3bt09a76Nv1sptBdE0mvnMaUvCA1VWFCmu5klEPokeEtSlNY7EXlQensk4ELVYOlOKnlSZqZDASfK9BBPMDHomCaB5h+8PEpyptgPz8QdBaYuUgL+EmC0qIoMizgqdvXDfUC/x5obbP2HmoATk/vuWWKKo65lqi5hgpjq2aBi1YkdlauXGG+TR9k2P5JDgiaYiRJqY1ajTJpJEKrxL8OVo4cyr55k2DB4eoTH7pHLYkjGFH6VWt2VrIuunSnGd9Gv1ARXfyy9yogq9aCBKYw67j8RsFDvp4CrnERBPxsw3/pqD9XOOj3IodTyWkzt5f8J5zFzkOyvruOJbeSvv3rNcI7iEVezZFtzIAQ/pwF036Ax4JGu46gRrPkXyfJOWenV2hpkprS7A/bak+aa9O8u0KMzrzxAjV0GnLFtD9nIhvvG7VuEfB0HQyTSUuhVQm358/gU7fu+Spcmr4uDMleRf8KaoGo0t7HuFOcs+wQW1T+FKalz98487AvTHoRqhwY2AD+7++GT0N7ns5ImLb8vJFnZbnMNS49kih8vPG9yBHCy7JHcPyw27kGx3vPsXzE8J+72wor1hK84Tb+VU9Adi/iD6B704FeMwVz3gRz2I6v/N038l\n\nif (!hash) {\n  store.setImportMap({\n    imports: {\n      ...store.getImportMap().imports,\n      ...(import.meta.env.PROD\n        ? {\n            pinia: '/pinia.esm-browser.js',\n            '@vue/devtools-api':\n              'https://cdn.jsdelivr.net/npm/@vue/devtools-api@6.6.1/lib/esm/index.js',\n            'vue-demi':\n              'https://cdn.jsdelivr.net/npm/vue-demi@0.14.7/lib/v3/index.mjs',\n          }\n        : {\n            pinia: '/src/pinia-dev-proxy',\n            vue: '/src/vue-dev-proxy',\n            'vue/server-renderer': '/src/vue-server-renderer-dev-proxy',\n          }),\n    },\n  })\n\n  store.setFiles({\n    // gets the tsconfig and import map\n    ...store.getFiles(),\n    'App.vue': AppVue,\n    'counter.ts': counterTs,\n  })\n}\n\n// persist state\nwatchEffect(() => {\n  const newHash = store\n    .serialize()\n    .replace(/^#/, useDevMode.value ? `#__DEV__` : `#`)\n  history.replaceState({}, '', newHash)\n})\n\nfunction toggleDevMode() {\n  const dev = (useDevMode.value = !useDevMode.value)\n  sfcOptions.script!.inlineTemplate =\n    sfcOptions.script!.isProd =\n    sfcOptions.template!.isProd =\n    sfcOptions.style!.isProd =\n      !dev\n  store.setFiles(store.getFiles())\n}\n\nconst theme = ref<'dark' | 'light'>('dark')\nfunction toggleTheme(isDark: boolean) {\n  theme.value = isDark ? 'dark' : 'light'\n}\n\nonMounted(() => {\n  const cls = document.documentElement.classList\n  toggleTheme(cls.contains('dark'))\n})\n</script>\n\n<template>\n  <Header\n    :store=\"store\"\n    :dev=\"useDevMode\"\n    @toggle-theme=\"toggleTheme\"\n    @toggle-dev=\"toggleDevMode\"\n  />\n  <Repl\n    :theme=\"theme\"\n    :editor=\"Monaco\"\n    @keydown.ctrl.s.prevent\n    @keydown.meta.s.prevent\n    :store=\"store\"\n    :showCompileOutput=\"true\"\n    :autoResize=\"true\"\n    :sfcOptions=\"sfcOptions\"\n    :clearConsole=\"false\"\n    :previewOptions=\"previewOptions\"\n  />\n</template>\n\n<style>\n:root {\n  --c-branding: #987a00;\n  --c-branding-dark: #5ad26c;\n\n  --color-branding: var(--c-branding);\n  --color-branding-dark: var(--c-branding-dark);\n}\n\n.dark {\n  --c-branding: #ffe166;\n\n  --color-branding: var(--c-branding);\n  --color-branding-dark: var(--c-branding-dark);\n}\n\n.dark {\n  color-scheme: dark;\n}\n\nbody {\n  font-size: 13px;\n  font-family:\n    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,\n    Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  margin: 0;\n  --base: #444;\n  --nav-height: 50px;\n}\n\n.vue-repl {\n  height: calc(var(--vh) - var(--nav-height)) !important;\n}\n\nbutton {\n  border: none;\n  outline: none;\n  cursor: pointer;\n  margin: 0;\n  background-color: transparent;\n}\n</style>\n\n<style scoped>\n.vue-repl :deep(.split-pane) {\n  --color-branding: var(--c-branding);\n  --color-branding-dark: var(--c-branding-dark);\n}\n</style>\n"
  },
  {
    "path": "packages/online-playground/src/Header.vue",
    "content": "<script setup lang=\"ts\">\nimport { downloadProject } from './download/download'\nimport { inject, ref } from 'vue'\nimport Sun from './icons/Sun.vue'\nimport Moon from './icons/Moon.vue'\nimport Share from './icons/Share.vue'\nimport Download from './icons/Download.vue'\nimport GitHub from './icons/GitHub.vue'\nimport type { ReplStore } from '@vue/repl'\nimport VersionSelect from './VersionSelect.vue'\nimport { PiniaVersionKey } from './defaults'\n\nconst props = defineProps<{\n  store: ReplStore\n  dev: boolean\n}>()\nconst emit = defineEmits(['toggle-theme', 'toggle-dev'])\n\nconst { store } = props\n\nconst currentCommit = __COMMIT__\n// parse version from the runtimeURL\nconst vueVersion = ref('latest')\nconst piniaVersion = inject(PiniaVersionKey)!\n\nasync function setVueVersion(v: string) {\n  vueVersion.value = `loading...`\n  await store.setVueVersion(v)\n  vueVersion.value = `v${v}`\n}\n\nasync function copyLink(e: MouseEvent) {\n  if (e.metaKey) {\n    // hidden logic for going to local debug from play.vuejs.org\n    window.location.href = 'http://localhost:5173/' + window.location.hash\n    return\n  }\n  await navigator.clipboard.writeText(location.href)\n  alert('Sharable URL has been copied to clipboard.')\n}\n\nfunction toggleDark() {\n  const cls = document.documentElement.classList\n  cls.toggle('dark')\n  localStorage.setItem(\n    'vue-sfc-playground-prefer-dark',\n    String(cls.contains('dark'))\n  )\n  emit('toggle-theme', cls.contains('dark'))\n}\n</script>\n\n<template>\n  <nav>\n    <h1>\n      <a href=\"https://masteringpinia.com\" target=\"_blank\">\n        <img alt=\"logo\" src=\"/logo-mp.png\" />\n        <span>Pinia Playground</span>\n      </a>\n    </h1>\n    <div class=\"links\">\n      <VersionSelect v-model=\"store.state.typescriptVersion\" pkg=\"typescript\">\n        <template #label>\n          <img src=\"/logo-ts.svg\" alt=\"TypeScript\" class=\"version-logo\" />\n        </template>\n      </VersionSelect>\n      <!-- <VersionSelect\n        :model-value=\"vueVersion\"\n        @update:model-value=\"setVueVersion\"\n        pkg=\"vue\"\n        label=\"Vue Version\"\n      >\n        <li>\n          <a @click=\"resetVueVersion\">This Commit ({{ currentCommit }})</a>\n        </li>\n        <li>\n          <a\n            href=\"https://app.netlify.com/sites/vue-sfc-playground/deploys\"\n            target=\"_blank\"\n            >Commits History</a\n          >\n        </li>\n      </VersionSelect> -->\n      <VersionSelect\n        :model-value=\"store.vueVersion || 'latest'\"\n        @update:model-value=\"setVueVersion\"\n        pkg=\"vue\"\n      >\n        <template #label>\n          <img src=\"/logo-vue.svg\" alt=\"Vue\" class=\"version-logo\" />\n        </template>\n      </VersionSelect>\n\n      <button\n        title=\"Toggle development production mode\"\n        class=\"toggle-dev\"\n        :class=\"{ dev }\"\n        @click=\"$emit('toggle-dev')\"\n      >\n        <span>{{ dev ? 'DEV' : 'PROD' }}</span>\n      </button>\n      <button title=\"Toggle dark mode\" class=\"toggle-dark\" @click=\"toggleDark\">\n        <Sun class=\"light\" />\n        <Moon class=\"dark\" />\n      </button>\n      <button title=\"Copy sharable URL\" class=\"share\" @click=\"copyLink\">\n        <Share />\n      </button>\n      <button\n        title=\"Download project files\"\n        class=\"download\"\n        @click=\"downloadProject(store)\"\n      >\n        <Download />\n      </button>\n      <a\n        href=\"https://github.com/vuejs/pinia/tree/v3/packages/online-playground\"\n        target=\"_blank\"\n        title=\"View on GitHub\"\n        class=\"github\"\n      >\n        <GitHub />\n      </a>\n    </div>\n  </nav>\n</template>\n\n<style>\nnav {\n  --bg: #fff;\n  --bg-light: #fff;\n  --border: #ddd;\n  --btn: #666;\n  --highlight: #333;\n  --green: #3ca877;\n  --purple: #904cbc;\n  --btn-bg: #eee;\n\n  color: var(--base);\n  height: var(--nav-height);\n  box-sizing: border-box;\n  padding: 0 1em;\n  background-color: var(--bg);\n  box-shadow: 0 0 4px rgba(0, 0, 0, 0.33);\n  position: relative;\n  z-index: 999;\n  display: flex;\n  justify-content: space-between;\n}\n\n.dark nav {\n  --base: #ddd;\n  --bg: #1a1a1a;\n  --bg-light: #242424;\n  --border: #383838;\n  --highlight: #fff;\n  --btn-bg: #333;\n\n  box-shadow: none;\n  border-bottom: 1px solid var(--border);\n}\n\nh1 {\n  font-weight: 500;\n  display: inline-flex;\n  place-items: center;\n}\n\nh1 a {\n  color: var(--color-branding);\n  text-decoration: none;\n}\n\nh1 img {\n  height: 24px;\n  margin-right: 10px;\n\n  animation: hithere 4s ease 5;\n  /* animation-delay: 5s; */\n}\n@keyframes hithere {\n  78% {\n    transform: scale(1);\n  }\n  79% {\n    transform: scale(1.2);\n  }\n  82%,\n  86% {\n    transform: rotate(-20deg) scale(1.2);\n  }\n  85% {\n    transform: rotate(20deg) scale(1.2);\n  }\n  91% {\n    transform: rotate(0deg) scale(1.2);\n  }\n  100% {\n    transform: scale(1);\n  }\n}\n\n@media (max-width: 570px) {\n  h1 span {\n    display: none;\n  }\n}\n\n@media (max-width: 770px) {\n  btn.download {\n    display: none;\n  }\n}\n\n.links {\n  display: flex;\n}\n\n.toggle-dev span {\n  font-size: 12px;\n  border-radius: 4px;\n  padding: 4px 6px;\n}\n\n.toggle-dev span {\n  background: var(--purple);\n  color: #fff;\n}\n\n.toggle-dev.dev span {\n  background: var(--green);\n}\n\n.toggle-dark svg {\n  width: 18px;\n  height: 18px;\n}\n\n.toggle-dark .dark,\n.dark .toggle-dark .light {\n  display: none;\n}\n\n.dark .toggle-dark .dark {\n  display: inline-block;\n}\n\n.links button,\n.links .github {\n  padding: 1px 6px;\n  color: var(--btn);\n}\n\n.links button:hover,\n.links .github:hover {\n  color: var(--highlight);\n}\n\n.version:hover .active-version::after {\n  border-top-color: var(--btn);\n}\n\n.dark .version:hover .active-version::after {\n  border-top-color: var(--highlight);\n}\n\n.versions {\n  display: none;\n  position: absolute;\n  left: 0;\n  top: 40px;\n  background-color: var(--bg-light);\n  border: 1px solid var(--border);\n  border-radius: 4px;\n  list-style-type: none;\n  padding: 8px;\n  margin: 0;\n  width: 200px;\n  max-height: calc(100vh - 70px);\n  overflow: scroll;\n}\n\n.versions a {\n  display: block;\n  padding: 6px 12px;\n  text-decoration: none;\n  cursor: pointer;\n  color: var(--base);\n}\n\n.versions a:hover {\n  color: var(--color-branding);\n}\n\n.versions.expanded {\n  display: block;\n}\n\n.links > * {\n  display: flex;\n  align-items: center;\n}\n\n.links > * + * {\n  margin-left: 4px;\n}\n\n.version-logo {\n  height: 1.2em;\n  margin-right: 4px;\n}\n</style>\n"
  },
  {
    "path": "packages/online-playground/src/VersionSelect.vue",
    "content": "<script setup lang=\"ts\">\nimport { onMounted, ref } from 'vue'\n\nconst expanded = ref(false)\nconst versions = ref<string[]>()\n\nconst version = defineModel()\nconst props = defineProps<{\n  pkg: string\n  label?: string\n}>()\n\nasync function toggle() {\n  expanded.value = !expanded.value\n  if (!versions.value) {\n    versions.value = await fetchVersions()\n  }\n}\n\nasync function fetchVersions(): Promise<string[]> {\n  const res = await fetch(\n    `https://data.jsdelivr.com/v1/package/npm/${props.pkg}`\n  )\n  const { versions } = (await res.json()) as { versions: string[] }\n\n  // TODO:\n  if (props.pkg === 'pinia') {\n    // if the latest version is a pre-release, list all current pre-releases\n    // otherwise filter out pre-releases\n    let isInPreRelease = versions[0].includes('-')\n    const filteredVersions: string[] = []\n    for (const v of versions) {\n      if (v.includes('-')) {\n        if (isInPreRelease) {\n          filteredVersions.push(v)\n        }\n      } else {\n        filteredVersions.push(v)\n        isInPreRelease = false\n      }\n      if (filteredVersions.length >= 30 || v === '3.0.10') {\n        break\n      }\n    }\n    return filteredVersions\n  } else if (props.pkg === 'vue') {\n    return versions\n      .filter(\n        (v) =>\n          v.startsWith('3.') &&\n          !v.includes('-alpha') &&\n          !v.includes('-beta') &&\n          !v.includes('-rc')\n      )\n      .slice(0, 30)\n  } else if (props.pkg === 'typescript') {\n    return versions.filter((v) => !v.includes('dev') && !v.includes('insiders'))\n  }\n  return versions\n}\n\nfunction setVersion(v: string) {\n  version.value = v\n  expanded.value = false\n}\n\nonMounted(() => {\n  window.addEventListener('click', () => {\n    expanded.value = false\n  })\n  window.addEventListener('blur', () => {\n    if (document.activeElement?.tagName === 'IFRAME') {\n      expanded.value = false\n    }\n  })\n})\n</script>\n\n<template>\n  <div class=\"version\" @click.stop>\n    <span class=\"active-version\" @click=\"toggle\">\n      <slot name=\"label\">\n        {{ label }}\n      </slot>\n      <span class=\"number\">{{ version }}</span>\n    </span>\n\n    <ul class=\"versions\" :class=\"{ expanded }\">\n      <li v-if=\"!versions\"><a>loading versions...</a></li>\n      <li v-for=\"version of versions\">\n        <a @click=\"setVersion(version)\">v{{ version }}</a>\n      </li>\n      <div @click=\"expanded = false\">\n        <slot />\n      </div>\n    </ul>\n  </div>\n</template>\n\n<style>\n.version {\n  margin-right: 12px;\n  position: relative;\n}\n\n.active-version {\n  cursor: pointer;\n  position: relative;\n  display: inline-flex;\n  place-items: center;\n}\n\n.active-version .number {\n  margin-left: 4px;\n}\n\n.active-version::after {\n  content: '';\n  width: 0;\n  height: 0;\n  border-left: 4px solid transparent;\n  border-right: 4px solid transparent;\n  border-top: 6px solid #aaa;\n  margin-left: 8px;\n}\n</style>\n"
  },
  {
    "path": "packages/online-playground/src/defaults.ts",
    "content": "import { InjectionKey, Ref } from 'vue'\n\nexport const AppVue = `\n<script setup lang=\"ts\">\nimport { useStore } from './counter.ts'\n\nconst store = useStore()\n</script>\n\n<template>\n  <button @click=\"store.n++\">Increment {{ store.n }}</button>\n</template>\n`.trimStart()\n\nexport const counterTs = `\nimport { defineStore } from 'pinia'\nimport { ref } from 'vue'\n\nexport const useStore = defineStore('counter', () => {\n  const n = ref(0)\n\n  return { n }\n})\n`.trimStart()\n\nexport const PiniaVersionKey: InjectionKey<Ref<string>> =\n  Symbol('pinia-version')\n"
  },
  {
    "path": "packages/online-playground/src/download/download.ts",
    "content": "import { saveAs } from 'file-saver'\n\nimport index from './template/index.html?raw'\nimport main from './template/main.js?raw'\nimport pkg from './template/package.json?raw'\nimport config from './template/vite.config.js?raw'\nimport readme from './template/README.md?raw'\nimport { ReplStore } from '@vue/repl'\n\nexport async function downloadProject(store: ReplStore) {\n  if (!confirm('Download project files?')) {\n    return\n  }\n\n  const { default: JSZip } = await import('jszip')\n  const zip = new JSZip()\n\n  // basic structure\n  zip.file('index.html', index)\n  zip.file('package.json', pkg)\n  zip.file('vite.config.js', config)\n  zip.file('README.md', readme)\n\n  // project src\n  const src = zip.folder('src')!\n  src.file('main.js', main)\n\n  const files = store.getFiles()\n  for (const file in files) {\n    if (file !== 'import-map.json') {\n      src.file(file, files[file])\n    } else {\n      zip.file(file, files[file])\n    }\n  }\n\n  const blob = await zip.generateAsync({ type: 'blob' })\n  saveAs(blob, 'vue-project.zip')\n}\n"
  },
  {
    "path": "packages/online-playground/src/download/template/README.md",
    "content": "# Vite Vue Starter\n\nThis is a project template using [Vite](https://vitejs.dev/). It requires [Node.js](https://nodejs.org) v12+.\n\nTo start:\n\n```sh\nnpm install\nnpm run dev\n\n# if using yarn:\nyarn\nyarn dev\n```\n"
  },
  {
    "path": "packages/online-playground/src/download/template/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/online-playground/src/download/template/main.js",
    "content": "import { createApp } from 'vue'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "packages/online-playground/src/download/template/package.json",
    "content": "{\n  \"name\": \"vite-vue-starter\",\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"pinia\": \"workspace:*\",\n    \"vue\": \"^3.3.0\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^4.3.4\",\n    \"vite\": \"^4.4.9\"\n  }\n}\n"
  },
  {
    "path": "packages/online-playground/src/download/template/vite.config.js",
    "content": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "packages/online-playground/src/icons/Download.vue",
    "content": "<template>\n  <svg width=\"1.7em\" height=\"1.7em\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n    <g>\n      <rect x=\"4\" y=\"18\" width=\"16\" height=\"2\" rx=\"1\" ry=\"1\" />\n      <rect\n        x=\"3\"\n        y=\"17\"\n        width=\"4\"\n        height=\"2\"\n        rx=\"1\"\n        ry=\"1\"\n        transform=\"rotate(-90 5 18)\"\n      />\n      <rect\n        x=\"17\"\n        y=\"17\"\n        width=\"4\"\n        height=\"2\"\n        rx=\"1\"\n        ry=\"1\"\n        transform=\"rotate(-90 19 18)\"\n      />\n      <path\n        d=\"M12 15a1 1 0 0 1-.58-.18l-4-2.82a1 1 0 0 1-.24-1.39a1 1 0 0 1 1.4-.24L12 12.76l3.4-2.56a1 1 0 0 1 1.2 1.6l-4 3a1 1 0 0 1-.6.2z\"\n      />\n      <path d=\"M12 13a1 1 0 0 1-1-1V4a1 1 0 0 1 2 0v8a1 1 0 0 1-1 1z\" />\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "packages/online-playground/src/icons/GitHub.vue",
    "content": "<template>\n  <svg width=\"1.7em\" height=\"1.7em\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n    <path\n      d=\"M10.9,2.1c-4.6,0.5-8.3,4.2-8.8,8.7c-0.5,4.7,2.2,8.9,6.3,10.5C8.7,21.4,9,21.2,9,20.8v-1.6c0,0-0.4,0.1-0.9,0.1 c-1.4,0-2-1.2-2.1-1.9c-0.1-0.4-0.3-0.7-0.6-1C5.1,16.3,5,16.3,5,16.2C5,16,5.3,16,5.4,16c0.6,0,1.1,0.7,1.3,1c0.5,0.8,1.1,1,1.4,1 c0.4,0,0.7-0.1,0.9-0.2c0.1-0.7,0.4-1.4,1-1.8c-2.3-0.5-4-1.8-4-4c0-1.1,0.5-2.2,1.2-3C7.1,8.8,7,8.3,7,7.6C7,7.2,7,6.6,7.3,6 c0,0,1.4,0,2.8,1.3C10.6,7.1,11.3,7,12,7s1.4,0.1,2,0.3C15.3,6,16.8,6,16.8,6C17,6.6,17,7.2,17,7.6c0,0.8-0.1,1.2-0.2,1.4 c0.7,0.8,1.2,1.8,1.2,3c0,2.2-1.7,3.5-4,4c0.6,0.5,1,1.4,1,2.3v2.6c0,0.3,0.3,0.6,0.7,0.5c3.7-1.5,6.3-5.1,6.3-9.3 C22,6.1,16.9,1.4,10.9,2.1z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "packages/online-playground/src/icons/Moon.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n    <path\n      fill=\"currentColor\"\n      d=\"M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "packages/online-playground/src/icons/Share.vue",
    "content": "<template>\n  <svg width=\"1.4em\" height=\"1.4em\" viewBox=\"0 0 24 24\">\n    <g\n      fill=\"none\"\n      stroke=\"currentColor\"\n      stroke-width=\"2\"\n      stroke-linecap=\"round\"\n      stroke-linejoin=\"round\"\n    >\n      <circle cx=\"18\" cy=\"5\" r=\"3\" />\n      <circle cx=\"6\" cy=\"12\" r=\"3\" />\n      <circle cx=\"18\" cy=\"19\" r=\"3\" />\n      <path d=\"M8.59 13.51l6.83 3.98\" />\n      <path d=\"M15.41 6.51l-6.82 3.98\" />\n    </g>\n  </svg>\n</template>\n"
  },
  {
    "path": "packages/online-playground/src/icons/Sun.vue",
    "content": "<template>\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n    <path\n      fill=\"currentColor\"\n      d=\"M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z\"\n    />\n    <path\n      fill=\"currentColor\"\n      d=\"M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z\"\n    />\n  </svg>\n</template>\n"
  },
  {
    "path": "packages/online-playground/src/main.ts",
    "content": "import { createApp } from 'vue'\nimport App from './App.vue'\nimport '@vue/repl/style.css'\n\n// @ts-expect-error Custom window property\nwindow.VUE_DEVTOOLS_CONFIG = {\n  defaultSelectedAppId: 'repl',\n}\n\ncreateApp(App).mount('#app')\n"
  },
  {
    "path": "packages/online-playground/src/pinia-dev-proxy.ts",
    "content": "// serve pinia to the iframe sandbox during dev.\nexport * from 'pinia'\n"
  },
  {
    "path": "packages/online-playground/src/vue-dev-proxy.ts",
    "content": "// serve vue to the iframe sandbox during dev.\nexport * from 'vue'\n"
  },
  {
    "path": "packages/online-playground/src/vue-server-renderer-dev-proxy.ts",
    "content": "// serve vue/server-renderer to the iframe sandbox during dev.\nexport * from 'vue/server-renderer'\n"
  },
  {
    "path": "packages/online-playground/tsconfig.json",
    "content": "{\n  \"files\": [\"src/**/*.ts\"],\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"Bundler\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"sourceMap\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"types\": [\"vite/client\"],\n    \"paths\": {\n      \"pinia\": [\"../pinia/src/index.ts\"]\n    }\n  }\n  // \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"]\n}\n"
  },
  {
    "path": "packages/online-playground/vite.config.ts",
    "content": "import fs from 'node:fs'\nimport path from 'node:path'\nimport { defineConfig, Plugin } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { execaSync } from 'execa'\n\nconst commit = execaSync('git', ['rev-parse', '--short', 'HEAD'])\n\nexport default defineConfig({\n  plugins: [\n    vue({\n      script: {\n        defineModel: true,\n        fs: {\n          fileExists: fs.existsSync,\n          readFile: (file) => fs.readFileSync(file, 'utf-8'),\n        },\n      },\n    }),\n    copyPiniaPlugin(),\n  ],\n  define: {\n    __COMMIT__: JSON.stringify(commit),\n    __VUE_PROD_DEVTOOLS__: JSON.stringify(true),\n  },\n  optimizeDeps: {\n    exclude: ['@vue/repl'],\n  },\n})\n\nfunction copyPiniaPlugin(): Plugin {\n  return {\n    name: 'copy-pinia',\n    generateBundle() {\n      const copyFile = (file: string) => {\n        const filePath = path.resolve(__dirname, file)\n        const basename = path.basename(file)\n        if (!fs.existsSync(filePath)) {\n          throw new Error(`${basename} not built. ` + `Run \"nr build`)\n        }\n        this.emitFile({\n          type: 'asset',\n          fileName: basename,\n          source: fs.readFileSync(filePath, 'utf-8'),\n        })\n      }\n\n      copyFile(`../pinia/dist/pinia.esm-browser.js`)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/pinia/.gitignore",
    "content": ""
  },
  {
    "path": "packages/pinia/CHANGELOG.md",
    "content": "## [3.0.4](https://github.com/vuejs/pinia/compare/v3.0.3...v3.0.4) (2025-11-05)\n\n### Features\n\n- **warn:** detect global context on the server side ([#2983](https://github.com/vuejs/pinia/issues/2983)) ([be9e356](https://github.com/vuejs/pinia/commit/be9e356117b249a940647dad170669b49489ecff))\n\n### Bug Fixes\n\n- incorrect supported values in package.json ([5cc55c2](https://github.com/vuejs/pinia/commit/5cc55c2e0bb42ef4c0db1c5201184a495db7d2f8))\n\n## [3.0.3](https://github.com/vuejs/pinia/compare/v3.0.2...v3.0.3) (2025-06-04)\n\nNo code changes.\n\n## [3.0.2](https://github.com/vuejs/pinia/compare/v3.0.1...v3.0.2) (2025-04-09)\n\n### Bug Fixes\n\n- fix `obj.hasOwnProperty` in `shouldHydrate`\n\n## [3.0.1](https://github.com/vuejs/pinia/compare/v3.0.0...v3.0.1) (2025-02-12)\n\n### Bug Fixes\n\n- avoid including devtools code in builds ([d3b24a3](https://github.com/vuejs/pinia/commit/d3b24a3d6a4b5af82c8ef7e66e4cecd890c30fdd)), closes [#2910](https://github.com/vuejs/pinia/issues/2910)\n\n# [3.0.0](https://github.com/vuejs/pinia/compare/v2.3.1...v3.0.0) (2025-02-11)\n\nThis version of Pinia has no new features, it drops support for Vue 2 and other deprecated APIs. It should be a straightforward upgrade for most users! 🎉\n\nSee the [migration guide](https://pinia.vuejs.org/cookbook/migration-v2-v3.html) for help.\n\n### ⚠ BREAKING CHANGES\n\n- We now use the native `Awaited` introduced in TS 4.5. This shouldn't affect you.\n- `PiniaStorePlugin` is now removed. Use `PiniaPlugin` instead.\n- `defineStore({ id: 'id' })` is now removed. Use `defineStore('id')` instead\n- The new version of Vue Devtools is too large to be included in the iife version and has been removed. It must now be included manually (depending on your workflow)\n\n### Code Refactoring\n\n- remove deprecated alias ([87c6182](https://github.com/vuejs/pinia/commit/87c6182c4bf61e1f96a4877eb884fd59cf824e1f))\n- remove internal type `_Awaited` ([ce48ec4](https://github.com/vuejs/pinia/commit/ce48ec46e0d7626eeefa0ee9c4e8c6b65fce31e1))\n- remove support for `id` as a property in `defineStore` ([24b2b89](https://github.com/vuejs/pinia/commit/24b2b89c7be4ffda8b6fbc35155757f5780971d8))\n\n## [2.3.1](https://github.com/vuejs/pinia/compare/v2.3.0...v2.3.1) (2025-01-20)\n\n### Bug Fixes\n\n- **types:** support for Vue 2.7 ([d14e1a7](https://github.com/vuejs/pinia/commit/d14e1a723e5f19cfa89f439d2f0444cc4f5f6dfc))\n\n# [2.3.0](https://github.com/vuejs/pinia/compare/v2.2.8...v2.3.0) (2024-12-04)\n\nThis version requires at least Vue 2.7. On January 2025, Pinia 3.0 will drop support for Vue 2 (which already reached EOL last year). If you need support or help migrating, you can [book help with Eduardo (@posva)](https://cal.com/posva/consultancy).\n\n### Features\n\n- writable `computed`s to be picked up by `mapWritableState` ([#2847](https://github.com/vuejs/pinia/issues/2847)) ([0fa633e](https://github.com/vuejs/pinia/commit/0fa633e81864b09d300859a0ed1c10d2a89affa8))\n\n### Bug Fixes\n\n- avoid npm bug when resolving optional deps ([#2841](https://github.com/vuejs/pinia/issues/2841)) ([1e45f33](https://github.com/vuejs/pinia/commit/1e45f332efe8c0f543cfd186cd26b768abdf2b62))\n\n## [2.2.8](https://github.com/vuejs/pinia/compare/v2.2.7...v2.2.8) (2024-11-28)\n\n### Features\n\n- deprecate old defineStore ([d1858e8](https://github.com/vuejs/pinia/commit/d1858e8c932d89cd2bf9121fe62179795ebb5c5f))\n\n### Bug Fixes\n\n- avoid immediate computing with `storeToRefs` ([67d3109](https://github.com/vuejs/pinia/commit/67d31094784cc3bd256b0636b79dc8e421f6c3fb)), closes [#2812](https://github.com/vuejs/pinia/issues/2812)\n- **types:** unwrap refs in `mapWritableState` for setup stores ([#2805](https://github.com/vuejs/pinia/issues/2805)) ([ea14e53](https://github.com/vuejs/pinia/commit/ea14e53fdfc0d0f4cd80d5242572f87714a77e3b)), closes [#2804](https://github.com/vuejs/pinia/issues/2804)\n\n## [2.2.7](https://github.com/vuejs/pinia/compare/pinia@2.2.6...v2.2.7) (2024-11-27)\n\n### Bug Fixes\n\n- **devtools:** avoid running outside of browsers ([eb5e6fd](https://github.com/vuejs/pinia/commit/eb5e6fd6073da8e828a9087c876d0e8fde3cdb3d)), closes [#2843](https://github.com/vuejs/pinia/issues/2843)\n\n## [2.2.6](https://github.com/vuejs/pinia/compare/pinia@2.2.5...pinia@2.2.6) (2024-11-03)\n\nNo code changes in this release\n\n## [2.2.5](https://github.com/vuejs/pinia/compare/pinia@2.2.4...pinia@2.2.5) (2024-10-29)\n\n### Bug Fixes\n\n- keep no side effect comment when minifying ([a31fb87](https://github.com/vuejs/pinia/commit/a31fb877aed1843fc144d09488e2b45afc917bf9))\n- reference the store directly in storeToRefs to ensure correct reactivity after HMR ([#2795](https://github.com/vuejs/pinia/issues/2795)) ([254eec7](https://github.com/vuejs/pinia/commit/254eec764ecccd120ab9dbc31352d76ffd6ecfa3))\n- **types:** handle union types in generic parameter ([#2794](https://github.com/vuejs/pinia/issues/2794)) ([ecc7449](https://github.com/vuejs/pinia/commit/ecc74492c642539ffc7386d57a8f9853437be327)), closes [#2785](https://github.com/vuejs/pinia/issues/2785)\n- up minimum peer dep of Vue ([5404d3e](https://github.com/vuejs/pinia/commit/5404d3e472ed0ac8ba0f09f501618908fe438c57)), closes [#2797](https://github.com/vuejs/pinia/issues/2797)\n\n### Features\n\n- **nuxt:** do not serialize skipHydrate properties ([e645fc1](https://github.com/vuejs/pinia/commit/e645fc12ea115a2d2cc395ad83e4cc3df350c4ea))\n\n## [2.2.4](https://github.com/vuejs/pinia/compare/pinia@2.2.3...pinia@2.2.4) (2024-10-01)\n\n### Bug Fixes\n\n- **types:** allow writable getters with storeToRefs ([b464a1f](https://github.com/vuejs/pinia/commit/b464a1f4ff499aff34087cc9cd77ad19cf8646a7)), closes [#2767](https://github.com/vuejs/pinia/issues/2767)\n\n## [2.2.3](https://github.com/vuejs/pinia/compare/pinia@2.2.2...pinia@2.2.3) (2024-09-30)\n\n### Bug Fixes\n\n- **types:** allow writable getters ([94f5a63](https://github.com/vuejs/pinia/commit/94f5a63fdc86f17f1dd17ed16534fbdecb8c448f)), closes [#2767](https://github.com/vuejs/pinia/issues/2767)\n- **types:** Don't double UnwrapRef in setup stores ([#2771](https://github.com/vuejs/pinia/issues/2771)) ([5ad1765](https://github.com/vuejs/pinia/commit/5ad17654de4153b6f26b45b20029ca9ac4885f8d)), closes [#2770](https://github.com/vuejs/pinia/issues/2770)\n- **types:** storeToRefs with nested refs ([#2659](https://github.com/vuejs/pinia/issues/2659)) ([623e5a0](https://github.com/vuejs/pinia/commit/623e5a0fe3444a4aa9c4908668927ee66f6352ba))\n\n## [2.2.2](https://github.com/vuejs/pinia/compare/pinia@2.2.1...pinia@2.2.2) (2024-08-15)\n\n### Features\n\n- improve tree shaking on `defineStore` ([#2740](https://github.com/vuejs/pinia/issues/2740)) ([3069105](https://github.com/vuejs/pinia/commit/3069105f15858393e5386bac7e62ea32a46c80bb))\n\n## [2.2.1](https://github.com/vuejs/pinia/compare/pinia@2.2.0...pinia@2.2.1) (2024-08-06)\n\n### Bug Fixes\n\n- **types:** breaking type with auto imported components ([#2730](https://github.com/vuejs/pinia/issues/2730)) ([82ca41c](https://github.com/vuejs/pinia/commit/82ca41c7d0f15439356c032bc12a4f825a290af6))\n\n# [2.2.0](https://github.com/vuejs/pinia/compare/pinia@2.1.8-beta.0...pinia@2.2.0) (2024-07-26)\n\n### Bug Fixes\n\n- **types:** require unwrapped state in patch ([c38fa0d](https://github.com/vuejs/pinia/commit/c38fa0dbae5629509bc7d1ffc999b5aedfc3d3b7))\n\n### Features\n\n- add `action` helper to consistently `$onAction` ([a8526fc](https://github.com/vuejs/pinia/commit/a8526fc78894423ee87c3059228cd97097e37f27))\n- **devtools:** expose selected store as global variable ([#2692](https://github.com/vuejs/pinia/issues/2692)) ([e0a7351](https://github.com/vuejs/pinia/commit/e0a73512af38050111ecbcf881908541aff9414d))\n\n## [2.1.8-beta.0](https://github.com/vuejs/pinia/compare/pinia@2.1.7...pinia@2.1.8-beta.0) (2024-04-17)\n\n### Bug Fixes\n\n- **devtools:** Do not patch mocked actions ([#2300](https://github.com/vuejs/pinia/issues/2300)) ([069ffd1](https://github.com/vuejs/pinia/commit/069ffd14a52ccb576d63d03d06b18dee69185ae7))\n- support webpack minification ([57914b5](https://github.com/vuejs/pinia/commit/57914b522fb901701c397a5ef62dad3339ee0cf9)), closes [#1143](https://github.com/vuejs/pinia/issues/1143)\n- **types:** fix storeToRefs state return type ([#2574](https://github.com/vuejs/pinia/issues/2574)) ([#2604](https://github.com/vuejs/pinia/issues/2604)) ([c8f727a](https://github.com/vuejs/pinia/commit/c8f727a0a2187c591134bd467efe426fb747ea40))\n- **types:** mapHelpers with getters types ([#2571](https://github.com/vuejs/pinia/issues/2571)) ([#2576](https://github.com/vuejs/pinia/issues/2576)) ([ea5c974](https://github.com/vuejs/pinia/commit/ea5c974c73d458518aff318dbab369e506db9285))\n- **types:** use declare module vue ([8a6ce86](https://github.com/vuejs/pinia/commit/8a6ce86db83b6315c067c8a98c898b3c74efe62e))\n\n### Features\n\n- disposePinia ([bb8bf60](https://github.com/vuejs/pinia/commit/bb8bf60581747c76e42796b82eb3aa04e2336fdf)), closes [vuejs/pinia#2453](https://github.com/vuejs/pinia/issues/2453)\n\n## [2.1.7](https://github.com/vuejs/pinia/compare/pinia@2.1.7...pinia@2.1.7) (2024-04-04)\n\n### Bug Fixes\n\n- support webpack minification ([57914b5](https://github.com/vuejs/pinia/commit/57914b522fb901701c397a5ef62dad3339ee0cf9)), closes [#1143](https://github.com/vuejs/pinia/issues/1143)\n- **types:** fix storeToRefs state return type ([#2574](https://github.com/vuejs/pinia/issues/2574)) ([#2604](https://github.com/vuejs/pinia/issues/2604)) ([c8f727a](https://github.com/vuejs/pinia/commit/c8f727a0a2187c591134bd467efe426fb747ea40))\n- **types:** mapHelpers with getters types ([#2571](https://github.com/vuejs/pinia/issues/2571)) ([#2576](https://github.com/vuejs/pinia/issues/2576)) ([ea5c974](https://github.com/vuejs/pinia/commit/ea5c974c73d458518aff318dbab369e506db9285))\n- **types:** use declare module vue ([8a6ce86](https://github.com/vuejs/pinia/commit/8a6ce86db83b6315c067c8a98c898b3c74efe62e))\n\n### Features\n\n- disposePinia ([bb8bf60](https://github.com/vuejs/pinia/commit/bb8bf60581747c76e42796b82eb3aa04e2336fdf)), closes [vuejs/pinia#2453](https://github.com/vuejs/pinia/issues/2453)\n\n## [2.1.7](https://github.com/vuejs/pinia/compare/pinia@2.1.6...pinia@2.1.7) (2023-10-13)\n\n### Bug Fixes\n\n- **devtools:** correctly load initial states ([9d49e30](https://github.com/vuejs/pinia/commit/9d49e3005ca677f8892e7af209e96d43b41e121c))\n\n### Features\n\n- **types:** SetupStoreDefinition ([391f9ac](https://github.com/vuejs/pinia/commit/391f9ac4f5e845afdd4b7d44fb1a9c7fb2b299a0))\n- **warn:** improve getActivePinia warning ([4640f09](https://github.com/vuejs/pinia/commit/4640f09d12094d8c2c2d9d2039b4479fd7d38d35))\n\n## [2.1.6](https://github.com/vuejs/pinia/compare/pinia@2.1.5...pinia@2.1.6) (2023-07-26)\n\n### Bug Fixes\n\n- **devtools:** preserve store reactivity ([709ed3b](https://github.com/vuejs/pinia/commit/709ed3b3a4787c3a9b5a59f863558b480a96eaaf))\n\n## [2.1.5](https://github.com/vuejs/pinia/compare/pinia@2.1.4...pinia@2.1.5) (2023-07-26)\n\n### Bug Fixes\n\n- **devtools:** correctly load the state ([beff091](https://github.com/vuejs/pinia/commit/beff091f80302def494305779f8aa1d12301bce4))\n- **devtools:** wrong toast message ([#2290](https://github.com/vuejs/pinia/issues/2290)) ([dfc04d3](https://github.com/vuejs/pinia/commit/dfc04d356c86128f868b12182615bd57228111d6))\n\n## [2.1.4](https://github.com/vuejs/pinia/compare/pinia@2.1.3...pinia@2.1.4) (2023-06-14)\n\n### Bug Fixes\n\n- **devtools:** group setup store sync actions mutations ([683efe1](https://github.com/vuejs/pinia/commit/683efe1a34cbca82c1ef19d268e3080503401de2))\n\n## [2.1.3](https://github.com/vuejs/pinia/compare/pinia@2.1.2...pinia@2.1.3) (2023-05-18)\n\n### Bug Fixes\n\n- **types:** revert declare module vue ([3000161](https://github.com/vuejs/pinia/commit/3000161205e66838d872e514a9e90f73a1b37039))\n\n## [2.1.2](https://github.com/vuejs/pinia/compare/pinia@2.1.1...pinia@2.1.2) (2023-05-18)\n\n- Force vue-demi version\n\n## [2.1.1](https://github.com/vuejs/pinia/compare/pinia@2.1.0...pinia@2.1.1) (2023-05-17)\n\n### Bug Fixes\n\n- expect Vue 3.3 ([b8fb165](https://github.com/vuejs/pinia/commit/b8fb1653211dbe6dc6aa3a8ac36185bf93bcfa25))\n\n# [2.1.0](https://github.com/vuejs/pinia/compare/pinia@2.0.36...pinia@2.1.0) (2023-05-17)\n\n‼️ This **requires** Vue 3.3 or latest vue-demi (for Vue 2)\n\n### Bug Fixes\n\n- **types:** use declare module vue ([b7f97dd](https://github.com/vuejs/pinia/commit/b7f97ddf160e999da7b639ba4fcaf554bed5f1e2))\n\n### Features\n\n- allow app injections in setup stores ([6a71019](https://github.com/vuejs/pinia/commit/6a71019ef2a633dcb097a63be895a8b1714863b6)), closes [#1784](https://github.com/vuejs/pinia/issues/1784)\n- **devtools:** allow resetting setup stores from inspector ([971dcdb](https://github.com/vuejs/pinia/commit/971dcdbf4b825e1791d02a85d6788fa72236107c)), closes [#2189](https://github.com/vuejs/pinia/issues/2189)\n\n### Reverts\n\n- Revert \"chore: tmp upgrade to beta\" ([2337130](https://github.com/vuejs/pinia/commit/2337130b0f11a3218a972555b84df6b2c055e6dc))\n\n## [2.0.36](https://github.com/vuejs/pinia/compare/pinia@2.0.35...pinia@2.0.36) (2023-05-08)\n\n### Features\n\n- **dx:** throw an error if store id is missing ([#2167](https://github.com/vuejs/pinia/issues/2167)) ([b74eb4f](https://github.com/vuejs/pinia/commit/b74eb4f9b5a2a20a9c8d3cedd221eea8dde201f6))\n- **warn:** improve warning message ([73518b3](https://github.com/vuejs/pinia/commit/73518b3f94fb164d02e740cb3132767a0d2a397b))\n\n## [2.0.35](https://github.com/vuejs/pinia/compare/pinia@2.0.34...pinia@2.0.35) (2023-04-20)\n\n### Bug Fixes\n\n- **types:** typescript 5.0 acceptHMRUpdate error ([#2098](https://github.com/vuejs/pinia/issues/2098)) ([#2152](https://github.com/vuejs/pinia/issues/2152)) ([1469971](https://github.com/vuejs/pinia/commit/146997196f87abc691340fd46ae758a0865b8a73))\n\n### Features\n\n- **types:** improve setActivePinia types ([1650c6e](https://github.com/vuejs/pinia/commit/1650c6efebc75fdc47b2ec082ba10c197b894aef))\n\n## [2.0.34](https://github.com/vuejs/pinia/compare/pinia@2.0.33...pinia@2.0.34) (2023-04-07)\n\nNo changes in this release\n\n## [2.0.33](https://github.com/vuejs/pinia/compare/pinia@2.0.32...pinia@2.0.33) (2023-03-06)\n\n### Bug Fixes\n\n- allow `$reset` to be overridden by plugins ([#2054](https://github.com/vuejs/pinia/issues/2054)) ([709e2b1](https://github.com/vuejs/pinia/commit/709e2b1a77410331ee1ce88212ac55a4e92fa941))\n\n## [2.0.32](https://github.com/vuejs/pinia/compare/pinia@2.0.31...pinia@2.0.32) (2023-02-21)\n\n### Bug Fixes\n\n- **types:** mapWritableState array ([a7ad90d](https://github.com/vuejs/pinia/commit/a7ad90d319c4fad29f40622a6b95e605c47377f9)), closes [#2014](https://github.com/vuejs/pinia/issues/2014)\n\n## [2.0.31](https://github.com/vuejs/pinia/compare/pinia@2.0.30...pinia@2.0.31) (2023-02-20)\n\n### Bug Fixes\n\n- **types:** mapWritableState array ([07eaf99](https://github.com/vuejs/pinia/commit/07eaf99a566ef5de4173b782504a163bd276e12e)), closes [#2014](https://github.com/vuejs/pinia/issues/2014)\n\n## [2.0.30](https://github.com/vuejs/pinia/compare/pinia@2.0.29...pinia@2.0.30) (2023-02-01)\n\n### Bug Fixes\n\n- avoid spread operator even in devtools code ([d2a4def](https://github.com/vuejs/pinia/commit/d2a4defc381ae8e023b45b05e4ac8588fe2add9e)), closes [#1885](https://github.com/vuejs/pinia/issues/1885)\n\n## [2.0.29](https://github.com/vuejs/pinia/compare/pinia@2.0.28...pinia@2.0.29) (2023-01-15)\n\n### Bug Fixes\n\n- **types:** type storeToRefs getters as ComputedRef ([#1898](https://github.com/vuejs/pinia/issues/1898)) ([dcf7ef0](https://github.com/vuejs/pinia/commit/dcf7ef0d3a6773da97c5cdde4b9a958492a15d7d))\n\n## [2.0.28](https://github.com/vuejs/pinia/compare/pinia@2.0.27...pinia@2.0.28) (2022-12-09)\n\n### Bug Fixes\n\n- avoid missing injection not found warn in edge Vue 2 edge case ([#1849](https://github.com/vuejs/pinia/issues/1849)) ([78ec9a1](https://github.com/vuejs/pinia/commit/78ec9a186dcbce3d583db332ae22094a182358cc)), closes [#1650](https://github.com/vuejs/pinia/issues/1650)\n\n## [2.0.27](https://github.com/vuejs/pinia/compare/pinia@2.0.26...pinia@2.0.27) (2022-11-27)\n\n- api docs changes\n\n## [2.0.26](https://github.com/vuejs/pinia/compare/pinia@2.0.25...pinia@2.0.26) (2022-11-23)\n\n### Bug Fixes\n\n- **types:** support older ts versions ([78fb214](https://github.com/vuejs/pinia/commit/78fb21409b731f4822b076e83b31c234e75c722d)), closes [#1818](https://github.com/vuejs/pinia/issues/1818)\n\n## [2.0.25](https://github.com/vuejs/pinia/compare/pinia@2.0.24...pinia@2.0.25) (2022-11-21)\n\n### Bug Fixes\n\n- **types:** implemented a workaround to be TS 4.9.x compatible ([#1818](https://github.com/vuejs/pinia/issues/1818)) ([c42a54c](https://github.com/vuejs/pinia/commit/c42a54c5d6bf20ca4b1c619da44f18be3c84db87))\n\n## [2.0.24](https://github.com/vuejs/pinia/compare/pinia@2.0.23...pinia@2.0.24) (2022-11-17)\n\n## [2.0.23](https://github.com/vuejs/pinia/compare/pinia@2.0.22...pinia@2.0.23) (2022-10-08)\n\n### Bug Fixes\n\n- **devtools:** init `_customProperties` for devtools ([#1704](https://github.com/vuejs/pinia/issues/1704)) ([8c1dfce](https://github.com/vuejs/pinia/commit/8c1dfce2e67c37e09251a7b85fcae2d6a4030fb8))\n\n## [2.0.22](https://github.com/vuejs/pinia/compare/pinia@2.0.21...pinia@2.0.22) (2022-09-06)\n\n### Features\n\n- **ssr:** handle Maps and Sets ([f9843eb](https://github.com/vuejs/pinia/commit/f9843eb589ea9752f9021f9ebcfc49f6659350d2)), closes [#1608](https://github.com/vuejs/pinia/issues/1608)\n\n## [2.0.21](https://github.com/vuejs/pinia/compare/pinia@2.0.20...pinia@2.0.21) (2022-08-26)\n\n### Bug Fixes\n\n- **build:** remove problematic browser export ([6efa780](https://github.com/vuejs/pinia/commit/6efa780f2c6e401e1d82a80a2aeceeac962f1c4e)), closes [#1593](https://github.com/vuejs/pinia/issues/1593)\n\n## [2.0.20](https://github.com/vuejs/pinia/compare/pinia@2.0.19...pinia@2.0.20) (2022-08-19)\n\n- **build**: support vue 2 devtools flag\n\n## [2.0.19](https://github.com/vuejs/pinia/compare/pinia@2.0.18...pinia@2.0.19) (2022-08-18)\n\n### Bug Fixes\n\n- **devtools:** use flag to include devtools ([4e92c36](https://github.com/vuejs/pinia/commit/4e92c360a4b4b8a39b2e3c284f31560120742b6f))\n\n## [2.0.18](https://github.com/vuejs/pinia/compare/pinia@2.0.17...pinia@2.0.18) (2022-08-10)\n\n### Bug Fixes\n\n- **ie:** completely skip devtools in dev for IE ([ca73db9](https://github.com/vuejs/pinia/commit/ca73db964b56205d43aa798e10d5c5bb5c4ea847)), closes [#1440](https://github.com/vuejs/pinia/issues/1440)\n\n## [2.0.17](https://github.com/vuejs/pinia/compare/pinia@2.0.16...pinia@2.0.17) (2022-07-25)\n\n### Bug Fixes\n\n- **devtools:** state formatting ([b01f5c2](https://github.com/vuejs/pinia/commit/b01f5c2a5cb62cfd3e0df9da5570925f3a6f077a)), closes [#1358](https://github.com/vuejs/pinia/issues/1358)\n- setupStore getter types ([#1430](https://github.com/vuejs/pinia/issues/1430)) ([#1444](https://github.com/vuejs/pinia/issues/1444)) ([6be93f2](https://github.com/vuejs/pinia/commit/6be93f2301a7711873f49c94999913947e3896b8))\n\n### Features\n\n- **devtools:** allow resetting fromp pinia inspector ([cee0e16](https://github.com/vuejs/pinia/commit/cee0e16e0ea343f97fa6b96f80ff32380637110b))\n\n## [2.0.16](https://github.com/vuejs/pinia/compare/pinia@2.0.15...pinia@2.0.16) (2022-07-12)\n\n### Bug Fixes\n\n- add missing require in exports ([96c0dbc](https://github.com/vuejs/pinia/commit/96c0dbc66ee166e6bd11be60c8486f239d83af40))\n\n## [2.0.15](https://github.com/vuejs/pinia/compare/pinia@2.0.14...pinia@2.0.15) (2022-07-11)\n\n### Features\n\n- warn when a getter conflicts with the state ([#1356](https://github.com/vuejs/pinia/issues/1356)) ([667b81d](https://github.com/vuejs/pinia/commit/667b81dfd7888bbae562bbd02c3670d4c664a8e2))\n\n## [2.0.14](https://github.com/vuejs/pinia/compare/pinia@2.0.13...pinia@2.0.14) (2022-05-05)\n\n### Bug Fixes\n\n- avoid multiple subscriptions with empty promises ([6c17168](https://github.com/vuejs/pinia/commit/6c17168b03307f257491dbd6c0962d5bfade02cc)), closes [#1129](https://github.com/vuejs/pinia/issues/1129)\n- correctly detect option stores ([11b92fd](https://github.com/vuejs/pinia/commit/11b92fd9e4b1d2402df79de9fc47c32c1b9ce726)), closes [#1272](https://github.com/vuejs/pinia/issues/1272)\n- **devtools:** remove in tests environment ([4aeb0a5](https://github.com/vuejs/pinia/commit/4aeb0a539f7389a628e6c32bb6849ce3cd50cc17))\n\n## [2.0.13](https://github.com/vuejs/pinia/compare/pinia@2.0.12...pinia@2.0.13) (2022-03-31)\n\n### Bug Fixes\n\n- avoid prototype pollution ([e4858f9](https://github.com/vuejs/pinia/commit/e4858f9d5f447ba6162ca9f2472608a8bac3eca7))\n- **vue2:** use toRefs in storeToRefs ([0f24ad2](https://github.com/vuejs/pinia/commit/0f24ad27f16bd473e20a8671cd24877d2603cbcf)), closes [#852](https://github.com/vuejs/pinia/issues/852)\n\n### Features\n\n- update devtools-api ([5334222](https://github.com/vuejs/pinia/commit/53342222c536b0dc122f491525ca1c5ad8d25a05))\n\n## [2.0.12](https://github.com/vuejs/pinia/compare/pinia@2.0.11...pinia@2.0.12) (2022-03-14)\n\n### Bug Fixes\n\n- **devtools:** avoid error in getters ([a64c19d](https://github.com/vuejs/pinia/commit/a64c19d33ed60dac1b1c1f98cf2055615a45f6d8)), closes [#1062](https://github.com/vuejs/pinia/issues/1062)\n- **types:** exclude internal properties from store ([f8f944f](https://github.com/vuejs/pinia/commit/f8f944fcdd4411c2f84297786955c4c157fbeebe)), closes [#1013](https://github.com/vuejs/pinia/issues/1013)\n\n### Features\n\n- **devtools:** allow disable logs ([43f690f](https://github.com/vuejs/pinia/commit/43f690f10c35b2c309cd091225379afc0479ad93))\n- **devtools:** use api.now() ([836ab86](https://github.com/vuejs/pinia/commit/836ab865b48f09df5dc848e69ad14597cbaaf21e))\n- up vue-devtools ([e8e5f28](https://github.com/vuejs/pinia/commit/e8e5f28e08a37ed1d30e680df95ce6f3307775ed))\n- **warn:** avoid vue 2 bug storeToRefs() ([f692fdf](https://github.com/vuejs/pinia/commit/f692fdfe623389f3d85c90e5a923c5cfb15c0b0b)), closes [#852](https://github.com/vuejs/pinia/issues/852)\n\n## [2.0.11](https://github.com/vuejs/pinia/compare/pinia@2.0.10...pinia@2.0.11) (2022-01-30)\n\n### Bug Fixes\n\n- **types:** custom Awaited for TS 4.x ([7fcb62e](https://github.com/vuejs/pinia/commit/7fcb62e6fc77e273daf5396d68a9b17126d3ea35)), closes [#1006](https://github.com/vuejs/pinia/issues/1006)\n\n## [2.0.10](https://github.com/vuejs/pinia/compare/pinia@2.0.9...pinia@2.0.10) (2022-01-27)\n\n### Bug Fixes\n\n- check HTMLAnchorElement in saveAs for mini-program ([#966](https://github.com/vuejs/pinia/issues/966)) ([#967](https://github.com/vuejs/pinia/issues/967)) ([85daefb](https://github.com/vuejs/pinia/commit/85daefb7c3325d3cd258de9812cb117c393b8f99))\n- **subscriptions:** allow removing subscriptions inside them ([#990](https://github.com/vuejs/pinia/issues/990)) ([465d222](https://github.com/vuejs/pinia/commit/465d22292a0086eb610f5f83edd64a795eba329b))\n- **types:** custom Awaited for TS 4.x ([7c51126](https://github.com/vuejs/pinia/commit/7c51126d5b59b0c1a693df7c4a93bd4cf309b79b)), closes [#957](https://github.com/vuejs/pinia/issues/957)\n\n## [2.0.9](https://github.com/vuejs/pinia/compare/pinia@2.0.8...pinia@2.0.9) (2021-12-24)\n\n### Features\n\n- **types:** support IDE features for store context ([#924](https://github.com/vuejs/pinia/issues/924)) ([4733f49](https://github.com/vuejs/pinia/commit/4733f49c5af400aea7d3d1002265f9cdebcdd222))\n\n## [2.0.8](https://github.com/vuejs/pinia/compare/pinia@2.0.7...pinia@2.0.8) (2021-12-20)\n\n### Bug Fixes\n\n- **subscribe:** avoid $subscriptions with $patch ([3bfe9e5](https://github.com/vuejs/pinia/commit/3bfe9e51fd0f9b3f939aace53463aa10f2e9c90f)), closes [#908](https://github.com/vuejs/pinia/issues/908)\n\n## [2.0.7](https://github.com/vuejs/pinia/compare/pinia@2.0.6...pinia@2.0.7) (2021-12-20)\n\n### Bug Fixes\n\n- allow using multiple `$onAction`, **ignore returned value** ([4dc1f1b](https://github.com/vuejs/pinia/commit/4dc1f1bec8f3db508c02fa6bdcc8203f280cbb3e)), closes [#893](https://github.com/vuejs/pinia/issues/893)\n- **subscribe:** direct mutation should not trigger detached subscriptions ([a9ef6b6](https://github.com/vuejs/pinia/commit/a9ef6b6c96ae8fdf891e29ab05c5276773425e1c)), closes [#908](https://github.com/vuejs/pinia/issues/908)\n\n### Features\n\n- update devtools-api ([ca26686](https://github.com/vuejs/pinia/commit/ca266868c5c5a00f3bebb326e9a0b9aaf90fb344))\n\n## [2.0.6](https://github.com/vuejs/pinia/compare/pinia@2.0.5...pinia@2.0.6) (2021-12-04)\n\n### Bug Fixes\n\n- downgrade peerdep requirement for ts ([100a60d](https://github.com/vuejs/pinia/commit/100a60d648e246cbc67ffe63105e1a5b3bcb9b44)), closes [#874](https://github.com/vuejs/pinia/issues/874)\n\n## [2.0.5](https://github.com/vuejs/pinia/compare/pinia@2.0.4...pinia@2.0.5) (2021-12-01)\n\n### Bug Fixes\n\n- accept reactive with storeToRefs ([3a2a334](https://github.com/vuejs/pinia/commit/3a2a334110dadb94cc1dddc10bd7673aa79b358f)), closes [#799](https://github.com/vuejs/pinia/issues/799)\n- shouldHydrate if not in skipHydrateMap ([#846](https://github.com/vuejs/pinia/issues/846)) ([bcc44bc](https://github.com/vuejs/pinia/commit/bcc44bcedf2f127cf0218db4a89bb16b028a4ffc))\n\n## [2.0.4](https://github.com/vuejs/pinia/compare/pinia@2.0.3...pinia@2.0.4) (2021-11-19)\n\n### Features\n\n- **devtools:** allow resetting directly from devtools ([44fa896](https://github.com/vuejs/pinia/commit/44fa896213e855bb611e30f25b9cd7ffe4a1643e))\n- **devtools:** display all getters in pinia root ([ce8f1e5](https://github.com/vuejs/pinia/commit/ce8f1e5b87fa21c09b044be965e34d48b50a129b))\n\n## [2.0.3](https://github.com/vuejs/pinia/compare/pinia@2.0.2...pinia@2.0.3) (2021-11-10)\n\n- Updated peer deps for composition api and vue devtools\n\n## [2.0.2](https://github.com/vuejs/pinia/compare/pinia@2.0.1...pinia@2.0.2) (2021-11-03)\n\n### Bug Fixes\n\n- **types:** for devtools-api ([d856d5d](https://github.com/vuejs/pinia/commit/d856d5df5d8e7402749d0dc135a349e84f5c9e9d))\n- **types:** remove dependency on Vue 3 only Plugin type ([ee358a6](https://github.com/vuejs/pinia/commit/ee358a6428bf1f34e7c00415be56da9aecc739b0))\n\n## [2.0.1](https://github.com/vuejs/pinia/compare/pinia@2.0.0...pinia@2.0.1) (2021-11-03)\n\nThis release correctly removes the deprecated APIs as advertised in v2. The [documentation](https://pinia.vuejs.org/cookbook/migration-v1-v2.html) contains a list of all the deprecations compared to v0.x.\n\n### Bug Fixes\n\n- use assign instead of spread for older browsers ([51cf9b6](https://github.com/vuejs/pinia/commit/51cf9b6e5a61d668a5e27b5e02833607ad0b5907))\n\n### Features\n\n- **warn:** improve getActivePinia warn ([6a0a209](https://github.com/vuejs/pinia/commit/6a0a209faf42b149bcda1dd373e0b1e5bf426ce4))\n\n# [2.0.0](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.15...pinia@2.0.0) (2021-10-27)\n\n🎉 🎉 🎉\n\n### Bug Fixes\n\n- **devtools:** root store access [#732](https://github.com/vuejs/pinia/issues/732) ([90d2c35](https://github.com/vuejs/pinia/commit/90d2c35d94ccb6e3991b99b181bfe3b360cfa289))\n- **plugins:** ensure plugins are used only once ([#745](https://github.com/vuejs/pinia/issues/745)) ([150fdfc](https://github.com/vuejs/pinia/commit/150fdfc8abe2577046af947b2e4fbbde2efb057e))\n- **ssr:** make skipHydrate compatible with @vue/composition-api ([71448b0](https://github.com/vuejs/pinia/commit/71448b008fc4517ab0036b98121a0346df75880e))\n\n### BREAKING CHANGES\n\nAll deprecated API have been removed.\n\n# [2.0.0-rc.15](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.14...pinia@2.0.0-rc.15) (2021-10-25)\n\n### Bug Fixes\n\n- **types:** remove unused option hydrate for setup stores ([37d07fb](https://github.com/vuejs/pinia/commit/37d07fb29ef2885d94cae3b3f212cea83772a073))\n\n### Code Refactoring\n\n- **ssr:** pass storeState instead of store to hydrate ([c85edac](https://github.com/vuejs/pinia/commit/c85edacefc8c69a54e84afbcd577b8d1027b3065))\n\n### Features\n\n- **ssr:** add skipHydrate to skip hydration on specific refs ([55deedb](https://github.com/vuejs/pinia/commit/55deedbc492a26ab98d96ed40ddfdf6ebac45aae))\n\n### BREAKING CHANGES\n\n- **ssr:** the `hydrate()` option for stores defined with the\n  options api no longers passes the whole store instance. Instead, it\n  passes the `storeState` so it can be directly modified. This is because\n  it was currently necessary to hydrate the store by setting properties\n  onto `store.$state`. This change makes it impossible to make the mistake\n  anymore.\n\n```diff\n defineStore('main', {\n   state: () => ({\n     customRef: useLocalStorage('key', 0)\n   }),\n-  hydrate(store) {\n-    store.$state.customRef = useLocalStorage('key', 0)\n+  hydrate(storeState) {\n+    storeState.customRef = useLocalStorage('key', 0)\n   }\n })\n```\n\n# [2.0.0-rc.14](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.13...pinia@2.0.0-rc.14) (2021-10-19)\n\nReadme update\n\n# [2.0.0-rc.13](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.12...pinia@2.0.0-rc.13) (2021-10-12)\n\n- bump vue-devtools-api version\n\n# [2.0.0-rc.12](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.11...pinia@2.0.0-rc.12) (2021-10-07)\n\n### Features\n\n- proper check of computed requiring @vue/composition-api@1.2.3 ([b099ef4](https://github.com/vuejs/pinia/commit/b099ef4ee143ba9cf44e08c1eb607de395563020))\n- **warn:** log store id with class constructor warning ([#702](https://github.com/vuejs/pinia/issues/702)) ([39eee6a](https://github.com/vuejs/pinia/commit/39eee6a48b5d84f916e90b513024e26e9c6d72c5))\n\n# [2.0.0-rc.11](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.10...pinia@2.0.0-rc.11) (2021-10-03)\n\n### Bug Fixes\n\n- **build:** expose mjs correctly ([2e9fe33](https://github.com/vuejs/pinia/commit/2e9fe33647f9b649e53841ec54f0df048835c1ba))\n- export the module version in mjs ([cffc313](https://github.com/vuejs/pinia/commit/cffc3134ec4d44c7a0a1492d942d44dc5d838df1))\n- **types:** correctly type global extensions ([cdbdba5](https://github.com/vuejs/pinia/commit/cdbdba5153198cde6b678cb96ab7948b351fd3cc)), closes [#630](https://github.com/vuejs/pinia/issues/630)\n- **warn:** avoid toRefs warning for Vue 2 ([c174fe1](https://github.com/vuejs/pinia/commit/c174fe1dfa48569b1fc9e04f105832ab9a8e3824)), closes [#648](https://github.com/vuejs/pinia/issues/648)\n\n# [2.0.0-rc.10](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.9...pinia@2.0.0-rc.10) (2021-09-30)\n\n### Bug Fixes\n\n- **ssr:** always call setActivePinia ([83d7d2f](https://github.com/vuejs/pinia/commit/83d7d2f4cabedf9c65ba90f2e18882261c49f71f)), closes [#665](https://github.com/vuejs/pinia/issues/665)\n- use assign to set $state ([f3a732f](https://github.com/vuejs/pinia/commit/f3a732f86fbd0399f7b7ebc6a762a2425d08bb4c)), closes [#682](https://github.com/vuejs/pinia/issues/682)\n- fix mjs, cjs versions for webpack based builds\n\n### Features\n\n- **warn:** incorrect state value [#641](https://github.com/vuejs/pinia/issues/641) ([#646](https://github.com/vuejs/pinia/issues/646)) ([6fd3883](https://github.com/vuejs/pinia/commit/6fd3883100ccc5c11668c3b855ff0660dd8af9fe))\n\n# [2.0.0-rc.9](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.8...pinia@2.0.0-rc.9) (2021-09-12)\n\n### Bug Fixes\n\n- correct store in getters vue 2 ([3d4080b](https://github.com/vuejs/pinia/commit/3d4080b503292f1d711daa51fad204c5f8db223d))\n- **vue2:** delay getters until stores are ready when cross using them ([ed48b00](https://github.com/vuejs/pinia/commit/ed48b0093c2a58caf8bb4548cceb13eeaf5f1378))\n- **vue2:** fix isComputed check for getters ([307078b](https://github.com/vuejs/pinia/commit/307078bb7a485ec01ff50fdcd58138433662ade0))\n\n# [2.0.0-rc.8](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.7...pinia@2.0.0-rc.8) (2021-09-06)\n\n### Bug Fixes\n\n- correctly set the store properties in Vue 2 ([9e40309](https://github.com/vuejs/pinia/commit/9e40309e5bfc54f5f71178cf90d37ebcf8dd8dca)), closes [#657](https://github.com/vuejs/pinia/issues/657)\n\n# [2.0.0-rc.7](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.6...pinia@2.0.0-rc.7) (2021-09-03)\n\n### Bug Fixes\n\n- **ssr:** properly hydrate setup stores ([4fbacfc](https://github.com/vuejs/pinia/commit/4fbacfcd87362515902e3f98fd53a51a39216b9f))\n\n### Features\n\n- add typedoc ([b98e23d](https://github.com/vuejs/pinia/commit/b98e23d5588925c6a0094a92067a3cc5784e965d))\n- allow stores to be cross used ([cda3658](https://github.com/vuejs/pinia/commit/cda365875c599e9786ab3479d42e8e6b3bb0fc23))\n- deprecate PiniaPlugin in favor of PiniaVuePlugin ([c0495c0](https://github.com/vuejs/pinia/commit/c0495c0fe5710894ff04009f1f136cfb4d9241c4))\n- support TS 4.4 ([#656](https://github.com/vuejs/pinia/issues/656)) ([39b2e15](https://github.com/vuejs/pinia/commit/39b2e15c0a2782280bd0de44230d2f3dc624b3c6))\n\n# [2.0.0-rc.6](https://github.com/vuejs/pinia/compare/pinia@2.0.0-rc.5...pinia@2.0.0-rc.6) (2021-08-19)\n\nFix missing types.\n\n# [2.0.0-rc.5](https://github.com/vuejs/pinia/compare/v2.0.0-rc.4...v2.0.0-rc.5) (2021-08-19)\n\n### Bug Fixes\n\n- **ssr:** convert hydrated state to refs ([3f186a2](https://github.com/vuejs/pinia/commit/3f186a28e954cd4ccb48e4f26448a96cb0a0d7e1)), closes [#619](https://github.com/vuejs/pinia/issues/619)\n\n### Features\n\n- destroy a store with $dispose ([#597](https://github.com/vuejs/pinia/issues/597)) ([a563e6a](https://github.com/vuejs/pinia/commit/a563e6abd1e58e6bf810987dd520f754987c32d8))\n- expose getActivePinia ([8b8d0c1](https://github.com/vuejs/pinia/commit/8b8d0c17958e3b4e2d9bc809c78a28931d1b00f0))\n- **testing:** add testing package ([fc05376](https://github.com/vuejs/pinia/commit/fc053763752c2b11d7b851f95334034a1f9b8347))\n\n# [2.0.0-rc.4](https://github.com/vuejs/pinia/compare/v2.0.0-rc.3...v2.0.0-rc.4) (2021-08-09)\n\nIf you are using Vue 2, make sure your `@vue/composition-api` version is at least `1.1.0`, which is currently under the npm dist tag `next`, which means it has to be installed with `npm install @vue/composition-api@next`.\n\n### Bug Fixes\n\n- **types:** unwrap computed in store getters ([35d4f59](https://github.com/vuejs/pinia/commit/35d4f591cf48166466f4d4e414de8063d55e3811)), closes [#602](https://github.com/vuejs/pinia/issues/602) [#603](https://github.com/vuejs/pinia/issues/603)\n\n# [2.0.0-rc.3](https://github.com/vuejs/pinia/compare/v2.0.0-rc.2...v2.0.0-rc.3) (2021-08-05)\n\n### Bug Fixes\n\n- set initial state in prod ([f8e3c83](https://github.com/vuejs/pinia/commit/f8e3c83a4c7be7bf537a5d6ffca97408263bc9a0)), closes [#598](https://github.com/vuejs/pinia/issues/598)\n\n# [2.0.0-rc.2](https://github.com/vuejs/pinia/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2021-08-04)\n\nThis version supports Vue 2! [Here](https://github.com/vuejs/pinia-vue-2-vite-example) is an example using Vue 2 and Vite for an optimal DX. **Note this version requires Vue Devtools 6**, and more specifically, they don't work with the current `@vue/devtools-api` (`6.0.0-beta.15`) because they require [this unreleased fix](https://github.com/vuejs/devtools/commit/3db47027d81c1701d2ddfe1dd86bae0d7ce63cef). To get all the goodness pinia has to offer **for Vue 2**, you will need to clone `vuejs/devtools`, run `yarn && yarn run build` and then _load an unpacked extension_ on a Chromium browser (after activating the developer mode in the extension panel). If you are using Vue 3, you can still use the Vue Devtools 6 regularly.\n\n### Bug Fixes\n\n- **devtools:** grouping of actions ([3d760f1](https://github.com/vuejs/pinia/commit/3d760f1c78936666174c1a352314081bccf11b01))\n- **devtools:** reflect changes on HMR ([aebc9a0](https://github.com/vuejs/pinia/commit/aebc9a0969bec40c06b28f96ff0d1d048f589f31))\n\n### Features\n\n- add support for Vue 2 ([e1ea1c8](https://github.com/vuejs/pinia/commit/e1ea1c8563816dd99963aae778c03335d0577266))\n- enable devtools with Vue 2 ([08cdff5](https://github.com/vuejs/pinia/commit/08cdff5be7415f8c635fe9431cb32931950e5fcb))\n\n# [2.0.0-rc.1](https://github.com/vuejs/pinia/compare/v2.0.0-rc.0...v2.0.0-rc.1) (2021-07-30)\n\nPosted <https://github.com/vuejs/pinia/issues/592> to help people installing or upgrading Pinia.\n\n### Bug Fixes\n\n- collect reactive effects ran in plugins ([54cee00](https://github.com/vuejs/pinia/commit/54cee009cf15a5086ad031da65278a7689230587))\n- **devtools:** update when custom properties change ([7dcb71e](https://github.com/vuejs/pinia/commit/7dcb71e8182900c451a56f1ad9e0e931dba48dcb))\n- **store:** keep original refs with $reset ([a7dadff](https://github.com/vuejs/pinia/commit/a7dadfff8aae4abb83696a47904b030295408a09)), closes [#593](https://github.com/vuejs/pinia/issues/593)\n\n# [2.0.0-rc.0](https://github.com/vuejs/pinia/compare/v2.0.0-beta.5...v2.0.0-rc.0) (2021-07-28)\n\n## Required Vue version ‼️\n\nThis release requires Vue 3.2.0, which is currently only available under the `beta` dist tag (`npm i vue@beta` or `yarn add vue@beta` + the corresponding packages like `@vue/compiler-sfc@beta`).\n\nFollow the instructions at <https://github.com/vuejs/pinia/issues/592> if you need help updating your package versions.\n\nIt contains major improvements:\n\n- Performance: Pinia now uses `effectScope()`, effectively reducing memory consumption and removing the drawbacks mentioned in the Plugin section about `useStore()` creating multiple store instances (still sharing the state).\n- Devtools: Many improvements over the information displayed in devtools as well as a few bugfixes\n- HMR (Hot Module Replacement): You can now modify your stores without reloading the page and losing the state, making development much easier. Until 3.2.0 (stable) is released, you can find an example [in the playground](https://github.com/vuejs/pinia/blob/2b98eafe441ea7e9a3ff3cef122c24eb5fa03f1d/playground/src/stores/counter.ts#L66-L68). After that, you can read up to date instructions [in the documentation](https://pinia.vuejs.org/cookbook/hot-module-replacement.html).\n- Setup syntax: You can now define stores with a function instead of options. This enables more complex patterns. See an example [in the playground](https://github.com/vuejs/pinia/blob/75f1fe6aa4ef2629ae1c9840a2d4542ac6e62686/playground/src/stores/jokes-swrv.ts). Setup Stores are unable to group actions like Option Stores due to their very permissive syntax.\n- Option syntax: we can now pass the `id` as the first parameter. This syntax is preferred over the object syntax to be consistent with the Setup syntax.\n\n### Bug Fixes\n\n- avoid modifying options argument ([59ac9b9](https://github.com/vuejs/pinia/commit/59ac9b962778f730ade9c2a8b1a575922957d907))\n- **devtools:** avoid grouping patches and mutations with finished actions ([18a87fe](https://github.com/vuejs/pinia/commit/18a87fe260317c679732d0ec271c036b9806448f))\n- **errors:** allow async errors to propagate ([17ee4e8](https://github.com/vuejs/pinia/commit/17ee4e85fb2c084ba27730dae4f21683686156c6)), closes [#576](https://github.com/vuejs/pinia/issues/576)\n- **ssr:** delay getters read ([2f3bd53](https://github.com/vuejs/pinia/commit/2f3bd5330e853b8ef11b6364a3a86e780c5f309f))\n- **types:** actual generic store ([e4c541f](https://github.com/vuejs/pinia/commit/e4c541fdd17ea97e25dfd45bd3378732ff6a344d))\n- **types:** stricter types for mapState ([f702356](https://github.com/vuejs/pinia/commit/f702356a5549dfe184c4d3805757c494a7088b19))\n\n### Features\n\n- allow actions to be destructured ([859d094](https://github.com/vuejs/pinia/commit/859d094bd993f4714093af17182ed73dd98659c5))\n- **devtools:** display pinia without stores ([ca59257](https://github.com/vuejs/pinia/commit/ca59257a4ca3a37f54d6b9690a2ceedbc545dedd))\n- **devtools:** show hot update in timeline ([3b9ed17](https://github.com/vuejs/pinia/commit/3b9ed1777621b1c8c0f781f5c974357da042c6e7))\n- **types:** add StorState, StoreGetters, and StoreActions helpers ([47c0610](https://github.com/vuejs/pinia/commit/47c06101555328b6ca24e2f574f8f402b3bf1675))\n\n### BREAKING CHANGES\n\n- **types:** The existing `Store<Id, S, G, A>` types was trying to be generic when no types were specified but failing at it. Now, `Store` without any type will default to an empty Store. This enables a stricter version of `defineStore` when any of state, getters, and actions are missing. If you were using `Store` as a type, you should now use `StoreGeneric` instead, which also replaces `GenericStore` (marked as deprecated).\n\n```diff\n-function takeAnyStore(store: Store) {}\n+function takeAnyStore(store: StoreGeneric) {}\n```\n\n- **types** The existing `DefineStoreOptions` is no longer the one that should be extended to add custom options unless you only want them to be applied to Option Stores. Use `DefineStoreOptionsBase` instead.\n\n# [2.0.0-beta.5](https://github.com/vuejs/pinia/compare/v2.0.0-beta.3...v2.0.0-beta.5) (2021-07-10)\n\n### Bug Fixes\n\n- **devtools:** avoid infinite loop when cross using stores ([55c651d](https://github.com/vuejs/pinia/commit/55c651d714a7d4083e4ef6369e3b5ab5dbf02182)), closes [#541](https://github.com/vuejs/pinia/issues/541)\n- **devtools:** avoid warning ([399a930](https://github.com/vuejs/pinia/commit/399a93002b9b3627e636af191e64a7b56f82d2db))\n- **types:** forbid non existant access in getters and actions ([2ee058e](https://github.com/vuejs/pinia/commit/2ee058ef0264dddb367c53ce534f832bdb7b5fb0))\n\n### Features\n\n- mark testing as internal ([18c8ed6](https://github.com/vuejs/pinia/commit/18c8ed6769f0313cdcf6139027c6162e973c8e89))\n- **testing:** add createTestingPinia ([120ac9d](https://github.com/vuejs/pinia/commit/120ac9d98eca0e11f24c5334022ef9bc805371af))\n- **testing:** allow stubing $patch ([10bef8a](https://github.com/vuejs/pinia/commit/10bef8ab2fa951fbaab0afe38b58a1396f23db8b))\n- **testing:** allows faking an app ([0d00a27](https://github.com/vuejs/pinia/commit/0d00a2734fa11d8a4fad6c9fb796f8c9c3a25f83))\n- **testing:** bypass useStore(pinia) ([5a52fb3](https://github.com/vuejs/pinia/commit/5a52fb33a1799e145a8d3e3423105247bb6980e7))\n\n### Performance Improvements\n\n- use esm version of file-saver ([49d1e38](https://github.com/vuejs/pinia/commit/49d1e38a808edbf58970ec2d47a1342d9a5229a1))\n\n# [2.0.0-beta.3](https://github.com/vuejs/pinia/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2021-06-18)\n\n### Bug Fixes\n\n- **patch:** avoid merging reactive objects ([a6a75e8](https://github.com/vuejs/pinia/commit/a6a75e891d3fc4a7ec2c5dea3ac8081cf460c4d2)), closes [#528](https://github.com/vuejs/pinia/issues/528)\n\n### Features\n\n- **devtools:** display custom properties ([fd901cd](https://github.com/vuejs/pinia/commit/fd901cdf34d035289067a189a874b7e2df1cbe3e))\n\n# [2.0.0-beta.2](https://github.com/vuejs/pinia/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2021-06-03)\n\n### Bug Fixes\n\n- **devtools:** register stores ([5fcca78](https://github.com/vuejs/pinia/commit/5fcca788c1da61f2a406e2924fca3a8bed51b667))\n\n# [2.0.0-beta.1](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.19...v2.0.0-beta.1) (2021-06-03)\n\n### Bug Fixes\n\n- **types:** fix extension for TS 4.3 ([aff5c1e](https://github.com/vuejs/pinia/commit/aff5c1e6fb5c559d3cae103a3049046ee97ce3c0))\n\n### Features\n\n- remove deprecated APIs ([239aec5](https://github.com/vuejs/pinia/commit/239aec50f270bc9025b1c28490dbdfbc720ab9d5))\n- **devtools:** add root state ([a75be78](https://github.com/vuejs/pinia/commit/a75be78e0d189d324c5f30ececb27cd1a61e7e77))\n- **devtools:** import/export global state ([b969f2a](https://github.com/vuejs/pinia/commit/b969f2a9884d0794733aea1162e0c154b34dee26))\n- **devtools:** load/save state ([9b503d6](https://github.com/vuejs/pinia/commit/9b503d6c81f45864eba299a20cfd2ec957c6884b))\n\n# [2.0.0-alpha.19](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.18...v2.0.0-alpha.19) (2021-05-20)\n\n### Bug Fixes\n\n- **devtools:** use older js ([e35da3b](https://github.com/vuejs/pinia/commit/e35da3be7a753ab5e4b1692395cf86cf0c314ba9))\n\n# [2.0.0-alpha.18](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.17...v2.0.0-alpha.18) (2021-05-17)\n\n### Bug Fixes\n\n- **types:** correct subtype Store ([48523da](https://github.com/vuejs/pinia/commit/48523da371ac291689b7a62b9ec433894a595827)), closes [#500](https://github.com/vuejs/pinia/issues/500)\n- **types:** export types ([befc132](https://github.com/vuejs/pinia/commit/befc132bace0b6fdc063d25db20549faa51e50b7))\n\n# [2.0.0-alpha.17](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.16...v2.0.0-alpha.17) (2021-05-17)\n\n### Bug Fixes\n\n- **types:** forbid non existent keys on store ([e747cba](https://github.com/vuejs/pinia/commit/e747cba7d90ebd9ef29a379d441744aecc31db80))\n- **types:** patch should unwrap refs ([b82eafc](https://github.com/vuejs/pinia/commit/b82eafc9e11ae820d5185359526d19dbbff0fa75))\n- **types:** unwrap refs passed to state ([b2d3ac9](https://github.com/vuejs/pinia/commit/b2d3ac994f0d1778d82fd0e5b14915fee4c5cb2b)), closes [#491](https://github.com/vuejs/pinia/issues/491)\n\n### Features\n\n- **devtools:** add more data to actions ([e8f4b0e](https://github.com/vuejs/pinia/commit/e8f4b0e95192ec7d10d380776e7e9e703fe31261))\n- **devtools:** allow editing state ([0bbbd69](https://github.com/vuejs/pinia/commit/0bbbd69db467d168770571ec0452630ff6409741))\n- **devtools:** allow editing stores from components ([b808fbc](https://github.com/vuejs/pinia/commit/b808fbcac9915d11f5979e1781ce125327a0e6ab))\n- **devtools:** display only relevant stores ([58f0af6](https://github.com/vuejs/pinia/commit/58f0af617b6516f69024cecb95ef8e0dd665104f))\n- **devtools:** group action and their changes ([ecd993a](https://github.com/vuejs/pinia/commit/ecd993abfcec2e95d63fe5ccfd64080d5c89cc0b))\n- **types:** allow defining custom state properties ([17fcbca](https://github.com/vuejs/pinia/commit/17fcbcafd30eec3af609bcc4549eee08968ea1a2))\n- **types:** infer args and returned value for onAction ([f3b3bcf](https://github.com/vuejs/pinia/commit/f3b3bcf52a8ba86bc0927f98f53045842905c216))\n- subscribe to actions with `$onAction` ([c9ce6ea](https://github.com/vuejs/pinia/commit/c9ce6ea55f225351bb95a47890f791134b233aad)), closes [#240](https://github.com/vuejs/pinia/issues/240)\n\n### Performance Improvements\n\n- **devtools:** avoid multiple subscriptions ([ea62f1d](https://github.com/vuejs/pinia/commit/ea62f1db8d82224ea0226fa5ec90da410ff31bda))\n\n### BREAKING CHANGES\n\n- The `type` property of the first parameter of `store.$subscribe()` has slightly changed. **In most scenarios this shouldn't affect you** as the possible values for `type` were including emojis (a bad decision...) and they are now using an enum without emojis. Emojis are used only in devtools to give a mental hint regarding the nature and origin of different events in the timeline.\n\n- In `store.$subscribe()`'s first argument, the `storeName` property has been deprecated in favor of `storeId`.\n\n# [2.0.0-alpha.16](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.14...v2.0.0-alpha.16) (2021-05-04)\n\n### Bug Fixes\n\n- **devtools:** add all stores ([2a8515c](https://github.com/vuejs/pinia/commit/2a8515cef57dce9cb9d0d8f7a8a52bbc1d3b6082)), closes [#472](https://github.com/vuejs/pinia/issues/472)\n\n### Features\n\n- **devtools:** display getters in components ([810b969](https://github.com/vuejs/pinia/commit/810b9697fe639070c21a76119d7d7001b6db069e))\n\n# [2.0.0-alpha.15](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.14...v2.0.0-alpha.15) (2021-05-04)\n\n### Bug Fixes\n\n- **devtools:** fix devtools attach ([017795a](https://github.com/vuejs/pinia/commit/017795aac3e654d6c67f99437851dcfe589d20b0))\n\n# [2.0.0-alpha.14](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.13...v2.0.0-alpha.14) (2021-05-03)\n\n### Features\n\n- **devtools:** work with stores added before app.use ([#444](https://github.com/vuejs/pinia/issues/444)) ([21f917f](https://github.com/vuejs/pinia/commit/21f917f057522b7e2ff70fa8517c5941c644a577))\n- **devtools:** add getters to devtools ([c4bf761](https://github.com/vuejs/pinia/commit/c4bf761e95b79a0831061e490ba1d69802bc9d95))\n- mark getters as readonly ([fcbeb95](https://github.com/vuejs/pinia/commit/fcbeb95cdd7f2b7d58731392d707183b311863ff))\n- **plugins:** allow chaining ([3a49d34](https://github.com/vuejs/pinia/commit/3a49d34f5d30c1d346243df0d043a0709b2a4861))\n- **mapHelpers:** warn on array mapStores ([d385bd9](https://github.com/vuejs/pinia/commit/d385bd98b136be15b6aa3ac6c2c8ca9261af4635))\n- pass options to context in plugins ([c8ad19f](https://github.com/vuejs/pinia/commit/c8ad19f6a959751d456aca93cd670d6b18064d50))\n- **types:** expose PiniaPluginContext ([94d12e7](https://github.com/vuejs/pinia/commit/94d12e7f127125c8aa915262471e07aae9d881bf))\n- add plugin api wip ([50bc807](https://github.com/vuejs/pinia/commit/50bc807dce932c5cbe02612505535f05dfe6325a))\n- **plugins:** allow void return ([5ef7140](https://github.com/vuejs/pinia/commit/5ef71407764384bef13a4f46fd001beade387d24))\n- **plugins:** pass a context object to plugins instead of app ([bcb4ec3](https://github.com/vuejs/pinia/commit/bcb4ec3422635dd57f655e58f72a9a7a1c7dba0d))\n- add plugin api wip ([b5c928d](https://github.com/vuejs/pinia/commit/b5c928da20efc532de84d8b8498d56f306a40e03))\n\n### Performance Improvements\n\n- **store:** reuse store instances when possible ([14f5a5f](https://github.com/vuejs/pinia/commit/14f5a5fd21677e7e5673443c35eeadbe2bdd8f05))\n\n### BREAKING CHANGES\n\n- **store:** getters now receive the state as their first argument and it's properly typed so you can write getters with arrow functions:\n\n  ```js\n  defineStore({\n    state: () => ({ n: 0 }),\n    getters: {\n      double: (state) => state.n * 2,\n    },\n  })\n  ```\n\n  To access other getters, you must still use the syntax that uses `this` **but it is now necessary to explicitly type the getter return type**. The same limitation exists in Vue for computed properties and it's a known limitation in TypeScript:\n\n  ```ts\n  defineStore({\n    state: () => ({ n: 0 }),\n    getters: {\n      double: (state) => state.n * 2,\n      // the `: number` is necessary when accessing `this` inside of\n      // a getter\n      doublePlusOne(state): number {\n        return this.double + 1\n      },\n    },\n  })\n  ```\n\n  For more information, refer to [the updated documentation for getters](https://pinia.vuejs.org/core-concepts/getters.html).\n\n- **plugins:** To improve the plugin api capabilities, `pinia.use()`\n  now receives a context object instead of just `app`:\n\n  ```js\n  // replace\n  pinia.use((app) => {})\n  // with\n  pinia.use(({ app }) => {})\n  ```\n\n  Check the new documentation for [Plugins](https://pinia.vuejs.org/core-concepts/plugins.html)!\n\n# [2.0.0-alpha.13](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.12...v2.0.0-alpha.13) (2021-04-10)\n\n### Bug Fixes\n\n- **subscribe:** remove subscription when unmounted ([10e1c30](https://github.com/vuejs/pinia/commit/10e1c3069a23f3a9c0d1e32cbc1fbb074709173e))\n\n### Features\n\n- **types:** fail on async patch ([c254a8a](https://github.com/vuejs/pinia/commit/c254a8abc73d7e2589acbffe432a33f985c1003d))\n\n# [2.0.0-alpha.12](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.11...v2.0.0-alpha.12) (2021-04-09)\n\n### Bug Fixes\n\n- **store:** avoid multiple subscriptions call ([71404cb](https://github.com/vuejs/pinia/commit/71404cb3ef6466c74b72c5fdb1075740a788a309)), closes [#429](https://github.com/vuejs/pinia/issues/429) [#430](https://github.com/vuejs/pinia/issues/430)\n\n# [2.0.0-alpha.11](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.10...v2.0.0-alpha.11) (2021-04-09)\n\n### Bug Fixes\n\n- **types:** enable autocomplete in object ([b299ff0](https://github.com/vuejs/pinia/commit/b299ff070101dc9d17ceb24bd43d697033695167))\n\n### Features\n\n- mapWritableState ([3218bdb](https://github.com/vuejs/pinia/commit/3218bdbb916049bebf1416cef1c103ecda66274b))\n- **mapState:** accept functions ([e2f2b92](https://github.com/vuejs/pinia/commit/e2f2b92f41f1908bfa6614655e625cbfcae5a716))\n- **mapStores:** allow custom suffix ([c957fb9](https://github.com/vuejs/pinia/commit/c957fb97f1b1ffdb927f40dfa0abd0304a8eb998))\n- **types:** allow extending mapStores suffix ([f14c7b9](https://github.com/vuejs/pinia/commit/f14c7b996dd8232370033c671566c4cafdf535bc))\n- add mapActions ([b5d27fb](https://github.com/vuejs/pinia/commit/b5d27fbd34b006ae7aec01c091d4e246c3fa73fc))\n- add mapStores ([d3d9327](https://github.com/vuejs/pinia/commit/d3d9327918081feaffe63b7debf4e5dbbcf55890))\n- mapState with array ([0e05811](https://github.com/vuejs/pinia/commit/0e058113668f8c9a81d09e7175d89ea921142c31))\n- mapState with object ([06805db](https://github.com/vuejs/pinia/commit/06805db9b4526cc0741016e0632cd6fb353a9728))\n- **types:** expose DefineStoreOptions ([c727070](https://github.com/vuejs/pinia/commit/c72707070a5096df62a4bab269ce9087e2d9c102))\n\n# [2.0.0-alpha.10](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.9...v2.0.0-alpha.10) (2021-04-01)\n\n### Features\n\n- **patch:** allow passing a function ([8d545e4](https://github.com/vuejs/pinia/commit/8d545e427c6415df00254eb9638116e96e64d3b5))\n- **types:** generics on PiniaCustomProperties ([36129cf](https://github.com/vuejs/pinia/commit/36129cf415abb8efccda859cd6b787594fe46f00))\n\n# [2.0.0-alpha.9](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.8...v2.0.0-alpha.9) (2021-03-31)\n\n### Bug Fixes\n\n- **types:** pass custom properties to stores ([d26df6e](https://github.com/vuejs/pinia/commit/d26df6e1133fc8dff58312df36ff2af6f129a560))\n\n# [2.0.0-alpha.8](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.7...v2.0.0-alpha.8) (2021-03-29)\n\n### Bug Fixes\n\n- use assign instead of spread ([b2bb5ba](https://github.com/vuejs/pinia/commit/b2bb5ba4faf52c41479a7d77490b85016b853229))\n- **cjs:** ensure dev checks on cjs build ([a255735](https://github.com/vuejs/pinia/commit/a255735211b796120d5f76470ea18759f1eb5d97))\n\n### Features\n\n- **devtools:** logo and titles ([0963fd0](https://github.com/vuejs/pinia/commit/0963fd0b647b0e5414782f78167c770cbab24a83))\n\n# [2.0.0-alpha.7](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.6...v2.0.0-alpha.7) (2021-01-21)\n\n### Bug Fixes\n\n- resilient _VUE_DEVTOOLS_TOAST_ ([#334](https://github.com/vuejs/pinia/issues/334)) ([c0cacd2](https://github.com/vuejs/pinia/commit/c0cacd2631d76d9d6de2b16d4106ad7decb51217))\n\n### Features\n\n- enable calling `useStore()` in client ([c949b80](https://github.com/vuejs/pinia/commit/c949b80391cae322f024b8cc369be351d5d6a693))\n- store plugins ([f027bf5](https://github.com/vuejs/pinia/commit/f027bf587b37c7fc30eba4da5f90d52d99e6536d))\n\n# [2.0.0-alpha.6](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.5...v2.0.0-alpha.6) (2020-12-31)\n\n### Bug Fixes\n\n- correct lifespan of stores ([483335c](https://github.com/vuejs/pinia/commit/483335c6660d593cf33468c1ab8c95da82cc392a)), closes [#255](https://github.com/vuejs/pinia/issues/255)\n\n### Features\n\n- **types:** export used types ([dc56fba](https://github.com/vuejs/pinia/commit/dc56fbafa21d8efa2a4b61ffb464f1befa25e34c)), closes [#315](https://github.com/vuejs/pinia/issues/315)\n\n### BREAKING CHANGES\n\n- `setActiveReq()` has been renamed to\n  `setActivePinia()`. And now receives the application's pinia as the\n  first parameter instead of an arbitrary object (like a Node http\n  request). **This affects particularly users doing SSR** but also\n  enables them to write universal code.\n\n# [2.0.0-alpha.5](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.4...v2.0.0-alpha.5) (2020-10-09)\n\n### Code Refactoring\n\n- prefix store properties with \\$ ([#254](https://github.com/vuejs/pinia/issues/254)) ([751f286](https://github.com/vuejs/pinia/commit/751f2867b97f210488eb82bad1ec05af6ab6e72c))\n\n### BREAKING CHANGES\n\n- all store properties (`id`, `state`, `patch`, `subscribe`, and `reset`) are now prefixed with `$` to allow properties defined with the same type and avoid types breaking. Tip: you can refactor your whole codebase with F2 (or right-click + Refactor) on each of the store's properties\n\n# [2.0.0-alpha.4](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.3...v2.0.0-alpha.4) (2020-09-29)\n\n### Bug Fixes\n\n- detach stores creation from currentInstance ([dc31736](https://github.com/vuejs/pinia/commit/dc317360ebebc208ca31d819953c573f6a7ac3cc))\n\n# [2.0.0-alpha.3](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.2...v2.0.0-alpha.3) (2020-09-28)\n\n### Code Refactoring\n\n- rename createStore to defineStore ([a9ad160](https://github.com/vuejs/pinia/commit/a9ad160bb38d6bfae3a52c66ae28793937af05d6))\n\n### Features\n\n- deprecation message createStore ([3054251](https://github.com/vuejs/pinia/commit/30542514389e4b903e7726039b98324afdafcc24))\n- **ssr:** support ssr ([59709e0](https://github.com/vuejs/pinia/commit/59709e0851db66d337054e3aab0db987fab20f9d))\n\n### BREAKING CHANGES\n\n- renamed `createStore` to `defineStore`. `createStore`\n  will be marked as deprecated during the alpha releases and then be\n  dropped.\n\n# [2.0.0-alpha.2](https://github.com/vuejs/pinia/compare/v2.0.0-alpha.1...v2.0.0-alpha.2) (2020-09-25)\n\n### Features\n\n- add devtools support ([849cb3f](https://github.com/vuejs/pinia/commit/849cb3f30559e312bf00625a42a7b697c68d9941))\n\n# [2.0.0-alpha.1](https://github.com/vuejs/pinia/compare/0.1.0-alpha.1...2.0.0-alpha.1) (2020-09-22)\n\n### Features\n\n- access the state and getters through `this` ([#190](https://github.com/vuejs/pinia/issues/190)) ([6df18ef](https://github.com/vuejs/pinia/commit/6df18ef49472b0348b09cb84801c9c69ae79b3d9))\n- merge all properties under this ([d5eaac1](https://github.com/vuejs/pinia/commit/d5eaac106c50be8febc25083839e7cb635ccfda7))\n\n### BREAKING CHANGES\n\n- `state` properties no longer need to be accessed through `store.state`\n- `getters` no longer receive parameters, access the store instance via `this`:\n  directly call `this.myState` to read state and other getters. **Update 2021-04-02**: `getters` receive the state again as the first parameter\n"
  },
  {
    "path": "packages/pinia/README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://pinia.vuejs.org\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <img width=\"180\" src=\"https://pinia.vuejs.org/logo.svg\" alt=\"Pinia logo\">\n  </a>\n</p>\n\n# Pinia\n\n> Intuitive, type safe and flexible Store for Vue\n\n## 👉 [Demo with Vue 3 on StackBlitz](https://stackblitz.com/github/piniajs/example-vue-3-vite)\n\n## Help me keep working on this project 💚\n\n- [Become a Sponsor on GitHub](https://github.com/sponsors/posva)\n- [One-time donation via PayPal](https://paypal.me/posva)\n\n## Documentation\n\nTo learn more about Pinia, check [its documentation](https://pinia.vuejs.org).\n\n## License\n\n[MIT](http://opensource.org/licenses/MIT)\n"
  },
  {
    "path": "packages/pinia/__tests__/actions.spec.ts",
    "content": "import { describe, it, expect, vi } from 'vitest'\nimport { createPinia, defineStore, setActivePinia } from '../src'\n\ndescribe('Actions', () => {\n  const useStore = () => {\n    // create a new store\n    setActivePinia(createPinia())\n    return defineStore('main', {\n      state: () => ({\n        a: true,\n        nested: {\n          foo: 'foo',\n          a: { b: 'string' },\n        },\n      }),\n      getters: {\n        nonA(): boolean {\n          return !this.a\n        },\n        otherComputed() {\n          return this.nonA\n        },\n      },\n      actions: {\n        async getNonA() {\n          return this.nonA\n        },\n        simple() {\n          this.toggle()\n          return 'simple'\n        },\n\n        toggle() {\n          return (this.a = !this.a)\n        },\n\n        setFoo(foo: string) {\n          this.$patch({ nested: { foo } })\n        },\n\n        combined() {\n          this.toggle()\n          this.setFoo('bar')\n        },\n\n        throws() {\n          throw new Error('fail')\n        },\n\n        async rejects() {\n          throw 'fail'\n        },\n      },\n    })()\n  }\n\n  const useB = defineStore('B', { state: () => ({ b: 'b' }) })\n\n  const useA = defineStore('A', {\n    state: () => ({ a: 'a' }),\n    actions: {\n      swap() {\n        const bStore = useB()\n        const b = bStore.$state.b\n        bStore.$state.b = this.$state.a\n        this.$state.a = b\n      },\n    },\n  })\n\n  it('can use the store as this', () => {\n    const store = useStore()\n    expect(store.$state.a).toBe(true)\n    store.toggle()\n    expect(store.$state.a).toBe(false)\n  })\n\n  it('store is forced as the context', () => {\n    const store = useStore()\n    expect(store.$state.a).toBe(true)\n    expect(() => {\n      store.toggle.call(null)\n    }).not.toThrow()\n    expect(store.$state.a).toBe(false)\n  })\n\n  it('can call other actions', () => {\n    const store = useStore()\n    expect(store.$state.a).toBe(true)\n    expect(store.$state.nested.foo).toBe('foo')\n    store.combined()\n    expect(store.$state.a).toBe(false)\n    expect(store.$state.nested.foo).toBe('bar')\n  })\n\n  it('supports being called between two applications', () => {\n    const pinia1 = createPinia()\n    const pinia2 = createPinia()\n    setActivePinia(pinia1)\n    const aStore = useA()\n\n    // simulate a different application\n    setActivePinia(pinia2)\n    const bStore = useB()\n    bStore.$state.b = 'c'\n\n    aStore.swap()\n    expect(aStore.$state.a).toBe('b')\n    // a different instance of b store was used\n    expect(bStore.$state.b).toBe('c')\n  })\n\n  it('can force the pinia', () => {\n    // setup other pinias to force possible override effects on the options effect\n    const pinia11 = createPinia()\n    // const pinia22 = createPinia()\n    setActivePinia(pinia11)\n    useA()\n    setActivePinia(undefined)\n\n    const pinia1 = createPinia()\n    const pinia2 = createPinia()\n    const aStore = useA(pinia1)\n\n    let bStore = useB(pinia2)\n    bStore.$state.b = 'c'\n\n    aStore.swap()\n    expect(aStore.$state.a).toBe('b')\n    // a different instance of b store was used\n    expect(bStore.$state.b).toBe('c')\n    bStore = useB(pinia1)\n    expect(bStore.$state.b).toBe('a')\n  })\n\n  it('throws errors', () => {\n    const store = useStore()\n    expect(() => store.throws()).toThrowError('fail')\n  })\n\n  it('throws async errors', async () => {\n    const store = useStore()\n    expect.assertions(1)\n    await expect(store.rejects()).rejects.toBe('fail')\n  })\n\n  it('can catch async errors', async () => {\n    const store = useStore()\n    expect.assertions(3)\n    const spy = vi.fn()\n    await expect(store.rejects().catch(spy)).resolves.toBe(undefined)\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith('fail')\n  })\n\n  it('can destructure actions', () => {\n    const store = useStore()\n    const { simple } = store\n    expect(simple()).toBe('simple')\n    // works with the wrong this\n    expect({ simple }.simple()).toBe('simple')\n    // special this check\n    expect({ $id: 'o', simple }.simple()).toBe('simple')\n    // override the function like devtools do\n    expect(\n      {\n        $id: store.$id,\n        simple,\n        // otherwise it would fail\n        toggle() {},\n      }.simple()\n    ).toBe('simple')\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/combinedStores.spec.ts",
    "content": "import { beforeEach, describe, it, expect } from 'vitest'\nimport { computed, ref } from 'vue'\nimport { createPinia, defineStore, setActivePinia } from '../src'\n\ndescribe('Composing stores', () => {\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  function useCounter() {\n    const n = ref(0)\n    const double = computed(() => n.value)\n\n    function increment(amount = 1) {\n      n.value += amount\n    }\n\n    return { n, double, increment }\n  }\n\n  const useStoreA = defineStore('a', () => {\n    const { n, double, increment } = useCounter()\n    const b = useStoreB()\n\n    function changeB() {\n      b.n++\n    }\n\n    function getInnerB() {\n      return b\n    }\n\n    return { n, increment, double, changeB, getInnerB }\n  })\n\n  const useStoreB = defineStore('b', () => {\n    const { n, double, increment } = useCounter()\n    const a = useStoreA()\n\n    function changeA() {\n      a.n++\n    }\n\n    function getInnerA() {\n      return a\n    }\n\n    return { n, increment, double, changeA, getInnerA }\n  })\n\n  it('works', () => {\n    expect(() => {\n      useStoreA()\n      useStoreB()\n    }).not.toThrow()\n  })\n\n  it('they can use each other', () => {\n    const b = useStoreB()\n    const a = useStoreA()\n\n    expect(a).toBe(b.getInnerA())\n    expect(b).toBe(a.getInnerB())\n\n    expect(a.n).toBe(0)\n    b.changeA()\n    expect(a.n).toBe(1)\n\n    expect(b.n).toBe(0)\n    a.changeB()\n    expect(b.n).toBe(1)\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/devtools.spec.ts",
    "content": "import { describe, it, expect } from 'vitest'\nimport { mount } from '@vue/test-utils'\nimport { createPinia, defineStore } from '../src'\nimport { devtoolsPlugin } from '../src/devtools'\n\ndescribe('devtoolsPlugin', () => {\n  const useStore = defineStore('test', {\n    actions: {\n      myAction() {\n        return 42\n      },\n    },\n  })\n\n  it('preserves mocked actions during testing', () => {\n    const pinia = createPinia()\n    // Simulate using createTestingPinia\n    pinia._testing = true\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    // Simulate mocking with @pinia/testing createSpy\n    pinia.use(({ store, options }) => {\n      Object.keys(options.actions).forEach((action) => {\n        store[action]._mockImplementation = () => {}\n      })\n    })\n    // Previously the mocked actions would be wrapped again\n    pinia.use(devtoolsPlugin)\n\n    const store = useStore(pinia)\n\n    // @ts-expect-error we have not actually loaded @pinia/testing and mocked actions\n    expect(store.myAction._mockImplementation).toBeDefined()\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/getters.spec.ts",
    "content": "import { beforeEach, describe, it, expect } from 'vitest'\nimport { ref, computed } from 'vue'\nimport { createPinia, defineStore, setActivePinia } from '../src'\n\nfunction expectType<T>(_value: T): void {}\n\ndescribe('Getters', () => {\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  const useStore = defineStore('main', {\n    state: () => ({\n      name: 'Eduardo',\n    }),\n    getters: {\n      upperCaseName(store) {\n        return store.name.toUpperCase()\n      },\n      doubleName(): string {\n        return this.upperCaseName\n      },\n      composed(): string {\n        return this.upperCaseName + ': ok'\n      },\n      arrowUpper: (state) => {\n        // @ts-expect-error\n        state.nope\n        state.name.toUpperCase()\n      },\n    },\n    actions: {\n      o() {\n        // @ts-expect-error it should type getters\n        this.arrowUpper.toUpperCase()\n        this.o().toUpperCase()\n        return 'a string'\n      },\n    },\n  })\n\n  const useB = defineStore('B', { state: () => ({ b: 'b' }) })\n\n  const useA = defineStore('A', {\n    state: () => ({ a: 'a' }),\n    getters: {\n      fromB(): string {\n        const bStore = useB()\n        return this.a + ' ' + bStore.b\n      },\n    },\n  })\n\n  it('adds getters to the store', () => {\n    const store = useStore()\n    expect(store.upperCaseName).toBe('EDUARDO')\n\n    // @ts-expect-error\n    store.nope\n\n    store.name = 'Ed'\n    expect(store.upperCaseName).toBe('ED')\n  })\n\n  it('updates the value', () => {\n    const store = useStore()\n    store.name = 'Ed'\n    expect(store.upperCaseName).toBe('ED')\n  })\n\n  it('supports changing between applications', () => {\n    const pinia1 = createPinia()\n    const pinia2 = createPinia()\n    setActivePinia(pinia1)\n    const aStore = useA()\n\n    // simulate a different application\n    setActivePinia(pinia2)\n    const bStore = useB()\n    bStore.b = 'c'\n\n    aStore.a = 'b'\n    expect(aStore.fromB).toBe('b b')\n  })\n\n  it('can use other getters', () => {\n    const store = useStore()\n    expect(store.composed).toBe('EDUARDO: ok')\n    store.name = 'Ed'\n    expect(store.composed).toBe('ED: ok')\n  })\n\n  it('keeps getters reactive when hydrating', () => {\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    pinia.state.value = { main: { name: 'Jack' } }\n    const store = useStore()\n    expect(store.name).toBe('Jack')\n    expect(store.upperCaseName).toBe('JACK')\n    store.name = 'Ed'\n    expect(store.upperCaseName).toBe('ED')\n  })\n\n  it('can use getters with setters', () => {\n    const useStore = defineStore('main', () => {\n      const name = ref('Eduardo')\n      const upperCaseName = computed({\n        get() {\n          return name.value.toUpperCase()\n        },\n        set(value: string) {\n          store.name = value.toLowerCase()\n        },\n      })\n      return { name, upperCaseName }\n    })\n\n    const store = useStore()\n    expect(store.upperCaseName).toBe('EDUARDO')\n    store.upperCaseName = 'ED'\n    expect(store.name).toBe('ed')\n  })\n\n  it('can use getters with setters with different types', () => {\n    const useStore = defineStore('main', () => {\n      const n = ref(0)\n      const double = computed({\n        get() {\n          return n.value * 2\n        },\n        set(value: string | number) {\n          n.value =\n            (typeof value === 'string' ? parseInt(value) || 0 : value) / 2\n        },\n      })\n      return { n, double }\n    })\n\n    const store = useStore()\n    expect(store.$state).not.toHaveProperty('double')\n    store.double = 4\n    expect(store.n).toBe(2)\n    // @ts-expect-error: still not doable\n    store.double = '6'\n    expect(store.n).toBe(3)\n  })\n\n  describe('cross used stores', () => {\n    const useA = defineStore('a', () => {\n      const B = useB()\n\n      const n = ref(0)\n      const double = computed(() => n.value * 2)\n      const sum = computed(() => n.value + B.n)\n\n      function increment() {\n        return n.value++\n      }\n\n      function incrementB() {\n        return B.increment()\n      }\n\n      return { n, double, sum, increment, incrementB }\n    })\n\n    const useB = defineStore('b', () => {\n      const A = useA()\n\n      const n = ref(0)\n      const double = computed(() => n.value * 2)\n\n      function increment() {\n        return n.value++\n      }\n\n      function incrementA() {\n        return A.increment()\n      }\n\n      return { n, double, incrementA, increment }\n    })\n\n    it('keeps getters reactive', () => {\n      const a = useA()\n      const b = useB()\n\n      expectType<() => number>(a.increment)\n      expectType<() => number>(b.increment)\n      expectType<() => number>(a.incrementB)\n      expectType<() => number>(b.incrementA)\n\n      expect(a.double).toBe(0)\n      b.incrementA()\n      expect(a.double).toBe(2)\n      a.increment()\n      expect(a.double).toBe(4)\n\n      expect(b.double).toBe(0)\n      a.incrementB()\n      expect(b.double).toBe(2)\n      b.increment()\n      expect(b.double).toBe(4)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/hmr.spec.ts",
    "content": "import { beforeEach, describe, it, expect, vi } from 'vitest'\nimport { computed, reactive, ref, toRefs, watch } from 'vue'\nimport {\n  createPinia,\n  defineStore,\n  DefineStoreOptions,\n  setActivePinia,\n  StateTree,\n} from '../src'\n\nfunction defineOptions<\n  O extends Omit<DefineStoreOptions<string, StateTree, any, any>, 'id'>,\n>(options: O): O {\n  return options\n}\n\ndescribe('HMR', () => {\n  const baseOptions = defineOptions({\n    state: () => ({\n      n: 0,\n      arr: [],\n      nestedArr: {\n        arr: [],\n      },\n      nested: {\n        a: 'a',\n      },\n    }),\n\n    actions: {\n      increment(amount = 1) {\n        // @ts-ignore\n        this.n += amount\n      },\n    },\n\n    getters: {\n      double: (state: any) => state.n * 2,\n    },\n  })\n\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  describe('Setup store', () => {\n    const baseSetup = () => {\n      const state = reactive({\n        n: 0,\n        arr: [],\n        nestedArr: {\n          arr: [],\n        },\n        nested: {\n          a: 'a',\n        },\n      })\n\n      function increment(amount = 1) {\n        state.n += amount\n      }\n\n      const double = computed(() => state.n * 2)\n\n      return { ...toRefs(state), increment, double }\n    }\n\n    describe('state', () => {\n      it('adds new state properties', () => {\n        const useStore = defineStore('id', baseSetup)\n        const store: any = useStore()\n        store.n++\n\n        // simulate a hmr\n        defineStore('id', () => {\n          const state = reactive({\n            newOne: 'hey',\n            n: 0,\n          })\n\n          function increment(amount = 1) {\n            state.n += amount\n          }\n\n          const double = computed(() => state.n * 2)\n\n          return { ...toRefs(state), increment, double }\n        })(null, store)\n\n        expect(store.$state).toEqual({ n: 1, newOne: 'hey' })\n        expect(store.n).toBe(1)\n        expect(store.newOne).toBe('hey')\n\n        defineStore('id', () => {\n          const state = reactive({\n            other: 'new',\n            n: 0,\n          })\n\n          function increment(amount = 1) {\n            state.n += amount\n          }\n\n          const double = computed(() => state.n * 2)\n\n          return { ...toRefs(state), increment, double }\n        })(null, store)\n\n        expect(store.$state).toEqual({ n: 1, other: 'new' })\n        expect(store.n).toBe(1)\n        expect(store).not.toHaveProperty('newOne')\n        expect(store.other).toBe('new')\n      })\n\n      it('keeps state reactive', () => {\n        const useStore = defineStore('id', baseSetup)\n        const store: any = useStore()\n\n        const directSpy = vi.fn()\n        const $stateSpy = vi.fn()\n\n        watch(() => store.n, directSpy, { flush: 'sync' })\n        watch(() => store.$state.n, $stateSpy, { flush: 'sync' })\n\n        // simulate a hmr\n        defineStore('id', () => {\n          const state = reactive({\n            newOne: 'hey',\n            n: 0,\n          })\n\n          function increment(amount = 1) {\n            state.n += amount\n          }\n\n          const double = computed(() => state.n * 2)\n\n          return { ...toRefs(state), increment, double }\n        })(null, store)\n\n        expect(directSpy).toHaveBeenCalledTimes(0)\n        expect($stateSpy).toHaveBeenCalledTimes(0)\n\n        store.n++\n        expect(directSpy).toHaveBeenCalledTimes(1)\n        expect($stateSpy).toHaveBeenCalledTimes(1)\n        store.$state.n++\n        expect(directSpy).toHaveBeenCalledTimes(2)\n        expect($stateSpy).toHaveBeenCalledTimes(2)\n\n        defineStore('id', () => {\n          const state = reactive({\n            other: 'new',\n            n: 0,\n          })\n\n          function increment(amount = 1) {\n            state.n += amount\n          }\n\n          const double = computed(() => state.n * 2)\n\n          return { ...toRefs(state), increment, double }\n        })(null, store)\n\n        store.n++\n        expect(directSpy).toHaveBeenCalledTimes(3)\n        expect($stateSpy).toHaveBeenCalledTimes(3)\n\n        store.$state.n++\n        expect(directSpy).toHaveBeenCalledTimes(4)\n        expect($stateSpy).toHaveBeenCalledTimes(4)\n      })\n\n      it.todo('handles nested objects updates')\n    })\n\n    describe('actions', () => {\n      it('adds new actions', () => {\n        const useStore = defineStore('id', baseSetup)\n        const store: any = useStore()\n\n        // simulate a hmr\n        defineStore('id', () => {\n          const data = baseSetup()\n          function decrement() {\n            data.n.value--\n          }\n          return { ...data, decrement }\n        })(null, store)\n\n        store.increment()\n        expect(store.n).toBe(1)\n        expect(store.$state.n).toBe(1)\n        store.decrement()\n        expect(store.n).toBe(0)\n        expect(store.$state.n).toBe(0)\n\n        defineStore('id', baseSetup)(null, store)\n\n        store.increment()\n        expect(store.n).toBe(1)\n        expect(store.$state.n).toBe(1)\n        expect(store).not.toHaveProperty('decrement')\n      })\n    })\n\n    describe('getters', () => {\n      it('adds new getters properties', () => {\n        const useStore = defineStore('id', baseSetup)\n        const store: any = useStore()\n        expect(store.double).toBe(0)\n\n        // simulate a hmr\n        defineStore('id', () => {\n          const data = baseSetup()\n          const triple = computed(() => data.n.value * 3)\n          return { ...data, triple }\n        })(null, store)\n\n        store.n = 3\n        expect(store.double).toBe(6)\n        expect(store.triple).toBe(9)\n\n        defineStore('id', baseSetup)(null, store)\n\n        store.n = 4\n        expect(store.double).toBe(8)\n        expect(store).not.toHaveProperty('triple')\n      })\n\n      it('keeps getters reactive', () => {\n        const useStore = defineStore('id', baseSetup)\n        const store: any = useStore()\n\n        const spy = vi.fn()\n\n        watch(\n          () => {\n            return store.double\n          },\n          spy,\n          { flush: 'sync' }\n        )\n\n        // simulate a hmr\n        defineStore('id', () => {\n          const data = baseSetup()\n          data.n.value = 2\n          // @ts-expect-error: not defined\n          data.newThing = ref(true)\n          return data\n        })(null, store)\n\n        // n isn't changed\n        expect(spy).toHaveBeenCalledTimes(0)\n\n        store.n++\n        // expect(store.double).toBe(6)\n        expect(spy).toHaveBeenCalledTimes(1)\n\n        defineStore('id', baseSetup)(null, store)\n\n        store.n++\n        // expect(store.double).toBe(8)\n        expect(spy).toHaveBeenCalledTimes(2)\n      })\n    })\n  })\n\n  describe('Options store', () => {\n    describe('state', () => {\n      it('adds new state properties', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n        store.n++\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ newOne: 'hey', n: 0 }),\n        })(null, store)\n\n        expect(store.$state).toEqual({ n: 1, newOne: 'hey' })\n        expect(store.n).toBe(1)\n        expect(store.newOne).toBe('hey')\n\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ other: 'new', n: 0 }),\n        })(null, store)\n\n        expect(store.$state).toEqual({ n: 1, other: 'new' })\n        expect(store.n).toBe(1)\n        expect(store).not.toHaveProperty('newOne')\n        expect(store.other).toBe('new')\n      })\n\n      it('patches nested objects', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ nested: { a: 'b', b: 'b' } }),\n        })(null, store)\n\n        expect(store.$state).toEqual({ nested: { a: 'a', b: 'b' } })\n\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ nested: { b: 'c' } }),\n        })(null, store)\n        // removes the nested a\n        expect(store.$state).toEqual({ nested: { b: 'b' } })\n      })\n\n      it('skips arrays', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ arr: [2] }),\n        })(null, store)\n\n        expect(store.$state).toEqual({ arr: [] })\n\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ arr: [1] }),\n        })(null, store)\n        expect(store.$state).toEqual({ arr: [] })\n      })\n\n      it('skips nested arrays', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ nestedArr: { arr: [2] } }),\n        })(null, store)\n\n        expect(store.$state).toEqual({ nestedArr: { arr: [] } })\n\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ nestedArr: { arr: [1] } }),\n        })(null, store)\n        expect(store.$state).toEqual({ nestedArr: { arr: [] } })\n      })\n\n      it('keeps state reactive', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n\n        const directSpy = vi.fn()\n        const $stateSpy = vi.fn()\n\n        watch(() => store.n, directSpy, { flush: 'sync' })\n        watch(() => store.$state.n, $stateSpy, { flush: 'sync' })\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ newOne: 'hey', n: 0 }),\n        })(null, store)\n\n        expect(directSpy).toHaveBeenCalledTimes(0)\n        expect($stateSpy).toHaveBeenCalledTimes(0)\n\n        store.n++\n        expect(directSpy).toHaveBeenCalledTimes(1)\n        expect($stateSpy).toHaveBeenCalledTimes(1)\n        store.$state.n++\n        expect(directSpy).toHaveBeenCalledTimes(2)\n        expect($stateSpy).toHaveBeenCalledTimes(2)\n\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ other: 'new', n: 0 }),\n        })(null, store)\n\n        store.n++\n        expect(directSpy).toHaveBeenCalledTimes(3)\n        expect($stateSpy).toHaveBeenCalledTimes(3)\n\n        store.$state.n++\n        expect(directSpy).toHaveBeenCalledTimes(4)\n        expect($stateSpy).toHaveBeenCalledTimes(4)\n      })\n\n      it.todo('handles nested objects updates')\n    })\n\n    describe('actions', () => {\n      it('adds new actions', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          actions: {\n            ...baseOptions.actions,\n            decrement() {\n              this.n--\n            },\n          },\n        })(null, store)\n\n        store.increment()\n        expect(store.n).toBe(1)\n        expect(store.$state.n).toBe(1)\n        store.decrement()\n        expect(store.n).toBe(0)\n        expect(store.$state.n).toBe(0)\n\n        defineStore('id', baseOptions)(null, store)\n\n        store.increment()\n        expect(store.n).toBe(1)\n        expect(store.$state.n).toBe(1)\n        expect(store).not.toHaveProperty('decrement')\n      })\n    })\n\n    describe('getters', () => {\n      it('adds new getters properties', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n        expect(store.double).toBe(0)\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          getters: {\n            ...baseOptions.getters,\n            triple: (state) => state.n * 3,\n          },\n        })(null, store)\n\n        store.n = 3\n        expect(store.double).toBe(6)\n        expect(store.triple).toBe(9)\n\n        defineStore('id', baseOptions)(null, store)\n\n        store.n = 4\n        expect(store.double).toBe(8)\n        expect(store).not.toHaveProperty('triple')\n      })\n\n      it('keeps getters reactive', () => {\n        const useStore = defineStore('id', baseOptions)\n        const store: any = useStore()\n\n        const spy = vi.fn()\n\n        watch(\n          () => {\n            return store.double\n          },\n          spy,\n          { flush: 'sync' }\n        )\n\n        // simulate a hmr\n        defineStore('id', {\n          ...baseOptions,\n          state: () => ({ n: 2, newThing: true }),\n        })(null, store)\n\n        // n isn't changed\n        expect(spy).toHaveBeenCalledTimes(0)\n\n        store.n++\n        // expect(store.double).toBe(6)\n        expect(spy).toHaveBeenCalledTimes(1)\n\n        defineStore('id', baseOptions)(null, store)\n\n        store.n++\n        // expect(store.double).toBe(8)\n        expect(spy).toHaveBeenCalledTimes(2)\n      })\n    })\n  })\n\n  describe('both', () => {\n    it.todo('keeps $subscribe subscriptions')\n    it.todo('$onAction subscriptions')\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/lifespan.spec.ts",
    "content": "import { describe, it, expect, vi } from 'vitest'\nimport {\n  createPinia,\n  defineStore,\n  disposePinia,\n  getActivePinia,\n  setActivePinia,\n} from '../src'\nimport { mount } from '@vue/test-utils'\nimport {\n  watch,\n  nextTick,\n  defineComponent,\n  ref,\n  Ref,\n  onMounted,\n  getCurrentInstance,\n} from 'vue'\n\ndescribe('Store Lifespan', () => {\n  function defineMyStore() {\n    return defineStore('main', {\n      state: () => ({\n        a: true,\n        n: 0,\n        aRef: ref(0),\n        nested: {\n          foo: 'foo',\n          a: { b: 'string' },\n        },\n      }),\n      getters: {\n        double(state) {\n          return state.n * 2\n        },\n        notA(state) {\n          return !state.a\n        },\n      },\n    })\n  }\n\n  const pinia = createPinia()\n\n  it('gets the active pinia outside of setup', () => {\n    setActivePinia(pinia)\n    expect(getCurrentInstance()).toBeFalsy()\n    expect(getActivePinia()).toBe(pinia)\n  })\n\n  it('gets the active pinia inside of setup', () => {\n    expect.assertions(3)\n    const pinia = createPinia()\n    setActivePinia(undefined)\n    expect(getActivePinia()).toBe(undefined)\n\n    mount(\n      {\n        template: 'no',\n        setup() {\n          expect(getActivePinia()).toBe(pinia)\n        },\n      },\n      { global: { plugins: [pinia] } }\n    )\n    // and outside too\n    expect(getActivePinia()).toBe(pinia)\n  })\n\n  it('state reactivity outlives component life', () => {\n    const useStore = defineMyStore()\n\n    const inComponentWatch = vi.fn()\n\n    const Component = defineComponent({\n      render: () => null,\n      setup() {\n        const store = useStore()\n        watch(() => store.n, inComponentWatch, {\n          flush: 'sync',\n        })\n        onMounted(() => {\n          store.n++\n        })\n      },\n    })\n\n    const options = {\n      global: {\n        plugins: [pinia],\n      },\n    }\n\n    let wrapper = mount(Component, options)\n    wrapper.unmount()\n\n    expect(inComponentWatch).toHaveBeenCalledTimes(1)\n\n    let store = useStore()\n    store.n++\n    expect(inComponentWatch).toHaveBeenCalledTimes(1)\n\n    wrapper = mount(Component, options)\n    wrapper.unmount()\n\n    expect(inComponentWatch).toHaveBeenCalledTimes(2)\n\n    store = useStore()\n    store.n++\n    expect(inComponentWatch).toHaveBeenCalledTimes(2)\n  })\n\n  it('ref in state reactivity outlives component life', async () => {\n    let n: Ref<number>\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    const globalWatch = vi.fn()\n    const destroy = watch(() => pinia.state.value.a?.n, globalWatch)\n\n    const useStore = defineStore('a', {\n      state: () => {\n        n = n || ref(0)\n        return { n }\n      },\n    })\n\n    const Component = defineComponent({\n      render: () => null,\n      setup() {\n        const store = useStore()\n        store.n++\n      },\n    })\n\n    const options = {\n      global: {\n        plugins: [pinia],\n      },\n    }\n\n    let wrapper = mount(Component, options)\n    wrapper.unmount()\n    await nextTick()\n\n    expect(globalWatch).toHaveBeenCalledTimes(1)\n\n    let store = useStore()\n    store.n++\n    await nextTick()\n    expect(globalWatch).toHaveBeenCalledTimes(2)\n\n    wrapper = mount(Component, options)\n    wrapper.unmount()\n    await nextTick()\n\n    expect(globalWatch).toHaveBeenCalledTimes(3)\n\n    store = useStore()\n    store.n++\n    await nextTick()\n    expect(globalWatch).toHaveBeenCalledTimes(4)\n\n    destroy()\n  })\n\n  it('dispose stops store reactivity', () => {\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    const inStoreWatch = vi.fn()\n\n    const useStore = defineStore('a', () => {\n      const n = ref(0)\n      watch(n, inStoreWatch, {\n        flush: 'sync',\n      })\n      return { n }\n    })\n\n    const store = useStore()\n    store.n++\n    expect(inStoreWatch).toHaveBeenCalledTimes(1)\n\n    disposePinia(pinia)\n    store.n++\n    expect(inStoreWatch).toHaveBeenCalledTimes(1)\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/mapHelpers.spec.ts",
    "content": "import { describe, it, expect } from 'vitest'\nimport {\n  createPinia,\n  defineStore,\n  mapActions,\n  mapGetters,\n  mapState,\n  mapStores,\n  mapWritableState,\n  setMapStoreSuffix,\n} from '../src'\nimport { mount } from '@vue/test-utils'\nimport { nextTick, defineComponent, ref, computed } from 'vue'\nimport { mockWarn } from './vitest-mock-warn'\n\ndescribe('Map Helpers', () => {\n  const useStore = defineStore('main', {\n    state: () => ({\n      a: true,\n      n: 0,\n      nested: {\n        foo: 'foo',\n        a: { b: 'string' },\n      },\n    }),\n    getters: {\n      double(state) {\n        return state.n * 2\n      },\n      notA(state) {\n        return !state.a\n      },\n    },\n    actions: {\n      doubleCount() {\n        this.n = this.n * 2\n      },\n    },\n  })\n\n  describe('mapStores', () => {\n    mockWarn()\n\n    it('can set custom suffix', async () => {\n      const pinia = createPinia()\n      setMapStoreSuffix('')\n      const Component = defineComponent({\n        template: `<p @click=\"main.n++\">{{ main.n }}</p>`,\n        computed: {\n          ...mapStores(useStore),\n        },\n      })\n\n      const wrapper = mount(Component, { global: { plugins: [pinia] } })\n      // @ts-expect-error: by default this shouldn't exist\n      expect(wrapper.vm.main).toBeDefined()\n      expect(wrapper.vm.mainStore).not.toBeDefined()\n      expect(wrapper.text()).toBe('0')\n      await nextTick()\n\n      await wrapper.trigger('click')\n      expect(wrapper.text()).toBe('1')\n      await wrapper.trigger('click')\n      expect(wrapper.text()).toBe('2')\n    })\n\n    it('should warn when an array is passed', () => {\n      mapStores([])\n      expect('pass all stores to \"mapStores()\"').toHaveBeenWarned()\n    })\n  })\n\n  it('mapGetters', () => {\n    expect(mapGetters).toBe(mapState)\n  })\n\n  describe('mapState', () => {\n    async function testComponent(\n      computedProperties: any,\n      template: string,\n      expectedText: string\n    ) {\n      const pinia = createPinia()\n      const Component = defineComponent({\n        template: `<p>${template}</p>`,\n        computed: {\n          ...computedProperties,\n        },\n      })\n\n      const wrapper = mount(Component, { global: { plugins: [pinia] } })\n\n      expect(wrapper.text()).toBe(expectedText)\n    }\n\n    it('array', async () => {\n      await testComponent(\n        mapState(useStore, ['n', 'a']),\n        `{{ n }} {{ a }}`,\n        `0 true`\n      )\n    })\n\n    it('object', async () => {\n      await testComponent(\n        mapState(useStore, { count: 'n', myA: 'a' }),\n        `{{ count }} {{ myA }}`,\n        `0 true`\n      )\n    })\n\n    it('object with functions', async () => {\n      await testComponent(\n        mapState(useStore, { triple: (state) => (state.n + 1) * 3, myA: 'a' }),\n        `{{ triple }} {{ myA }}`,\n        `3 true`\n      )\n    })\n\n    it('uses component context', async () => {\n      const pinia = createPinia()\n      let vm\n      const Component = defineComponent({\n        template: `<p>{{ n }}</p>`,\n        computed: {\n          ...mapState(useStore, {\n            n(store) {\n              vm = this\n              return store.n\n            },\n          }),\n        },\n      })\n\n      const wrapper = mount(Component, { global: { plugins: [pinia] } })\n      expect(vm).toBe(wrapper.vm)\n    })\n\n    it('getters', async () => {\n      await testComponent(\n        mapState(useStore, ['double', 'notA', 'a']),\n        `{{ a }} {{ notA }} {{ double }}`,\n        `true false 0`\n      )\n    })\n  })\n\n  describe('mapActions', () => {\n    const useStore = defineStore('main', {\n      state: () => ({ n: 0 }),\n      actions: {\n        increment() {\n          this.n++\n        },\n        setN(newN: number) {\n          return (this.n = newN)\n        },\n      },\n    })\n\n    it('array', () => {\n      const pinia = createPinia()\n      const Component = defineComponent({\n        template: `<p></p>`,\n        methods: {\n          ...mapActions(useStore, ['increment', 'setN']),\n        },\n      })\n\n      const wrapper = mount(Component, { global: { plugins: [pinia] } })\n\n      expect(wrapper.vm.increment()).toBe(undefined)\n      expect(wrapper.vm.setN(4)).toBe(4)\n    })\n\n    it('object', () => {\n      const pinia = createPinia()\n      const Component = defineComponent({\n        template: `<p></p>`,\n        methods: {\n          ...mapActions(useStore, { inc: 'increment', set: 'setN' }),\n        },\n      })\n\n      const wrapper = mount(Component, { global: { plugins: [pinia] } })\n\n      expect(wrapper.vm.inc()).toBe(undefined)\n      expect(wrapper.vm.set(4)).toBe(4)\n    })\n  })\n\n  describe('mapWritableState', () => {\n    async function testComponent(\n      computedProperties: any,\n      template: string,\n      expectedText: string,\n      expectedText2: string\n    ) {\n      const pinia = createPinia()\n      const Component = defineComponent({\n        template: `<p>${template}</p>`,\n        computed: {\n          ...computedProperties,\n        },\n        methods: Object.keys(computedProperties).reduce((methods, name) => {\n          // @ts-ignore\n          methods['set_' + name] = function (v: any) {\n            // @ts-ignore\n            this[name] = v\n          }\n          return methods\n        }, {}),\n      })\n\n      const wrapper = mount(Component, { global: { plugins: [pinia] } })\n\n      expect(wrapper.text()).toBe(expectedText)\n\n      for (const key in computedProperties) {\n        // @ts-ignore\n        wrapper.vm['set_' + key]('replaced')\n      }\n\n      await nextTick()\n\n      expect(wrapper.text()).toBe(expectedText2)\n    }\n\n    it('array', async () => {\n      await testComponent(\n        mapWritableState(useStore, ['n', 'a']),\n        `{{ n }} {{ a }}`,\n        `0 true`,\n        'replaced replaced'\n      )\n    })\n\n    it('object', async () => {\n      await testComponent(\n        mapWritableState(useStore, { count: 'n', myA: 'a' }),\n        `{{ count }} {{ myA }}`,\n        `0 true`,\n        'replaced replaced'\n      )\n    })\n\n    it('setup store', async () => {\n      const useSetupStore = defineStore('setup', () => {\n        const text = ref('initial')\n\n        const textUpper = computed({\n          get: () => text.value.toUpperCase(),\n          set: (v) => {\n            text.value = v\n          },\n        })\n\n        return { text, textUpper }\n      })\n\n      await testComponent(\n        mapWritableState(useSetupStore, ['text', 'textUpper']),\n        `{{ text }} {{ textUpper }}`,\n        `initial INITIAL`,\n        'replaced REPLACED'\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/onAction.spec.ts",
    "content": "import { beforeEach, describe, it, expect, vi } from 'vitest'\nimport { createPinia, defineStore, setActivePinia } from '../src'\nimport { mount } from '@vue/test-utils'\nimport { nextTick } from 'vue'\n\ndescribe('Subscriptions', () => {\n  const useStore = () => {\n    // create a new store\n    setActivePinia(createPinia())\n    return defineStore('main', {\n      state: () => ({\n        user: 'Eduardo',\n      }),\n      actions: {\n        direct(name: string) {\n          this.user = name\n        },\n        patchObject(user: string) {\n          this.$patch({ user })\n        },\n        patchFn(name: string) {\n          this.$patch((state) => {\n            state.user = name\n          })\n        },\n        async asyncUpperName() {\n          return this.user.toUpperCase()\n        },\n        upperName() {\n          return this.user.toUpperCase()\n        },\n        throws(e: any) {\n          throw e\n        },\n        async rejects(e: any) {\n          throw e\n        },\n      },\n    })()\n  }\n\n  let store: ReturnType<typeof useStore>\n  beforeEach(() => {\n    store = useStore()\n  })\n\n  it('fires callback when action is called', () => {\n    const spy = vi.fn()\n    store.$onAction(spy)\n    store.direct('Cleiton')\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith(\n      expect.objectContaining({\n        name: 'direct',\n        args: ['Cleiton'],\n        store,\n      })\n    )\n  })\n\n  it('removes the callback when unsubscribe is called', () => {\n    const spy = vi.fn()\n    const unsubscribe = store.$onAction(spy)\n    unsubscribe()\n    store.direct('Cleiton')\n    expect(spy).not.toHaveBeenCalled()\n  })\n\n  it('can register multiple onAction', async () => {\n    const spy1 = vi.fn()\n    const spy2 = vi.fn()\n    store.$onAction(({ after }) => {\n      after(spy1)\n    })\n    store.$onAction(({ after }) => {\n      after(spy2)\n    })\n\n    await expect(store.asyncUpperName()).resolves.toBe('EDUARDO')\n    expect(spy2).toHaveBeenCalledTimes(1)\n    expect(spy1).toHaveBeenCalledTimes(1)\n  })\n\n  it('calls after with the returned value', async () => {\n    const spy = vi.fn()\n    // Cannot destructure because of https://github.com/microsoft/TypeScript/issues/38020\n    store.$onAction((context) => {\n      if (context.name === 'upperName') {\n        context.after((ret) => {\n          // @ts-expect-error\n          ret * 2\n          ret.toUpperCase()\n        })\n      }\n      context.after(spy)\n    })\n    expect(store.upperName()).toBe('EDUARDO')\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith('EDUARDO')\n  })\n\n  it('calls after with the resolved value', async () => {\n    const spy = vi.fn()\n    store.$onAction(({ after }) => {\n      after(spy)\n    })\n    await expect(store.asyncUpperName()).resolves.toBe('EDUARDO')\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith('EDUARDO')\n  })\n\n  it('calls onError when it throws', () => {\n    const spy = vi.fn()\n    store.$onAction(({ onError }) => {\n      onError(spy)\n    })\n    expect(() => store.throws('fail')).toThrow()\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith('fail')\n  })\n\n  it('calls onError when it rejects', async () => {\n    const spy = vi.fn()\n    expect.assertions(3)\n    store.$onAction(({ onError }) => {\n      onError(spy)\n    })\n    await expect(store.rejects('fail')).rejects.toBe('fail')\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(spy).toHaveBeenCalledWith('fail')\n  })\n\n  it('listeners are not affected when unsubscribe is called multiple times', () => {\n    const func1 = vi.fn()\n    const func2 = vi.fn()\n    const unsubscribe1 = store.$onAction(func1)\n    store.$onAction(func2)\n    unsubscribe1()\n    unsubscribe1()\n    store.direct('Cleiton')\n    expect(func1).not.toHaveBeenCalled()\n    expect(func2).toHaveBeenCalledTimes(1)\n  })\n\n  it('can listen to setup actions within other actions thanks to `action`', () => {\n    const store = defineStore('id', ({ action }) => {\n      const a1 = action(() => 1)\n      const a2 = action(() => a1() * 2)\n      return { a1, a2 }\n    })()\n    const spy = vi.fn()\n    store.$onAction(spy)\n    store.a1()\n    expect(spy).toHaveBeenCalledTimes(1)\n\n    store.a2()\n    expect(spy).toHaveBeenCalledTimes(3)\n    expect(spy).toHaveBeenNthCalledWith(\n      2,\n      expect.objectContaining({ name: 'a2' })\n    )\n    expect(spy).toHaveBeenNthCalledWith(\n      3,\n      expect.objectContaining({ name: 'a1' })\n    )\n  })\n\n  describe('multiple store instances', () => {\n    const useStore = defineStore('main', {\n      state: () => ({\n        name: 'Eduardo',\n      }),\n\n      actions: {\n        changeName(name: string) {\n          this.name = name\n        },\n      },\n    })\n\n    it('triggers subscribe only once', async () => {\n      setActivePinia(createPinia())\n      const s1 = useStore()\n      const s2 = useStore()\n\n      expect(s2).toBe(s1)\n\n      const spy1 = vi.fn()\n      const spy2 = vi.fn()\n\n      s1.$onAction(spy1)\n      s2.$onAction(spy2)\n\n      expect(spy1).toHaveBeenCalledTimes(0)\n      expect(spy2).toHaveBeenCalledTimes(0)\n\n      s1.changeName('Edu')\n\n      expect(spy1).toHaveBeenCalledTimes(1)\n      expect(spy2).toHaveBeenCalledTimes(1)\n    })\n\n    it('removes on unmount', async () => {\n      const pinia = createPinia()\n      const spy1 = vi.fn()\n      const spy2 = vi.fn()\n\n      const wrapper = mount(\n        {\n          setup() {\n            const s1 = useStore()\n            s1.$onAction(spy1)\n          },\n          template: `<p/>`,\n        },\n        { global: { plugins: [pinia] } }\n      )\n\n      const s1 = useStore()\n      const s2 = useStore()\n\n      s2.$onAction(spy2)\n\n      expect(spy1).toHaveBeenCalledTimes(0)\n      expect(spy2).toHaveBeenCalledTimes(0)\n\n      s1.changeName('Cleiton')\n\n      expect(spy2).toHaveBeenCalledTimes(1)\n      expect(spy1).toHaveBeenCalledTimes(1)\n\n      s1.changeName('other')\n      expect(spy1).toHaveBeenCalledTimes(2)\n      expect(spy2).toHaveBeenCalledTimes(2)\n\n      wrapper.unmount()\n      await nextTick()\n\n      s1.changeName('again')\n      expect(spy1).toHaveBeenCalledTimes(2)\n      expect(spy2).toHaveBeenCalledTimes(3)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/pinia/stores/cart.ts",
    "content": "import { defineStore } from '../../../src'\nimport { useUserStore } from './user'\n\nexport const useCartStore = defineStore('cart', {\n  state: () => ({\n    id: 2,\n    rawItems: [] as string[],\n  }),\n  getters: {\n    items: (state) =>\n      state.rawItems.reduce(\n        (items, item) => {\n          const existingItem = items.find((it) => it.name === item)\n\n          if (!existingItem) {\n            items.push({ name: item, amount: 1 })\n          } else {\n            existingItem.amount++\n          }\n\n          return items\n        },\n        [] as { name: string; amount: number }[]\n      ),\n  },\n\n  actions: {\n    addItem(name: string) {\n      this.rawItems.push(name)\n    },\n\n    removeItem(name: string) {\n      const i = this.rawItems.indexOf(name)\n      if (i > -1) this.$state.rawItems.splice(i, 1)\n    },\n\n    // TODO: use multiple stores\n    // https://github.com/vuejs/vue-next-internal-discussions/issues/22\n    async purchaseItems() {\n      const user = useUserStore()\n      if (!user.name) return\n\n      // console.log('Purchasing', this.items)\n      const n = this.items.length\n      this.rawItems = []\n\n      return { amount: n, user: user.name }\n    },\n  },\n})\n\nexport type CartStore = ReturnType<typeof useCartStore>\n\nexport function addItem(name: string) {\n  const store = useCartStore()\n  store.rawItems.push(name)\n}\n\nexport function removeItem(name: string) {\n  const store = useCartStore()\n  const i = store.rawItems.indexOf(name)\n  if (i > -1) store.rawItems.splice(i, 1)\n}\n\nexport async function purchaseItems() {\n  const cart = useCartStore()\n  const user = useUserStore()\n  if (!user.name) return\n\n  console.log('Purchasing', cart.items)\n  const n = cart.items.length\n  cart.rawItems = []\n\n  return n\n}\n"
  },
  {
    "path": "packages/pinia/__tests__/pinia/stores/combined.ts",
    "content": "// @ts-nocheck TODO: implement it or do something different for combined stores\nimport { useUserStore } from './user'\nimport { useCartStore } from './cart'\nimport { pinia, CombinedState } from '../../../src/pinia'\n// in this file we could import other stores that use one each other while\n// avoiding any recursive import\n\nexport function _test() {\n  type S = CombinedState<{\n    user: typeof useUserStore\n    cart: typeof useCartStore\n  }>\n\n  let a: S\n\n  a.user.isAdmin = false\n  a.cart.rawItems.push()\n}\n"
  },
  {
    "path": "packages/pinia/__tests__/pinia/stores/user.ts",
    "content": "import { defineStore } from '../../../src'\n\nfunction apiLogin(a: string, p: string) {\n  if (a === 'ed' && p === 'ed') return Promise.resolve({ isAdmin: true })\n  return Promise.reject(new Error('invalid credentials'))\n}\n\nexport const useUserStore = defineStore('user', {\n  state: () => ({\n    name: 'Eduardo',\n    isAdmin: true,\n  }),\n  actions: {\n    async login(user: string, password: string) {\n      const userData = await apiLogin(user, password)\n\n      this.$patch({\n        name: user,\n        ...userData,\n      })\n    },\n\n    logout() {\n      this.login('a', 'b').then(() => {})\n\n      this.$patch({\n        name: '',\n        isAdmin: false,\n      })\n    },\n  },\n  getters: {\n    test(state) {\n      return state.name.toUpperCase()\n    },\n  },\n})\n\nexport type UserStore = ReturnType<typeof useUserStore>\n\n// let a: WrapStoreWithId<UserStore>\n\nexport function logout() {\n  const store = useUserStore()\n\n  store.login('e', 'e').then(() => {})\n\n  store.$patch({\n    name: '',\n    isAdmin: false,\n  })\n\n  // we could do other stuff like redirecting the user\n}\n"
  },
  {
    "path": "packages/pinia/__tests__/rootState.spec.ts",
    "content": "import { describe, it, expect } from 'vitest'\nimport { createPinia, defineStore } from '../src'\nimport { mockWarn } from './vitest-mock-warn'\n\ndescribe('Root State', () => {\n  mockWarn()\n  const useA = defineStore('a', {\n    state: () => ({ a: 'a' }),\n  })\n\n  const useB = defineStore('b', {\n    state: () => ({ b: 'b' }),\n  })\n\n  it('warns if creating a store without a pinia', () => {\n    expect(() => useA()).toThrowError(/there was no active Pinia/)\n  })\n\n  it('works with no stores', () => {\n    expect(createPinia().state.value).toEqual({})\n  })\n\n  it('retrieves the root state of one store', () => {\n    const pinia = createPinia()\n    useA(pinia)\n    expect(pinia.state.value).toEqual({\n      a: { a: 'a' },\n    })\n  })\n\n  it('does not mix up different applications', () => {\n    const pinia1 = createPinia()\n    const pinia2 = createPinia()\n    useA(pinia1)\n    useB(pinia2)\n    expect(pinia1.state.value).toEqual({\n      a: { a: 'a' },\n    })\n    expect(pinia2.state.value).toEqual({\n      b: { b: 'b' },\n    })\n  })\n\n  it('can hold multiple stores', () => {\n    const pinia1 = createPinia()\n    useA(pinia1)\n    useB(pinia1)\n    expect(pinia1.state.value).toEqual({\n      a: { a: 'a' },\n      b: { b: 'b' },\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/ssr.spec.ts",
    "content": "/**\n * @vitest-environment node\n */\nimport { describe, it, expect } from 'vitest'\nimport {\n  createPinia,\n  defineStore,\n  getActivePinia,\n  setActivePinia,\n  shouldHydrate,\n} from '../src'\nimport { Component, createSSRApp, inject, ref, computed, customRef } from 'vue'\nimport { renderToString, ssrInterpolate } from '@vue/server-renderer'\nimport { useUserStore } from './pinia/stores/user'\nimport { useCartStore } from './pinia/stores/cart'\nimport { mockConsoleError, mockWarn } from './vitest-mock-warn'\n\ndescribe('SSR', () => {\n  mockWarn()\n  mockConsoleError()\n\n  const App = {\n    ssrRender(ctx: any, push: any, _parent: any) {\n      push(\n        `<div>${ssrInterpolate(ctx.user.name)}: ${ssrInterpolate(\n          ctx.cart.items\n        )}</div>`\n      )\n    },\n    setup() {\n      const cart = useCartStore()\n      const user = useUserStore()\n      user.name = inject('name', 'default')\n      cart.addItem('one')\n      return { cart, user }\n    },\n  }\n\n  function createMyApp(MyApp: Component = App) {\n    const app = createSSRApp(MyApp)\n    const pinia = createPinia()\n    app.use(pinia)\n    // const rootEl = document.createElement('div')\n    // document.body.appendChild(rootEl)\n\n    return { app, pinia }\n  }\n\n  it('keeps apps separated', async () => {\n    const { app: a1 } = createMyApp()\n    const { app: a2 } = createMyApp()\n\n    expect(await renderToString(a1)).toMatchInlineSnapshot(`\n      \"<div>default: [\n        {\n          &quot;name&quot;: &quot;one&quot;,\n          &quot;amount&quot;: 1\n        }\n      ]</div>\"\n    `)\n    expect(await renderToString(a2)).toMatchInlineSnapshot(`\n      \"<div>default: [\n        {\n          &quot;name&quot;: &quot;one&quot;,\n          &quot;amount&quot;: 1\n        }\n      ]</div>\"\n    `)\n  })\n\n  it('automatically hydrates', async () => {\n    const { app, pinia } = createMyApp({\n      ssrRender(ctx: any, push: any, _parent: any) {\n        push(\n          `<p>${ssrInterpolate(ctx.user.name)}: ${ssrInterpolate(\n            ctx.cart.rawItems.join(', ')\n          )}</p>`\n        )\n      },\n      setup() {\n        const cart = useCartStore()\n        const user = useUserStore()\n        return { cart, user }\n      },\n    })\n\n    pinia.state.value.user = {\n      name: 'Tom',\n      isAdmin: false,\n    }\n\n    pinia.state.value.cart = {\n      id: 10,\n      rawItems: ['water', 'water', 'apples'],\n    }\n\n    expect(await renderToString(app)).toBe(`<p>Tom: water, water, apples</p>`)\n  })\n\n  it('hydrates custom refs', async () => {\n    function useCustomRef() {\n      let value = 3\n      return customRef((track, trigger) => ({\n        get() {\n          track()\n          return value\n        },\n        set(newValue: number) {\n          value = newValue\n          trigger()\n        },\n      }))\n    }\n\n    const useMainOptions = defineStore('main-options', {\n      state: () => ({\n        customRef: useCustomRef(),\n      }),\n    })\n\n    const { app } = createMyApp({\n      ssrRender(ctx: any, push: any, _parent: any) {\n        push(`<p>${ssrInterpolate(ctx.store.customRef)}</p>`)\n      },\n      setup() {\n        const store = useMainOptions()\n        return { store }\n      },\n    })\n\n    expect(await renderToString(app)).toBe(`<p>3</p>`)\n  })\n\n  it('can use a different store', async () => {\n    const { app: a1 } = createMyApp()\n    const { app: a2 } = createMyApp()\n    a1.provide('name', 'a1')\n    a2.provide('name', 'a2')\n\n    expect(await renderToString(a1)).toMatchInlineSnapshot(`\n      \"<div>a1: [\n        {\n          &quot;name&quot;: &quot;one&quot;,\n          &quot;amount&quot;: 1\n        }\n      ]</div>\"\n    `)\n    expect(await renderToString(a2)).toMatchInlineSnapshot(`\n      \"<div>a2: [\n        {\n          &quot;name&quot;: &quot;one&quot;,\n          &quot;amount&quot;: 1\n        }\n      ]</div>\"\n    `)\n  })\n\n  it('accepts a store with no state', () => {\n    const pinia = createPinia()\n    pinia.state.value.a = { start: 'start' }\n    const store = defineStore('a', {})(pinia)\n    expect(store.$state).toEqual({ start: 'start' })\n  })\n\n  // NOTE: added to make things easier but people should avoid creating objects\n  // with `Object.create(null)` and store them in pinia stores\n  // https://github.com/vuejs/pinia/issues/2837\n  it('can hydrate objects without a a null prototype chain', () => {\n    const obj = Object.create(null)\n    expect(() => {\n      shouldHydrate(obj)\n    }).not.toThrow()\n  })\n\n  it('errors if getActivePinia called outside of context', async () => {\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    expect(getActivePinia()).toBe(pinia)\n    expect('Pinia instance not found in context').toHaveBeenErrored()\n  })\n\n  describe('Setup Store', () => {\n    const useStore = defineStore('main', () => {\n      const count = ref(0)\n      const name = ref('Eduardo')\n      const double = computed(() => count.value * 2)\n\n      function increment() {\n        count.value++\n      }\n\n      return { name, count, double, increment }\n    })\n\n    const App = {\n      ssrRender(ctx: any, push: any, _parent: any) {\n        push(\n          `<button>${ssrInterpolate(ctx.store.count)};${ssrInterpolate(\n            ctx.store.double\n          )};${ssrInterpolate(ctx.store.name)}</button>`\n        )\n      },\n      setup() {\n        const store = useStore()\n        store.count++\n\n        return { store }\n      },\n    }\n\n    it('works', async () => {\n      const { pinia, app } = createMyApp(App)\n\n      pinia.state.value.main = {\n        count: 2,\n        name: 'Eduardo',\n      }\n\n      expect(await renderToString(app)).toBe('<button>3;6;Eduardo</button>')\n    })\n\n    it('store can be changed before rendering', async () => {\n      const { pinia, app } = createMyApp(App)\n\n      pinia.state.value.main = {\n        count: 2,\n        name: 'Eduardo',\n      }\n\n      const store = useStore(pinia)\n      store.count = 10\n\n      expect(await renderToString(app)).toBe('<button>11;22;Eduardo</button>')\n    })\n\n    it('pinia can be changed before rendering', async () => {\n      const { pinia, app } = createMyApp(App)\n\n      pinia.state.value.main = {\n        count: 0,\n        name: 'Eduardo',\n      }\n\n      // create the store before changing\n      useStore(pinia)\n      pinia.state.value.main.name = 'Ed'\n\n      expect(await renderToString(app)).toBe('<button>1;2;Ed</button>')\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/state.spec.ts",
    "content": "import { beforeEach, describe, it, expect, vi, Mock } from 'vitest'\nimport { createPinia, defineStore, setActivePinia, skipHydrate } from '../src'\nimport { computed, nextTick, reactive, ref, watch, customRef } from 'vue'\n\ndescribe('State', () => {\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  const useStore = defineStore('main', {\n    state: () => ({\n      name: 'Eduardo',\n      counter: 0,\n      nested: { n: 0 },\n    }),\n  })\n\n  it('can directly access state at the store level', () => {\n    const store = useStore()\n    expect(store.name).toBe('Eduardo')\n    store.name = 'Ed'\n    expect(store.name).toBe('Ed')\n  })\n\n  it('state is reactive', () => {\n    const store = useStore()\n    const upperCased = computed(() => store.name.toUpperCase())\n    expect(upperCased.value).toBe('EDUARDO')\n    store.name = 'Ed'\n    expect(upperCased.value).toBe('ED')\n  })\n\n  it('can be set with patch', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    store.$patch({ name: 'a' })\n\n    expect(store.name).toBe('a')\n    expect(store.$state.name).toBe('a')\n    expect(pinia.state.value.main.name).toBe('a')\n  })\n\n  it('can be set on store', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    store.name = 'a'\n\n    expect(store.name).toBe('a')\n    expect(store.$state.name).toBe('a')\n    expect(pinia.state.value.main.name).toBe('a')\n  })\n\n  it('can be set on store.$state', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    store.$state.name = 'a'\n\n    expect(store.name).toBe('a')\n    expect(store.$state.name).toBe('a')\n    expect(pinia.state.value.main.name).toBe('a')\n  })\n\n  it('can be nested set with patch', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    store.$patch({ nested: { n: 3 } })\n\n    expect(store.nested.n).toBe(3)\n    expect(store.$state.nested.n).toBe(3)\n    expect(pinia.state.value.main.nested.n).toBe(3)\n  })\n\n  it('can be nested set on store', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    store.nested.n = 3\n\n    expect(store.nested.n).toBe(3)\n    expect(store.$state.nested.n).toBe(3)\n    expect(pinia.state.value.main.nested.n).toBe(3)\n  })\n\n  it('can be nested set on store.$state', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    store.$state.nested.n = 3\n\n    expect(store.nested.n).toBe(3)\n    expect(store.$state.nested.n).toBe(3)\n    expect(pinia.state.value.main.nested.n).toBe(3)\n  })\n\n  it('state can be watched', async () => {\n    const store = useStore()\n    const spy = vi.fn()\n    watch(() => store.name, spy)\n    expect(spy).not.toHaveBeenCalled()\n    store.name = 'Ed'\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(1)\n  })\n\n  it('state can be watched when a ref is given', async () => {\n    const store = useStore()\n    const spy = vi.fn()\n    watch(() => store.name, spy)\n    expect(spy).not.toHaveBeenCalled()\n    const nameRef = ref('Ed')\n    // @ts-expect-error\n    store.$state.name = nameRef\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(1)\n  })\n\n  it('can be given a ref', () => {\n    const pinia = createPinia()\n    const store = useStore(pinia)\n\n    // If the ref is directly set to the store, it won't work,\n    // it must be set into the `store.$state` so it connects to pinia\n    // store.name = ref('Ed')\n\n    // @ts-expect-error\n    store.$state.name = ref('Ed')\n\n    expect(store.name).toBe('Ed')\n    expect(store.$state.name).toBe('Ed')\n    expect(pinia.state.value.main.name).toBe('Ed')\n\n    store.name = 'Other'\n    expect(store.name).toBe('Other')\n    expect(store.$state.name).toBe('Other')\n    expect(pinia.state.value.main.name).toBe('Other')\n  })\n\n  it('unwraps refs', () => {\n    const name = ref('Eduardo')\n    const counter = ref(0)\n    const double = computed({\n      get: () => counter.value * 2,\n      set(val) {\n        counter.value = val / 2\n      },\n    })\n\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    const useStore = defineStore('main', {\n      state: () => ({\n        name,\n        counter,\n        double,\n      }),\n    })\n\n    const store = useStore()\n\n    expect(store.name).toBe('Eduardo')\n    expect(store.$state.name).toBe('Eduardo')\n    expect(pinia.state.value.main).toEqual({\n      name: 'Eduardo',\n      double: 0,\n      counter: 0,\n    })\n\n    name.value = 'Ed'\n    expect(store.name).toBe('Ed')\n    expect(store.$state.name).toBe('Ed')\n    expect(pinia.state.value.main.name).toBe('Ed')\n\n    store.name = 'Edu'\n    expect(store.name).toBe('Edu')\n\n    store.$patch({ counter: 2 })\n    expect(store.counter).toBe(2)\n    expect(counter.value).toBe(2)\n  })\n\n  it('can reset the state', () => {\n    const store = useStore()\n    store.name = 'Ed'\n    store.nested.n++\n    store.$reset()\n    expect(store.$state).toEqual({\n      counter: 0,\n      name: 'Eduardo',\n      nested: {\n        n: 0,\n      },\n    })\n  })\n\n  it('can reset the state of an empty store', () => {\n    const store = defineStore('a', {})(createPinia())\n    expect(store.$state).toEqual({})\n    store.$reset()\n    expect(store.$state).toEqual({})\n  })\n\n  it('can hydrate refs', () => {\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    pinia.state.value.main = {\n      stuff: 1,\n      a: 2,\n      // nested: { a: 2 },\n      state: {\n        count: 5,\n        a: 2,\n        // nested: { a: 2 },\n      },\n    }\n\n    const stuff = ref(2)\n    const useStore = defineStore('main', () => {\n      const a = ref(0)\n      // const nested = ref({ a })\n      const state = reactive({\n        a,\n        // nested,\n        count: 0,\n      })\n      return {\n        stuff,\n        a,\n        // nested,\n        state,\n        double: computed(() => stuff.value * 2),\n      }\n    })\n\n    const store = useStore()\n\n    expect(stuff.value).toBe(1)\n    expect(store.$state).toEqual({\n      stuff: 1,\n      a: 2,\n      // nested: { a: 2 },\n      state: {\n        // nested: { a: 2 },\n        count: 5,\n        a: 2,\n      },\n    })\n    expect(store.stuff).toBe(1)\n    expect(store.double).toBe(2)\n    expect(store.a).toBe(2)\n    expect(store.state).toEqual({\n      // nested: { a: 2 },\n      a: 2,\n      count: 5,\n    })\n\n    store.a = 0\n    expect(store.a).toBe(0)\n    expect(store.state).toEqual({\n      // nested: { a: 0 },\n      a: 0,\n      count: 5,\n    })\n\n    store.stuff = 5\n    expect(store.stuff).toBe(5)\n    expect(stuff.value).toBe(5)\n    expect(store.$state.stuff).toBe(5)\n    expect(store.double).toBe(10)\n\n    stuff.value = 15\n    expect(store.stuff).toBe(15)\n    expect(stuff.value).toBe(15)\n    expect(store.$state.stuff).toBe(15)\n    expect(store.double).toBe(30)\n  })\n\n  it('hydrates Set in option stores', async () => {\n    const useStore = defineStore('main', {\n      state: () => ({ set: new Set() }),\n    })\n\n    const pinia = createPinia()\n    pinia.state.value.main = {\n      set: new Set([1, 2]),\n    }\n    setActivePinia(pinia)\n\n    const store = useStore()\n    expect(store.set).toBeInstanceOf(Set)\n    expect([...store.set.values()]).toEqual([1, 2])\n  })\n\n  it('hydrates Set in setup stores', async () => {\n    const useStore = defineStore('main', () => {\n      const setRef = ref(new Set())\n      const setReactive = reactive(new Set())\n      return { setRef, setReactive }\n    })\n\n    const pinia = createPinia()\n    pinia.state.value.main = {\n      setRef: new Set([1, 2]),\n      setReactive: new Set([3, 4]),\n    }\n    setActivePinia(pinia)\n\n    const store = useStore()\n    expect(store.setRef).toBeInstanceOf(Set)\n    expect(store.setReactive).toBeInstanceOf(Set)\n    expect([...store.setRef.values()]).toEqual([1, 2])\n    expect([...store.setReactive.values()]).toEqual([3, 4])\n  })\n\n  it('hydrates Map in option stores', async () => {\n    const useStore = defineStore('main', {\n      state: () => ({ map: new Map() }),\n    })\n\n    const map = new Map()\n\n    map.set('ssr', 'test')\n    map.set('other', 'test2')\n\n    const pinia = createPinia()\n    pinia.state.value.main = { map }\n    setActivePinia(pinia)\n\n    const store = useStore()\n    expect(store.map).toBeInstanceOf(Map)\n    expect([...store.map.entries()]).toEqual([\n      ['ssr', 'test'],\n      ['other', 'test2'],\n    ])\n  })\n\n  it('hydrates Map in setup stores', async () => {\n    const useStore = defineStore('main', () => {\n      const mapRef = ref(new Map())\n      const mapReactive = reactive(new Map())\n      return { mapRef, mapReactive }\n    })\n\n    const mapRef = new Map()\n    const mapReactive = new Map()\n\n    mapRef.set('ref:ssr', 'test')\n    mapRef.set('ref:other', 'test2')\n    mapReactive.set('reactive:ssr', 'test')\n    mapReactive.set('reactive:other', 'test2')\n\n    const pinia = createPinia()\n    pinia.state.value.main = { mapRef, mapReactive }\n    setActivePinia(pinia)\n\n    const store = useStore()\n    expect(store.mapRef).toBeInstanceOf(Map)\n    expect(store.mapReactive).toBeInstanceOf(Map)\n    expect([...store.mapRef.entries()]).toEqual([\n      ['ref:ssr', 'test'],\n      ['ref:other', 'test2'],\n    ])\n    expect([...store.mapReactive.entries()]).toEqual([\n      ['reactive:ssr', 'test'],\n      ['reactive:other', 'test2'],\n    ])\n  })\n\n  describe('custom refs', () => {\n    let spy!: Mock\n    function useCustomRef() {\n      let value = 0\n\n      return customRef((track, trigger) => {\n        spy = vi.fn(function (newValue: number) {\n          value = newValue\n          trigger()\n        })\n        return {\n          get() {\n            track()\n            return value\n          },\n          set: spy as any,\n        }\n      })\n    }\n\n    it('hydrates custom refs options', async () => {\n      const pinia = createPinia()\n      pinia.state.value.main = { myCustom: 24, other: 'ssr' }\n\n      setActivePinia(pinia)\n\n      const useMainOptions = defineStore('main', {\n        state: () => ({ myCustom: useCustomRef(), other: 'start' }),\n        hydrate(storeState, initialState) {\n          // @ts-expect-error: cannot set as a ref\n          storeState.myCustom = useCustomRef()\n          // Object.assign(store, initialState)\n          // const { myCustom, ...rest } = initialState\n          // Object.assign(store, rest)\n        },\n      })\n\n      const main = useMainOptions()\n\n      // skips the value from hydration\n      expect(main.myCustom).toBe(0)\n      expect(main.$state.myCustom).toBe(0)\n      expect(main.other).toBe('ssr')\n      expect(main.$state.other).toBe('ssr')\n\n      expect(spy).toHaveBeenCalledTimes(0)\n      main.myCustom++\n      main.$state.myCustom++\n      main.$patch({ myCustom: 0 })\n      main.$patch((state) => {\n        state.myCustom++\n      })\n\n      expect(main.myCustom).toBe(1)\n      expect(main.$state.myCustom).toBe(1)\n      expect(main.other).toBe('ssr')\n      expect(main.$state.other).toBe('ssr')\n      expect(spy).toHaveBeenCalledTimes(4)\n    })\n\n    it('hydrates custom refs setup', async () => {\n      const pinia = createPinia()\n      pinia.state.value.main = { myCustom: 24 }\n\n      setActivePinia(pinia)\n\n      const useMainOptions = defineStore('main', () => ({\n        myCustom: skipHydrate(useCustomRef()),\n      }))\n\n      const main = useMainOptions()\n\n      // 0 because it skipped hydration\n      expect(main.myCustom).toBe(0)\n      expect(spy).toHaveBeenCalledTimes(0)\n      main.myCustom++\n      main.$state.myCustom++\n      main.$patch({ myCustom: 0 })\n      main.$patch((state) => {\n        state.myCustom++\n      })\n      expect(main.myCustom).toBe(1)\n      expect(spy).toHaveBeenCalledTimes(4)\n    })\n\n    // TODO: should warn of nested skipHydrate() calls\n    it.skip('hydrates custom nested refs setup', async () => {\n      const pinia = createPinia()\n      pinia.state.value.main = { a: { myCustom: 24 } }\n\n      setActivePinia(pinia)\n\n      const useMainOptions = defineStore('main', () => ({\n        a: ref({\n          myCustom: skipHydrate(useCustomRef()),\n        }),\n      }))\n\n      const main = useMainOptions()\n\n      // 0 because it skipped hydration\n      expect(main.a.myCustom).toBe(0)\n      expect(spy).toHaveBeenCalledTimes(0)\n      main.a.myCustom++\n      main.$state.a.myCustom++\n      main.$patch({ a: { myCustom: 0 } })\n      main.$patch((state) => {\n        state.a.myCustom++\n      })\n      expect(main.a.myCustom).toBe(1)\n      expect(spy).toHaveBeenCalledTimes(4)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/store.patch.spec.ts",
    "content": "import { describe, it, expect } from 'vitest'\nimport { reactive, ref } from 'vue'\nimport { createPinia, defineStore, Pinia, setActivePinia } from '../src'\n\ndescribe('store.$patch', () => {\n  const useStore = () => {\n    // create a new store\n    setActivePinia(createPinia())\n    return defineStore('main', {\n      state: () => ({\n        a: true,\n        nested: {\n          foo: 'foo',\n          a: { b: 'string' },\n        },\n        list: [] as number[],\n      }),\n    })()\n  }\n\n  const useArrayStore = () => {\n    // create a new store\n    setActivePinia(createPinia())\n    return defineStore('main', {\n      state: () => ({\n        items: [{ id: 0 }],\n        currentItem: { id: 1 },\n      }),\n    })()\n  }\n\n  it('patches a property without touching the rest', () => {\n    const store = useStore()\n    store.$patch({ a: false })\n    expect(store.$state).toEqual({\n      a: false,\n      nested: {\n        foo: 'foo',\n        a: { b: 'string' },\n      },\n      list: [],\n    })\n\n    expect(store.a).toBe(false)\n  })\n\n  it('replaces whole arrays', () => {\n    const store = useStore()\n    store.$patch({ list: [1, 2] })\n    expect(store.$state.list).toEqual([1, 2])\n    expect(store.list).toEqual([1, 2])\n  })\n\n  it('can patch an item that has been copied to an array', () => {\n    const store = useArrayStore()\n    store.$state.currentItem = { id: 2 }\n    // NOTE: a patch of an object is always recursive, writing in the object, in\n    // place.\n    //store.$patch({ currentItem: { id: 2 } })\n    store.items.push(store.currentItem)\n    // store.$patch({ currentItem: { id: 3 } })\n    store.$state.currentItem = { id: 3 }\n\n    expect(store.$state.items).toEqual([{ id: 0 }, { id: 2 }])\n    expect(store.items).toEqual([{ id: 0 }, { id: 2 }])\n  })\n\n  it('replaces whole nested arrays', () => {\n    const store = useStore()\n    // @ts-expect-error: new state\n    store.$patch({ nested: { list: [1, 2] } })\n    expect(store.$state.nested).toEqual({\n      foo: 'foo',\n      a: { b: 'string' },\n      list: [1, 2],\n    })\n    // @ts-expect-error: new state\n    store.$patch({ nested: { list: [] } })\n    expect(store.$state.nested).toEqual({\n      foo: 'foo',\n      a: { b: 'string' },\n      list: [],\n    })\n  })\n\n  it('patches using a function', () => {\n    const store = useStore()\n    store.$patch((state) => {\n      expect(state).toBe(store.$state)\n      state.a = !state.a\n      state.list.push(1)\n    })\n    expect(store.$state).toEqual({\n      a: false,\n      nested: {\n        foo: 'foo',\n        a: { b: 'string' },\n      },\n      list: [1],\n    })\n  })\n\n  it('patches a nested property without touching the rest', () => {\n    const store = useStore()\n    store.$patch({ nested: { foo: 'bar' } })\n    expect(store.$state).toEqual({\n      a: true,\n      nested: {\n        foo: 'bar',\n        a: { b: 'string' },\n      },\n      list: [],\n    })\n    store.$patch({ nested: { a: { b: 'hello' } } })\n    expect(store.$state).toEqual({\n      a: true,\n      nested: {\n        foo: 'bar',\n        a: { b: 'hello' },\n      },\n      list: [],\n    })\n  })\n\n  it('patches multiple properties at the same time', () => {\n    const store = useStore()\n    store.$patch({ a: false, nested: { foo: 'hello' } })\n    expect(store.$state).toEqual({\n      a: false,\n      nested: {\n        foo: 'hello',\n        a: { b: 'string' },\n      },\n      list: [],\n    })\n  })\n\n  describe('skipping nested objects', () => {\n    const useStore = (pinia?: Pinia) => {\n      // create a new store\n      setActivePinia(pinia || createPinia())\n      return defineStore('main', {\n        state: () => ({\n          arr: [] as any[],\n          name: 'Eduardo',\n          item: { a: 0, b: 0 } as null | { a: number; b?: number },\n        }),\n      })()\n    }\n    // const useStore = (pinia?: Pinia) => {\n    //   // create a new store\n    //   setActivePinia(pinia || createPinia())\n    //   return defineStore('main', () => {\n    //     const arr = ref([] as any[])\n    //     const item = ref({ a: 0, b: 0 } as null | { a: number; b?: number })\n\n    //     return { arr, item }\n    //   })()\n    // }\n\n    it('ref of primitive', () => {\n      const pinia = createPinia()\n      const store = useStore(pinia)\n      const name = ref('Edu')\n      // @ts-expect-error: because it's a ref\n      store.$patch({ name })\n      expect(pinia.state.value.main.name).toEqual('Edu')\n      expect(store.$state.name).toEqual('Edu')\n      expect(store.name).toEqual('Edu')\n    })\n\n    it('ref of object', () => {\n      const pinia = createPinia()\n      const store = useStore(pinia)\n      const item = ref({ a: 1, b: 1 })\n      const oldItem = store.item\n      // @ts-expect-error: because it's a ref\n      store.$state.item = item\n      expect(oldItem).toEqual({ a: 0, b: 0 })\n      expect(pinia.state.value.main.item).toEqual({ a: 1, b: 1 })\n      expect(store.$state.item).toEqual({ a: 1, b: 1 })\n      expect(store.item).toEqual({ a: 1, b: 1 })\n\n      // @ts-expect-error: because it's a ref\n      store.$patch({ item: ref({ a: 2, b: 2 }) })\n      expect(pinia.state.value.main.item).toEqual({ a: 2, b: 2 })\n      expect(store.$state.item).toEqual({ a: 2, b: 2 })\n      expect(store.item).toEqual({ a: 2, b: 2 })\n    })\n\n    it('nested ref', () => {\n      const store = useStore()\n      const item = ref({ nested: { a: 1, b: 1 } })\n      const oldItem = store.item\n      store.$patch({ item: item.value.nested })\n      expect(oldItem).toEqual({ a: 0, b: 0 })\n      expect(store.item).toEqual({ a: 1, b: 1 })\n    })\n\n    it('reactive', () => {\n      const store = useStore()\n      const item = reactive({ a: 1, b: 1 })\n      const oldItem = store.item\n      store.$patch({ item })\n      expect(oldItem).toEqual({ a: 0, b: 0 })\n      expect(store.item).toEqual({ a: 1, b: 1 })\n    })\n\n    it('from store', () => {\n      const store = useStore()\n      store.arr.push({ a: 1, b: 1 })\n      const oldItem = store.item\n      store.$patch({ item: store.arr[0] })\n      expect(oldItem).toEqual({ a: 0, b: 0 })\n      expect(store.item).toEqual({ a: 1, b: 1 })\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/store.spec.ts",
    "content": "import { beforeEach, describe, it, expect, vi } from 'vitest'\nimport { createPinia, defineStore, setActivePinia } from '../src'\nimport { mount } from '@vue/test-utils'\nimport { defineComponent, getCurrentInstance, nextTick, watch } from 'vue'\nimport { mockWarn } from './vitest-mock-warn'\n\ndescribe('Store', () => {\n  mockWarn()\n\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  const useStore = defineStore('main', {\n    state: () => ({\n      a: true,\n      nested: {\n        foo: 'foo',\n        a: { b: 'string' },\n      },\n    }),\n  })\n\n  it('reuses a store', () => {\n    const useStore = defineStore('main', {})\n    expect(useStore()).toBe(useStore())\n  })\n\n  it('works with id as first argument', () => {\n    const useStore = defineStore('main', {\n      state: () => ({\n        a: true,\n        nested: {\n          foo: 'foo',\n          a: { b: 'string' },\n        },\n      }),\n    })\n    expect(useStore()).toBe(useStore())\n    const useStoreEmpty = defineStore('main', {})\n    expect(useStoreEmpty()).toBe(useStoreEmpty())\n  })\n\n  it('sets the initial state', () => {\n    const store = useStore()\n    expect(store.$state).toEqual({\n      a: true,\n      nested: {\n        foo: 'foo',\n        a: { b: 'string' },\n      },\n    })\n  })\n\n  it('works without setting the active pinia', async () => {\n    setActivePinia(undefined)\n    const pinia = createPinia()\n    const useStore = defineStore('main', {\n      state: () => ({ n: 0 }),\n    })\n    const TestComponent = defineComponent({\n      template: `<div>{{ store. n }}</div>`,\n      setup() {\n        const store = useStore()\n        return { store }\n      },\n    })\n    const w1 = mount(TestComponent, { global: { plugins: [pinia] } })\n    const w2 = mount(TestComponent, { global: { plugins: [pinia] } })\n    expect(w1.text()).toBe('0')\n    expect(w2.text()).toBe('0')\n\n    w1.vm.store.n++\n    await w1.vm.$nextTick()\n    expect(w1.text()).toBe('1')\n    expect(w2.text()).toBe('1')\n  })\n\n  it('can be reset', () => {\n    const store = useStore()\n    store.$state.a = false\n    const spy = vi.fn()\n    store.$subscribe(spy, { flush: 'sync' })\n    expect(spy).not.toHaveBeenCalled()\n    store.$reset()\n    expect(spy).toHaveBeenCalledTimes(1)\n    store.$state.nested.foo = 'bar'\n    expect(spy).toHaveBeenCalledTimes(2)\n    expect(store.$state).toEqual({\n      a: true,\n      nested: {\n        foo: 'bar',\n        a: { b: 'string' },\n      },\n    })\n\n    expect(store.nested.foo).toBe('bar')\n  })\n\n  it('can create an empty state if no state option is provided', () => {\n    const store = defineStore('some', {})()\n\n    expect(store.$state).toEqual({})\n  })\n\n  it('can hydrate the state', () => {\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    const useStore = defineStore('main', {\n      state: () => ({\n        a: true,\n        nested: {\n          foo: 'foo',\n          a: { b: 'string' },\n        },\n      }),\n    })\n\n    pinia.state.value.main = {\n      a: false,\n      nested: {\n        foo: 'bar',\n        a: { b: 'string 2' },\n      },\n    }\n\n    const store = useStore()\n\n    expect(store.$state).toEqual({\n      a: false,\n      nested: {\n        foo: 'bar',\n        a: { b: 'string 2' },\n      },\n    })\n  })\n\n  it('can replace its state', () => {\n    const store = useStore()\n    const spy = vi.fn()\n    watch(() => store.a, spy, { flush: 'sync' })\n    expect(store.a).toBe(true)\n\n    expect(spy).toHaveBeenCalledTimes(0)\n    // TODO: remove once plugin state achieve generics\n    // @ts-expect-error\n    store.$state = {\n      a: false,\n      nested: {\n        foo: 'bar',\n        a: {\n          b: 'hey',\n        },\n      },\n    }\n    expect(spy).toHaveBeenCalledTimes(1)\n\n    expect(store.$state).toEqual({\n      a: false,\n      nested: {\n        foo: 'bar',\n        a: { b: 'hey' },\n      },\n    })\n  })\n\n  it('do not share the state between same id store', () => {\n    const store = useStore()\n    const store2 = useStore(createPinia())\n    expect(store.$state).not.toBe(store2.$state)\n    store.$state.nested.a.b = 'hey'\n    expect(store2.$state.nested.a.b).toBe('string')\n  })\n\n  it('should outlive components', async () => {\n    const pinia = createPinia()\n    const useStore = defineStore('main', {\n      state: () => ({ n: 0 }),\n    })\n\n    const wrapper = mount(\n      {\n        setup() {\n          const store = useStore()\n\n          return { store }\n        },\n\n        template: `n: {{ store.n }}`,\n      },\n      {\n        global: {\n          plugins: [pinia],\n        },\n      }\n    )\n\n    expect(wrapper.html()).toBe('n: 0')\n\n    const store = useStore(pinia)\n\n    const spy = vi.fn()\n    watch(() => store.n, spy)\n\n    expect(spy).toHaveBeenCalledTimes(0)\n    store.n++\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(1)\n    expect(wrapper.html()).toBe('n: 1')\n\n    wrapper.unmount()\n    await nextTick()\n    store.n++\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(2)\n  })\n\n  it('should not break getCurrentInstance', () => {\n    let store: ReturnType<typeof useStore> | undefined\n\n    let i1: any = {}\n    let i2: any = {}\n    const wrapper = mount(\n      {\n        setup() {\n          i1 = getCurrentInstance()\n          store = useStore()\n          i2 = getCurrentInstance()\n\n          return { store }\n        },\n\n        template: `a: {{ store.a }}`,\n      },\n      {\n        global: {\n          plugins: [createPinia()],\n        },\n      }\n    )\n\n    expect(i1 === i2).toBe(true)\n\n    wrapper.unmount()\n  })\n\n  it('reuses stores from parent components', () => {\n    let s1, s2\n    const useStore = defineStore('one', {})\n    const pinia = createPinia()\n\n    const Child = defineComponent({\n      setup() {\n        s2 = useStore()\n      },\n      template: `child`,\n    })\n\n    mount(\n      {\n        setup() {\n          s1 = useStore()\n          return { s1 }\n        },\n        components: { Child },\n        template: `<child/>`,\n      },\n      { global: { plugins: [pinia] } }\n    )\n\n    expect(s1).toBeDefined()\n    expect(s1 === s2).toBe(true)\n  })\n\n  it('can share the same pinia in two completely different instances', async () => {\n    const useStore = defineStore('one', { state: () => ({ n: 0 }) })\n    const pinia = createPinia()\n\n    const Comp = defineComponent({\n      setup() {\n        const store = useStore()\n        return { store }\n      },\n      template: `{{ store.n }}`,\n    })\n\n    const One = mount(Comp, {\n      global: {\n        plugins: [pinia],\n      },\n    })\n\n    const Two = mount(Comp, {\n      global: {\n        plugins: [pinia],\n      },\n    })\n\n    const store = useStore(pinia)\n\n    expect(One.text()).toBe('0')\n    expect(Two.text()).toBe('0')\n\n    store.n++\n    await nextTick()\n\n    expect(One.text()).toBe('1')\n    expect(Two.text()).toBe('1')\n  })\n\n  it('can be disposed', () => {\n    const pinia = createPinia()\n    const useStore = defineStore('main', { state: () => ({ n: 0 }) })\n\n    const store = useStore(pinia)\n    const spy = vi.fn()\n\n    store.$subscribe(spy, { flush: 'sync' })\n    pinia.state.value.main.n++\n    expect(spy).toHaveBeenCalledTimes(1)\n\n    expect(useStore()).toBe(store)\n    store.$dispose()\n    pinia.state.value.main.n++\n\n    expect(spy).toHaveBeenCalledTimes(1)\n\n    expect(useStore()).not.toBe(store)\n  })\n\n  const warnTextCheckPlainObject = (storeId: string) =>\n    `The \"state\" must be a plain object. It cannot be\\n\\tstate: () => new MyClass()\\nFound in store \"${storeId}\".`\n\n  it('warns when state is created with a class constructor', () => {\n    class MyState {}\n\n    const useMyStore = defineStore('store', { state: () => new MyState() })\n    useMyStore()\n    expect(warnTextCheckPlainObject('store')).toHaveBeenWarned()\n  })\n\n  it('only warns about constructors when store is initially created', () => {\n    class MyState {}\n    const useMyStore = defineStore('arrowInit', { state: () => new MyState() })\n    useMyStore()\n    expect(warnTextCheckPlainObject('arrowInit')).toHaveBeenWarnedTimes(1)\n  })\n\n  it('does not warn when state is created with a plain object', () => {\n    const useMyStore = defineStore('poInit', {\n      state: () => ({ someValue: undefined }),\n    })\n    useMyStore()\n    expect(warnTextCheckPlainObject('poInit')).toHaveBeenWarnedTimes(0)\n  })\n\n  it('warns when state name conflicts with getters name (with id as first argument)', () => {\n    const useStore = defineStore('main', {\n      state: () => ({ anyName: 0 }),\n      getters: { anyName: (state) => state.anyName },\n    })\n    useStore()\n\n    expect(\n      `[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with \"anyName\" in store \"main\".`\n    ).toHaveBeenWarnedTimes(1)\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/storePlugins.spec.ts",
    "content": "import { describe, it, expect, vi } from 'vitest'\nimport { createPinia, defineStore } from '../src'\nimport { mount } from '@vue/test-utils'\nimport { App, computed, ref, toRef, watch } from 'vue'\n\ndeclare module '../src' {\n  export interface PiniaCustomProperties<Id> {\n    pluginN: number\n    uid: App['_uid']\n    hasApp: boolean\n    idFromPlugin: Id\n    globalA: string\n    globalB: string\n    shared: number\n    double: number\n  }\n\n  export interface PiniaCustomStateProperties<S> {\n    // pluginN: 'test' extends Id ? number : never | undefined\n    pluginN: number\n    shared: number\n  }\n}\n\ndescribe('store plugins', () => {\n  const useStore = defineStore('test', {\n    actions: {\n      incrementN() {\n        return this.pluginN++\n      },\n    },\n\n    getters: {\n      doubleN: (state) => state.pluginN * 2,\n    },\n  })\n\n  it('adds properties to stores', () => {\n    const pinia = createPinia()\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    // must call use after installing the plugin\n    pinia.use(({ app, store }) => {\n      if (!Object.hasOwn(store.$state, 'pluginN')) {\n        // @ts-expect-error: cannot be a ref yet\n        store.$state.pluginN = ref(20)\n      }\n      // @ts-expect-error: TODO: allow setting refs\n      store.pluginN = toRef(store.$state, 'pluginN')\n      return { uid: app._uid }\n    })\n\n    const store = useStore(pinia)\n\n    expect(store.$state.pluginN).toBe(20)\n    expect(store.pluginN).toBe(20)\n    expect(store.uid).toBeDefined()\n    // @ts-expect-error: pluginN is a number\n    store.pluginN.notExisting\n    // @ts-expect-error: it should always be 'test'\n    store.idFromPlugin == 'hello'\n  })\n\n  it('overrides $reset', () => {\n    const pinia = createPinia()\n\n    const useStore = defineStore('main', {\n      state: () => ({ n: 0 }),\n    })\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    pinia.use(({ app, store }) => {\n      if (!Object.hasOwn(store.$state, 'pluginN')) {\n        // @ts-expect-error: cannot be a ref yet\n        store.$state.pluginN = ref(20)\n      }\n      // @ts-expect-error: TODO: allow setting refs\n      store.pluginN = toRef(store.$state, 'pluginN')\n\n      const originalReset = store.$reset.bind(store)\n      return {\n        uid: app._uid,\n        $reset() {\n          originalReset()\n          store.pluginN = 20\n        },\n      }\n    })\n\n    const store = useStore(pinia)\n\n    store.pluginN = 200\n    store.$reset()\n    expect(store.$state.pluginN).toBe(20)\n    expect(store.pluginN).toBe(20)\n  })\n\n  it('can install plugins before installing pinia', () => {\n    const pinia = createPinia()\n\n    pinia.use(() => ({ pluginN: 1 }))\n    pinia.use(({ app }) => ({ uid: app._uid }))\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    pinia.use((app) => ({ hasApp: !!app }))\n\n    const store = useStore(pinia)\n\n    expect(store.pluginN).toBe(1)\n    expect(store.uid).toBeDefined()\n    expect(store.hasApp).toBe(true)\n  })\n\n  it('can be used in actions', () => {\n    const pinia = createPinia()\n\n    // must call use after installing the plugin\n    pinia.use(() => {\n      return { pluginN: 20 }\n    })\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    const store = useStore(pinia)\n\n    expect(store.incrementN()).toBe(20)\n  })\n\n  it('can be used in getters', () => {\n    const pinia = createPinia()\n\n    // must call use after installing the plugin\n    pinia.use(() => {\n      return { pluginN: 20 }\n    })\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    const store = useStore(pinia)\n    expect(store.doubleN).toBe(40)\n  })\n\n  it('allows chaining', () => {\n    const pinia = createPinia()\n\n    // must call use after installing the plugin\n    pinia.use(() => ({ globalA: 'a' })).use(() => ({ globalB: 'b' }))\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    const store = useStore(pinia)\n    expect(store.globalA).toBe('a')\n    expect(store.globalB).toBe('b')\n  })\n\n  it('shares the same ref among stores', () => {\n    const pinia = createPinia()\n\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    // must call use after installing the plugin\n    pinia.use(({ app, store }) => {\n      if (!Object.hasOwn(store.$state, 'shared')) {\n        // @ts-expect-error: cannot be a ref yet\n        store.$state.shared = ref(20)\n      }\n      // @ts-expect-error: TODO: allow setting refs\n      store.shared = toRef(store.$state, 'shared')\n    })\n\n    const store = useStore(pinia)\n    const store2 = useStore(pinia)\n\n    expect(store.$state.shared).toBe(20)\n    expect(store.shared).toBe(20)\n    expect(store2.$state.shared).toBe(20)\n    expect(store2.shared).toBe(20)\n\n    store.$state.shared = 10\n    expect(store.$state.shared).toBe(10)\n    expect(store.shared).toBe(10)\n    expect(store2.$state.shared).toBe(10)\n    expect(store2.shared).toBe(10)\n\n    store.shared = 1\n    expect(store.$state.shared).toBe(1)\n    expect(store.shared).toBe(1)\n    expect(store2.$state.shared).toBe(1)\n    expect(store2.shared).toBe(1)\n  })\n\n  it('passes the options of the options store', async () => {\n    const options = {\n      state: () => ({ n: 0 }),\n      actions: {\n        increment() {\n          // @ts-expect-error\n          this.n++\n        },\n      },\n      getters: {\n        a() {\n          return 'a'\n        },\n      },\n    }\n    const useStore = defineStore('main', options)\n    const pinia = createPinia()\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    await new Promise<void>((done) => {\n      pinia.use((context) => {\n        expect(context.options).toEqual(options)\n        done()\n      })\n      useStore(pinia)\n    })\n  })\n\n  it('passes the options of a setup store', async () => {\n    const useStore = defineStore('main', () => {\n      const n = ref(0)\n\n      function increment() {\n        n.value++\n      }\n      const a = computed(() => 'a')\n\n      return { n, increment, a }\n    })\n    const pinia = createPinia()\n    mount({ template: 'none' }, { global: { plugins: [pinia] } })\n\n    await new Promise<void>((done) => {\n      pinia.use((context) => {\n        expect(context.options).toEqual({\n          actions: {\n            increment: expect.any(Function),\n          },\n        })\n        ;(context.store as any).increment()\n        expect((context.store as any).n).toBe(1)\n        done()\n      })\n\n      useStore()\n    })\n  })\n\n  it('run inside store effect', async () => {\n    const pinia = createPinia()\n\n    // must call use after installing the plugin\n    pinia.use(({ store }) => ({\n      // @ts-expect-error: invalid computed\n      double: computed(() => store.$state.n * 2),\n    }))\n\n    const useStore = defineStore('main', {\n      state: () => ({ n: 1 }),\n    })\n\n    mount(\n      {\n        template: 'none',\n        setup() {\n          // create it inside of the component\n          useStore()\n        },\n      },\n      { global: { plugins: [pinia] } }\n    ).unmount()\n\n    const store = useStore(pinia)\n\n    const spy = vi.fn()\n    watch(() => store.double, spy, { flush: 'sync' })\n\n    expect(spy).toHaveBeenCalledTimes(0)\n\n    store.n++\n    expect(spy).toHaveBeenCalledTimes(1)\n  })\n\n  it('only executes plugins once after multiple installs', async () => {\n    const pinia = createPinia()\n\n    const spy = vi.fn()\n    pinia.use(spy)\n\n    for (let i = 0; i < 3; i++) {\n      mount({ template: 'none' }, { global: { plugins: [pinia] } }).unmount()\n    }\n\n    useStore(pinia)\n\n    expect(spy).toHaveBeenCalledTimes(1)\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/storeSetup.spec.ts",
    "content": "import { beforeEach, describe, it, expect, vi } from 'vitest'\nimport { createPinia, defineStore, setActivePinia } from '../src'\nimport { computed, createApp, inject, nextTick, ref, watch } from 'vue'\nimport { mount } from '@vue/test-utils'\n\nfunction expectType<T>(_value: T): void {}\n\ndescribe('store with setup syntax', () => {\n  function mainFn() {\n    const name = ref('Eduardo')\n    const counter = ref(0)\n    function increment(amount = 1) {\n      counter.value += amount\n    }\n    const double = computed(() => counter.value * 2)\n\n    return { name, counter, increment, double }\n  }\n\n  const useStore = defineStore('main', mainFn)\n\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  it('should extract the $state', () => {\n    const store = useStore()\n    expectType<{ name: string; counter: number }>(store.$state)\n    expect(store.$state).toEqual({ name: 'Eduardo', counter: 0 })\n    expect(store.name).toBe('Eduardo')\n    expect(store.counter).toBe(0)\n    expect(store.double).toBe(0)\n    store.increment()\n    expect(store.counter).toBe(1)\n    expect(store.double).toBe(2)\n    expect(store.$state).toEqual({ name: 'Eduardo', counter: 1 })\n    expect(store.$state).not.toHaveProperty('double')\n    expect(store.$state).not.toHaveProperty('increment')\n  })\n\n  it('can store a function', () => {\n    const store = defineStore('main', () => {\n      const fn = ref(() => {})\n      function action() {}\n      return { fn, action }\n    })()\n    expectType<{ fn: () => void }>(store.$state)\n    expect(store.$state).toEqual({ fn: expect.any(Function) })\n    expect(store.fn).toEqual(expect.any(Function))\n    store.action()\n  })\n\n  it('can directly access state at the store level', () => {\n    const store = useStore()\n\n    expect(store.name).toBe('Eduardo')\n    store.name = 'Ed'\n    expect(store.name).toBe('Ed')\n  })\n\n  it('state is reactive', () => {\n    const store = useStore()\n    const upperCased = computed(() => store.name.toUpperCase())\n    expect(upperCased.value).toBe('EDUARDO')\n    store.name = 'Ed'\n    expect(upperCased.value).toBe('ED')\n  })\n\n  it('state can be watched', async () => {\n    const store = useStore()\n    const spy = vi.fn()\n    watch(() => store.name, spy)\n    expect(spy).not.toHaveBeenCalled()\n    store.name = 'Ed'\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(1)\n  })\n\n  // TODO: could be fixed by using computed or getters + setters in store\n  it.skip('state refs can be watched', async () => {\n    const store = useStore()\n    const spy = vi.fn()\n    watch(() => store.name, spy)\n    expect(spy).not.toHaveBeenCalled()\n    const nameRef = ref('Ed')\n    store._p.state.value[store.$id].name = nameRef\n    // @ts-ignore\n    // store.$state.name = nameRef\n    await nextTick()\n    expect(spy).toHaveBeenCalledTimes(1)\n  })\n\n  it('unwraps refs', () => {\n    const name = ref('Eduardo')\n    const counter = ref(0)\n    const double = computed({\n      get: () => counter.value * 2,\n      set(val) {\n        counter.value = val / 2\n      },\n    })\n\n    const pinia = createPinia()\n    setActivePinia(pinia)\n    const useStore = defineStore('main', {\n      state: () => ({\n        name,\n        counter,\n        double,\n      }),\n    })\n\n    const store = useStore()\n\n    expect(store.name).toBe('Eduardo')\n    expect(store.$state.name).toBe('Eduardo')\n    expect(pinia.state.value.main).toEqual({\n      name: 'Eduardo',\n      counter: 0,\n      double: 0,\n    })\n\n    name.value = 'Ed'\n    expect(store.name).toBe('Ed')\n    expect(store.$state.name).toBe('Ed')\n    expect(pinia.state.value.main.name).toBe('Ed')\n\n    store.name = 'Edu'\n    expect(store.name).toBe('Edu')\n\n    store.$patch({ counter: 2 })\n    expect(store.counter).toBe(2)\n    expect(counter.value).toBe(2)\n  })\n\n  it('can use app level injections', async () => {\n    const pinia = createPinia()\n    const app = createApp({}).use(pinia)\n    app.provide('hello', 'pinia')\n    const useStore = defineStore('id', () => {\n      const injected = ref(inject('hello', 'nope'))\n\n      return { injected }\n    })\n\n    const store = useStore()\n    expect(store.injected).toBe('pinia')\n  })\n\n  // TODO: until https://github.com/vuejs/test-utils/issues/2059 is fixed\n  it.skip('injects level app injections even within components', async () => {\n    const pinia = createPinia()\n    const useStore = defineStore('id', () => {\n      const injected = ref(inject('hello', 'nope'))\n\n      return { injected }\n    })\n\n    const NestedComponent = {\n      template: '<div>{{ injected }}</div>',\n      setup() {\n        const store = useStore()\n        return { injected: store.injected }\n      },\n    }\n    const Component = {\n      template: '<NestedComponent />',\n      components: { NestedComponent },\n      setup() {\n        // provide('hello', 'component')\n        return {}\n      },\n    }\n    const wrapper = mount(Component, {\n      global: {\n        plugins: [pinia],\n        provide: {\n          hello: 'pinia',\n        },\n      },\n    })\n    expect(wrapper.text()).toBe('pinia')\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/storeToRefs.spec.ts",
    "content": "import { describe, beforeEach, it, expect, vi } from 'vitest'\nimport { computed, reactive, ref, ToRefs } from 'vue'\nimport { createPinia, defineStore, setActivePinia, storeToRefs } from '../src'\n\ndescribe('storeToRefs', () => {\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  function objectOfRefs<O extends Record<any, any>>(o: O): ToRefs<O> {\n    return Object.keys(o).reduce((newO, key) => {\n      // @ts-expect-error: we only need to match\n      newO[key] = expect.objectContaining({ value: o[key] })\n      return newO\n    }, {} as ToRefs<O>)\n  }\n\n  it('empty state', () => {\n    expect(storeToRefs(defineStore('a', {})())).toEqual({})\n    expect(storeToRefs(defineStore('a', () => {})())).toEqual({})\n  })\n\n  it('plain values', () => {\n    const { a, b, c, d } = storeToRefs(\n      defineStore('a', {\n        state: () => ({ a: null as null | undefined, b: false, c: 1, d: 'd' }),\n      })()\n    )\n\n    expect(a.value).toBe(null)\n    expect(b.value).toBe(false)\n    expect(c.value).toBe(1)\n    expect(d.value).toBe('d')\n\n    a.value = undefined\n    expect(a.value).toBe(undefined)\n\n    b.value = true\n    expect(b.value).toBe(true)\n\n    c.value = 2\n    expect(c.value).toBe(2)\n\n    d.value = 'e'\n    expect(d.value).toBe('e')\n  })\n\n  it('setup store', () => {\n    const store = defineStore('a', () => {\n      return {\n        a: ref<null | undefined>(null),\n        b: ref(false),\n        c: ref(1),\n        d: ref('d'),\n        r: reactive({ n: 1 }),\n      }\n    })()\n\n    const { a, b, c, d, r } = storeToRefs(store)\n\n    expect(a.value).toBe(null)\n    expect(b.value).toBe(false)\n    expect(c.value).toBe(1)\n    expect(d.value).toBe('d')\n    expect(r.value).toEqual({ n: 1 })\n\n    a.value = undefined\n    expect(a.value).toBe(undefined)\n\n    b.value = true\n    expect(b.value).toBe(true)\n\n    c.value = 2\n    expect(c.value).toBe(2)\n\n    d.value = 'e'\n    expect(d.value).toBe('e')\n\n    r.value.n++\n    expect(r.value).toEqual({ n: 2 })\n    expect(store.r).toEqual({ n: 2 })\n    store.r.n++\n    expect(r.value).toEqual({ n: 3 })\n    expect(store.r).toEqual({ n: 3 })\n  })\n\n  it('empty getters', () => {\n    expect(\n      storeToRefs(\n        defineStore('a', {\n          state: () => ({ n: 0 }),\n        })()\n      )\n    ).toEqual(objectOfRefs({ n: 0 }))\n    expect(\n      storeToRefs(\n        defineStore('a', () => {\n          return { n: ref(0) }\n        })()\n      )\n    ).toEqual(objectOfRefs({ n: 0 }))\n  })\n\n  it('contains getters', () => {\n    expect(\n      storeToRefs(\n        defineStore('a', {\n          state: () => ({ n: 1 }),\n          getters: {\n            double: (state) => state.n * 2,\n          },\n        })()\n      )\n    ).toEqual(objectOfRefs({ n: 1, double: 2 }))\n    expect(\n      storeToRefs(\n        defineStore('a', () => {\n          const n = ref(0)\n          const double = computed(() => n.value * 2)\n          return { n, double }\n        })()\n      )\n    ).toEqual(objectOfRefs({ n: 1, double: 2 }))\n  })\n\n  it('contain plugin states', () => {\n    const pinia = createPinia()\n    // directly push because no app\n    pinia._p.push(() => ({\n      // @ts-expect-error: cannot set a ref yet\n      pluginN: ref(20),\n      // should not appear in refs\n      shared: 10,\n    }))\n    setActivePinia(pinia)\n\n    expect(\n      storeToRefs(\n        defineStore('a', {\n          state: () => ({ n: 0 }),\n        })()\n      )\n    ).toEqual(objectOfRefs({ n: 0, pluginN: 20 }))\n    expect(\n      storeToRefs(\n        defineStore('a', () => {\n          return { n: ref(0) }\n        })()\n      )\n    ).toEqual(objectOfRefs({ n: 0, pluginN: 20 }))\n  })\n\n  it('preserve setters in getters', () => {\n    const useStore = defineStore('main', () => {\n      const n = ref(0)\n      const double = computed({\n        get() {\n          return n.value * 2\n        },\n        set(value: string | number) {\n          n.value =\n            (typeof value === 'string' ? parseInt(value) || 0 : value) / 2\n        },\n      })\n      return { n, double }\n    })\n    const refs = storeToRefs(useStore())\n    refs.double.value = 4\n    expect(refs.n.value).toBe(2)\n  })\n\n  it('keep reactivity', () => {\n    const store = defineStore('a', () => {\n      const n = ref(0)\n      const double = computed(() => n.value * 2)\n      return { n, double }\n    })()\n\n    const { double } = storeToRefs(store)\n\n    // Assuming HMR operation\n    // @ts-expect-error: hmr\n    store.double =\n      //\n      computed(() => 1)\n\n    expect(double.value).toEqual(1)\n  })\n\n  it('does not trigger getters', () => {\n    const n = ref(0)\n    const spy = vi.fn(() => n.value * 2)\n    const store = defineStore('a', () => {\n      const double = computed(spy)\n      return { n, double }\n    })()\n\n    expect(spy).toHaveBeenCalledTimes(0)\n    storeToRefs(store)\n    expect(spy).toHaveBeenCalledTimes(0)\n  })\n\n  tds(() => {\n    const store1 = defineStore('a', () => {\n      const n = ref(0)\n      const double = computed(() => n.value * 2)\n      return { n, double }\n    })()\n\n    storeToRefs(store1).double\n  })\n\n  function tds(_fn: Function) {}\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/subscriptions.spec.ts",
    "content": "import { beforeEach, describe, it, expect, vi } from 'vitest'\nimport { createPinia, defineStore, MutationType, setActivePinia } from '../src'\nimport { mount } from '@vue/test-utils'\nimport { nextTick, ref } from 'vue'\n\ndescribe('Subscriptions', () => {\n  const useOptionsStore = defineStore('main', {\n    state: () => ({\n      user: 'Eduardo',\n    }),\n  })\n\n  const useSetupStore = defineStore('main', () => {\n    return {\n      user: ref('Eduardo'),\n    }\n  })\n\n  beforeEach(() => {\n    setActivePinia(createPinia())\n  })\n\n  describe.each([\n    { useStore: useOptionsStore, name: 'Options Stores' },\n    { useStore: useSetupStore, name: 'Setup Stores' },\n  ])('with: $name', ({ useStore }) => {\n    it('fires callback changed through $state', () => {\n      const store = useStore()\n      const spy = vi.fn()\n      store.$subscribe(spy, { flush: 'sync' })\n      store.$state.user = 'Cleiton'\n      expect(spy).toHaveBeenCalledTimes(1)\n      expect(spy).toHaveBeenCalledWith(\n        expect.objectContaining({\n          storeId: 'main',\n          type: MutationType.direct,\n        }),\n        store.$state\n      )\n    })\n\n    it('fires callback when changed througg store', async () => {\n      const store = useStore()\n      const spy = vi.fn()\n      store.$subscribe(spy)\n      expect(spy).toHaveBeenCalledTimes(0)\n      store.user = 'Cleiton'\n      await nextTick()\n      expect(spy).toHaveBeenCalledTimes(1)\n    })\n\n    it('subscribe to changes done via patch', () => {\n      const store = useStore()\n      const spy = vi.fn()\n      store.$subscribe(spy, { flush: 'sync' })\n\n      const patch = { user: 'Cleiton' }\n      store.$patch(patch)\n\n      expect(spy).toHaveBeenCalledTimes(1)\n      expect(spy).toHaveBeenCalledWith(\n        expect.objectContaining({\n          payload: patch,\n          storeId: 'main',\n          type: MutationType.patchObject,\n        }),\n        store.$state\n      )\n    })\n    const flushOptions = ['post', 'pre', 'sync'] as const\n\n    flushOptions.forEach((flush) => {\n      it('calls once inside components with flush ' + flush, async () => {\n        const pinia = createPinia()\n        setActivePinia(pinia)\n        const spy1 = vi.fn()\n\n        mount(\n          {\n            setup() {\n              const s1 = useStore()\n              s1.$subscribe(spy1, { flush })\n            },\n            template: `<p/>`,\n          },\n          { global: { plugins: [pinia] } }\n        )\n\n        const s1 = useStore()\n\n        expect(spy1).toHaveBeenCalledTimes(0)\n\n        s1.user = 'Edu'\n        await nextTick()\n        await nextTick()\n        expect(spy1).toHaveBeenCalledTimes(1)\n\n        s1.$patch({ user: 'a' })\n        await nextTick()\n        await nextTick()\n        expect(spy1).toHaveBeenCalledTimes(2)\n\n        s1.$patch((state) => {\n          state.user = 'other'\n        })\n        await nextTick()\n        await nextTick()\n        expect(spy1).toHaveBeenCalledTimes(3)\n      })\n    })\n\n    it('works with multiple different flush', async () => {\n      const spyPre = vi.fn()\n      const spyPost = vi.fn()\n      const spySync = vi.fn()\n\n      const s1 = useStore()\n      s1.$subscribe(spyPre, { flush: 'pre' })\n      s1.$subscribe(spyPost, { flush: 'post' })\n      s1.$subscribe(spySync, { flush: 'sync' })\n\n      expect(spyPre).toHaveBeenCalledTimes(0)\n      expect(spyPost).toHaveBeenCalledTimes(0)\n      expect(spySync).toHaveBeenCalledTimes(0)\n\n      s1.user = 'Edu'\n      expect(spyPre).toHaveBeenCalledTimes(0)\n      expect(spyPost).toHaveBeenCalledTimes(0)\n      expect(spySync).toHaveBeenCalledTimes(1)\n      await nextTick()\n      expect(spyPre).toHaveBeenCalledTimes(1)\n      expect(spyPost).toHaveBeenCalledTimes(1)\n      expect(spySync).toHaveBeenCalledTimes(1)\n\n      s1.$patch({ user: 'a' })\n      // patch still triggers all subscriptions immediately\n      expect(spyPre).toHaveBeenCalledTimes(2)\n      expect(spyPost).toHaveBeenCalledTimes(2)\n      expect(spySync).toHaveBeenCalledTimes(2)\n      await nextTick()\n      expect(spyPre).toHaveBeenCalledTimes(2)\n      expect(spyPost).toHaveBeenCalledTimes(2)\n      expect(spySync).toHaveBeenCalledTimes(2)\n\n      s1.$patch((state) => {\n        state.user = 'other'\n      })\n      expect(spyPre).toHaveBeenCalledTimes(3)\n      expect(spyPost).toHaveBeenCalledTimes(3)\n      expect(spySync).toHaveBeenCalledTimes(3)\n      await nextTick()\n      expect(spyPre).toHaveBeenCalledTimes(3)\n      expect(spyPost).toHaveBeenCalledTimes(3)\n      expect(spySync).toHaveBeenCalledTimes(3)\n    })\n\n    it('works with multiple different flush and multiple state changes', async () => {\n      const spyPre = vi.fn()\n      const spyPost = vi.fn()\n      const spySync = vi.fn()\n\n      const s1 = useStore()\n      s1.$subscribe(spyPre, { flush: 'pre' })\n      s1.$subscribe(spyPost, { flush: 'post' })\n      s1.$subscribe(spySync, { flush: 'sync' })\n\n      s1.user = 'Edu'\n      expect(spyPre).toHaveBeenCalledTimes(0)\n      expect(spyPost).toHaveBeenCalledTimes(0)\n      expect(spySync).toHaveBeenCalledTimes(1)\n      s1.$patch({ user: 'a' })\n      expect(spyPre).toHaveBeenCalledTimes(1)\n      expect(spyPost).toHaveBeenCalledTimes(1)\n      expect(spySync).toHaveBeenCalledTimes(2)\n      await nextTick()\n      expect(spyPre).toHaveBeenCalledTimes(1)\n      expect(spyPost).toHaveBeenCalledTimes(1)\n      expect(spySync).toHaveBeenCalledTimes(2)\n    })\n\n    it('unsubscribes callback when unsubscribe is called', () => {\n      const spy = vi.fn()\n      const store = useStore()\n      const unsubscribe = store.$subscribe(spy, { flush: 'sync' })\n      unsubscribe()\n      store.$state.user = 'Cleiton'\n      expect(spy).not.toHaveBeenCalled()\n    })\n\n    it('listeners are not affected when unsubscribe is called multiple times', () => {\n      const func1 = vi.fn()\n      const func2 = vi.fn()\n      const store = useStore()\n      const unsubscribe1 = store.$subscribe(func1, { flush: 'sync' })\n      store.$subscribe(func2, { flush: 'sync' })\n      unsubscribe1()\n      unsubscribe1()\n      store.$state.user = 'Cleiton'\n      expect(func1).not.toHaveBeenCalled()\n      expect(func2).toHaveBeenCalledTimes(1)\n    })\n\n    describe('multiple', () => {\n      it('triggers subscribe only once', async () => {\n        const s1 = useStore()\n        const s2 = useStore()\n\n        const spy1 = vi.fn()\n        const spy2 = vi.fn()\n\n        s1.$subscribe(spy1, { flush: 'sync' })\n        s2.$subscribe(spy2, { flush: 'sync' })\n\n        expect(spy1).toHaveBeenCalledTimes(0)\n        expect(spy2).toHaveBeenCalledTimes(0)\n\n        s1.user = 'Edu'\n\n        expect(spy1).toHaveBeenCalledTimes(1)\n        expect(spy2).toHaveBeenCalledTimes(1)\n      })\n\n      it('triggers pre subscriptions only once on $patch', async () => {\n        const s1 = useStore()\n        const spy1 = vi.fn()\n\n        s1.$subscribe(spy1, { flush: 'pre' })\n\n        // First mutation: works as expected\n        s1.$patch({ user: 'Edu' })\n        // anything else than awaiting a non promise or Promise.resolve() works\n        await false\n        // await Promise.resolve(false)\n        // adding an extra await works\n        // await false\n        // adding any other delay also works\n        // await delay(20)\n        // await nextTick()\n        expect(spy1).toHaveBeenCalledTimes(1)\n        expect(spy1).not.toHaveBeenCalledWith(\n          expect.objectContaining({ type: MutationType.direct }),\n          s1.$state\n        )\n\n        s1.$patch({ user: 'Myk' })\n        await nextTick()\n\n        expect(spy1).toHaveBeenCalledTimes(2)\n        expect(spy1).not.toHaveBeenCalledWith(\n          expect.objectContaining({ type: MutationType.direct }),\n          s1.$state\n        )\n      })\n\n      it('removes on unmount', async () => {\n        const pinia = createPinia()\n        setActivePinia(pinia)\n        const spy1 = vi.fn()\n        const spy2 = vi.fn()\n\n        const wrapper = mount(\n          {\n            setup() {\n              const s1 = useStore()\n              s1.$subscribe(spy1, { flush: 'sync' })\n            },\n            template: `<p/>`,\n          },\n          { global: { plugins: [pinia] } }\n        )\n\n        const s1 = useStore()\n        const s2 = useStore()\n\n        s2.$subscribe(spy2, { flush: 'sync' })\n\n        expect(spy1).toHaveBeenCalledTimes(0)\n        expect(spy2).toHaveBeenCalledTimes(0)\n\n        s1.user = 'Edu'\n        expect(spy1).toHaveBeenCalledTimes(1)\n        expect(spy2).toHaveBeenCalledTimes(1)\n\n        s1.$patch({ user: 'a' })\n        expect(spy1).toHaveBeenCalledTimes(2)\n        expect(spy2).toHaveBeenCalledTimes(2)\n\n        s1.$patch((state) => {\n          state.user = 'other'\n        })\n        expect(spy1).toHaveBeenCalledTimes(3)\n        expect(spy2).toHaveBeenCalledTimes(3)\n\n        wrapper.unmount()\n        await nextTick()\n\n        s1.$patch({ user: 'b' })\n        expect(spy1).toHaveBeenCalledTimes(3)\n        expect(spy2).toHaveBeenCalledTimes(4)\n        s1.$patch((state) => {\n          state.user = 'c'\n        })\n        expect(spy1).toHaveBeenCalledTimes(3)\n        expect(spy2).toHaveBeenCalledTimes(5)\n        s1.user = 'd'\n        expect(spy1).toHaveBeenCalledTimes(3)\n        expect(spy2).toHaveBeenCalledTimes(6)\n      })\n    })\n\n    it('subscribe is post by default', async () => {\n      const spy = vi.fn()\n      const store = useStore()\n      store.$subscribe(spy)\n      store.$state.user = 'Cleiton'\n      expect(spy).toHaveBeenCalledTimes(0)\n      await nextTick()\n      expect(spy).toHaveBeenCalledTimes(1)\n      expect(spy).toHaveBeenCalledWith(\n        expect.objectContaining({\n          storeId: 'main',\n          type: MutationType.direct,\n        }),\n        store.$state\n      )\n    })\n\n    it('subscribe once with patch', () => {\n      const spy1 = vi.fn()\n      const spy2 = vi.fn()\n      const store = useStore()\n      function once() {\n        const unsubscribe = store.$subscribe(\n          () => {\n            spy1()\n            unsubscribe()\n          },\n          { flush: 'sync' }\n        )\n      }\n      once()\n      store.$subscribe(spy2, { flush: 'sync' })\n      expect(spy1).toHaveBeenCalledTimes(0)\n      expect(spy2).toHaveBeenCalledTimes(0)\n      store.$patch((state) => {\n        state.user = 'a'\n      })\n      expect(spy1).toHaveBeenCalledTimes(1)\n      expect(spy2).toHaveBeenCalledTimes(1)\n      store.$patch((state) => {\n        state.user = 'b'\n      })\n      expect(spy1).toHaveBeenCalledTimes(1)\n      expect(spy2).toHaveBeenCalledTimes(2)\n    })\n\n    // https://github.com/vuejs/pinia/issues/992\n    it('triggers sync subscription when state is synchronously mutated after patch', async () => {\n      const store = useStore()\n      const syncSpy = vi.fn()\n      const preSpy = vi.fn()\n      const postSpy = vi.fn()\n      store.$subscribe(syncSpy, { flush: 'sync' })\n      store.$subscribe(preSpy, { flush: 'pre' })\n      store.$subscribe(postSpy, { flush: 'post' })\n\n      store.$patch({ user: 'Edu' })\n      store.user = 'a'\n      expect(syncSpy).toHaveBeenCalledTimes(2)\n\n      // FIXME: ideally, these should be 2 but we cannot use\n      // a sync flush within the store's $subscribe method\n      // https://github.com/vuejs/pinia/issues/610\n      await nextTick()\n      expect(preSpy).toHaveBeenCalledTimes(1)\n      expect(postSpy).toHaveBeenCalledTimes(1)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/__tests__/vitest-mock-warn.ts",
    "content": "// https://github.com/posva/jest-mock-warn/blob/master/src/index.js\nimport type { MockInstance } from 'vitest'\nimport { afterEach, beforeEach, expect, vi } from 'vitest'\n\ninterface CustomMatchers<R = unknown> {\n  toHaveBeenWarned: () => R\n  toHaveBeenWarnedLast: () => R\n  toHaveBeenWarnedTimes: (n: number) => R\n  toHaveBeenErrored: () => R\n  toHaveBeenErroredLast: () => R\n  toHaveBeenErroredTimes: (n: number) => R\n}\n\ndeclare module 'vitest' {\n  interface Assertion<T = any> extends CustomMatchers<T> {}\n  interface AsymmetricMatchersContaining extends CustomMatchers {}\n}\n\nfunction createMockConsoleMethod(method: 'warn' | 'error') {\n  let mockInstance: MockInstance<(typeof console)[typeof method]>\n  const asserted = new Map<string, string | RegExp>()\n\n  expect.extend({\n    [`toHaveBeen${method.charAt(0).toUpperCase() + method.slice(1)}ed`](\n      received: string | RegExp\n    ) {\n      asserted.set(received.toString(), received)\n      const passed = mockInstance.mock.calls.some((args) =>\n        typeof received === 'string'\n          ? String(args[0]).includes(received)\n          : received.test(String(args[0]))\n      )\n\n      return passed\n        ? {\n            pass: true,\n            message: () =>\n              `expected \"${received}\" not to have been ${method}ed.`,\n          }\n        : {\n            pass: false,\n            message: () => `expected \"${received}\" to have been ${method}ed.`,\n          }\n    },\n\n    [`toHaveBeen${method.charAt(0).toUpperCase() + method.slice(1)}edLast`](\n      received: string | RegExp\n    ) {\n      asserted.set(received.toString(), received)\n      const lastCall = String(mockInstance.mock.calls.at(-1)?.[0])\n      const passed =\n        typeof received === 'string'\n          ? lastCall?.includes(received)\n          : received.test(lastCall)\n\n      return passed\n        ? {\n            pass: true,\n            message: () =>\n              `expected \"${received}\" not to have been ${method}ed last.`,\n          }\n        : {\n            pass: false,\n            message: () =>\n              `expected \"${received}\" to have been ${method}ed last.`,\n          }\n    },\n\n    [`toHaveBeen${method.charAt(0).toUpperCase() + method.slice(1)}edTimes`](\n      received: string | RegExp,\n      n: number\n    ) {\n      asserted.set(received.toString(), received)\n      const count = mockInstance.mock.calls.filter((args) =>\n        typeof received === 'string'\n          ? String(args[0]).includes(received)\n          : received.test(String(args[0]))\n      ).length\n\n      return count === n\n        ? {\n            pass: true,\n            message: () =>\n              `expected \"${received}\" to have been ${method}ed ${n} times.`,\n          }\n        : {\n            pass: false,\n            message: () =>\n              `expected \"${received}\" to have been ${method}ed ${n} times but got ${count}.`,\n          }\n    },\n  })\n\n  beforeEach(() => {\n    asserted.clear()\n    mockInstance = vi.spyOn(console, method).mockImplementation(() => {})\n  })\n\n  afterEach(() => {\n    const assertedArray = Array.from(asserted)\n    const unassertedLogs = mockInstance.mock.calls\n      .map((args) => String(args[0]))\n      .filter(\n        (msg) =>\n          !assertedArray.some(([_key, assertedMsg]) =>\n            typeof assertedMsg === 'string'\n              ? msg.includes(assertedMsg)\n              : assertedMsg.test(msg)\n          )\n      )\n\n    mockInstance.mockRestore()\n\n    if (unassertedLogs.length) {\n      unassertedLogs.forEach((msg) => console[method](msg))\n      throw new Error(`Test case threw unexpected ${method}s.`, {\n        cause: unassertedLogs,\n      })\n    }\n  })\n}\n\nexport function mockWarn() {\n  createMockConsoleMethod('warn')\n}\n\nexport function mockConsoleError() {\n  createMockConsoleMethod('error')\n}\n"
  },
  {
    "path": "packages/pinia/__tests__/vitest-setup.ts",
    "content": "import { beforeEach } from 'vitest'\nimport { setActivePinia } from '../src'\n\nbeforeEach(() => {\n  setActivePinia(undefined)\n})\n"
  },
  {
    "path": "packages/pinia/package.json",
    "content": "{\n  \"name\": \"pinia\",\n  \"version\": \"3.0.4\",\n  \"description\": \"Intuitive, type safe and flexible Store for Vue\",\n  \"keywords\": [\n    \"api\",\n    \"composition\",\n    \"pigna\",\n    \"pinia\",\n    \"piña\",\n    \"safe\",\n    \"setup\",\n    \"store\",\n    \"ts\",\n    \"type\",\n    \"typed\",\n    \"typescript\",\n    \"vue\",\n    \"vuex\"\n  ],\n  \"homepage\": \"https://pinia.vuejs.org\",\n  \"bugs\": {\n    \"url\": \"https://github.com/vuejs/pinia/issues\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Eduardo San Martin Morote\",\n    \"email\": \"posva13@gmail.com\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/vuejs/pinia.git\"\n  },\n  \"funding\": \"https://github.com/sponsors/posva\",\n  \"files\": [\n    \"LICENSE\",\n    \"README.md\",\n    \"dist/*.js\",\n    \"dist/*.mjs\",\n    \"dist/pinia.d.ts\"\n  ],\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"types\": \"dist/pinia.d.ts\",\n  \"unpkg\": \"dist/pinia.iife.js\",\n  \"jsdelivr\": \"dist/pinia.iife.js\",\n  \"exports\": {\n    \".\": \"./dist/pinia.mjs\",\n    \"./package.json\": \"./package.json\"\n  },\n  \"scripts\": {\n    \"build\": \"tsdown\",\n    \"changelog\": \"conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l pinia -r 1\",\n    \"test:dts\": \"tsc -p ./test-dts/tsconfig.json\",\n    \"test\": \"pnpm run build && pnpm test:dts\"\n  },\n  \"dependencies\": {\n    \"@vue/devtools-api\": \"^8.0.5\"\n  },\n  \"devDependencies\": {\n    \"@vue/test-utils\": \"^2.4.6\",\n    \"tsdown\": \"^0.20.1\"\n  },\n  \"peerDependencies\": {\n    \"typescript\": \">=5.6.0\",\n    \"vue\": \"^3.5.11\"\n  },\n  \"peerDependenciesMeta\": {\n    \"typescript\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "packages/pinia/src/createPinia.ts",
    "content": "import { Pinia, PiniaPlugin, setActivePinia, piniaSymbol } from './rootStore'\nimport { ref, App, markRaw, effectScope, Ref } from 'vue'\nimport { registerPiniaDevtools, devtoolsPlugin } from './devtools'\nimport { IS_CLIENT } from './env'\nimport { StateTree, StoreGeneric } from './types'\n\n/**\n * Creates a Pinia instance to be used by the application\n */\nexport function createPinia(): Pinia {\n  const scope = effectScope(true)\n  // NOTE: here we could check the window object for a state and directly set it\n  // if there is anything like it with Vue 3 SSR\n  const state = scope.run<Ref<Record<string, StateTree>>>(() =>\n    ref<Record<string, StateTree>>({})\n  )!\n\n  let _p: Pinia['_p'] = []\n  // plugins added before calling app.use(pinia)\n  let toBeInstalled: PiniaPlugin[] = []\n\n  const pinia: Pinia = markRaw({\n    install(app: App) {\n      // this allows calling useStore() outside of a component setup after\n      // installing pinia's plugin\n      setActivePinia(pinia)\n      pinia._a = app\n      app.provide(piniaSymbol, pinia)\n      app.config.globalProperties.$pinia = pinia\n      /* istanbul ignore else */\n      if (__USE_DEVTOOLS__ && IS_CLIENT) {\n        registerPiniaDevtools(app, pinia)\n      }\n      toBeInstalled.forEach((plugin) => _p.push(plugin))\n      toBeInstalled = []\n    },\n\n    use(plugin) {\n      if (!this._a) {\n        toBeInstalled.push(plugin)\n      } else {\n        _p.push(plugin)\n      }\n      return this\n    },\n\n    _p,\n    // it's actually undefined here\n    // @ts-expect-error\n    _a: null,\n    _e: scope,\n    _s: new Map<string, StoreGeneric>(),\n    state,\n  })\n\n  // pinia devtools rely on dev only features so they cannot be forced unless\n  // the dev build of Vue is used. Avoid old browsers like IE11.\n  if (__USE_DEVTOOLS__ && IS_CLIENT && typeof Proxy !== 'undefined') {\n    pinia.use(devtoolsPlugin)\n  }\n\n  return pinia\n}\n\n/**\n * Dispose a Pinia instance by stopping its effectScope and removing the state, plugins and stores. This is mostly\n * useful in tests, with both a testing pinia or a regular pinia and in applications that use multiple pinia instances.\n * Once disposed, the pinia instance cannot be used anymore.\n *\n * @param pinia - pinia instance\n */\nexport function disposePinia(pinia: Pinia) {\n  pinia._e.stop()\n  pinia._s.clear()\n  pinia._p.splice(0)\n  pinia.state.value = {}\n  // @ts-expect-error: non valid\n  pinia._a = null\n}\n"
  },
  {
    "path": "packages/pinia/src/devtools/actions.ts",
    "content": "import { Pinia } from '../rootStore'\nimport { saveAs } from './file-saver'\nimport { toastMessage } from './utils'\n\n/**\n * This file contain devtools actions, they are not Pinia actions.\n */\n\n// ---\n\nexport function checkClipboardAccess() {\n  if (!('clipboard' in navigator)) {\n    toastMessage(`Your browser doesn't support the Clipboard API`, 'error')\n    return true\n  }\n}\n\nfunction checkNotFocusedError(error: unknown): error is Error {\n  if (\n    error instanceof Error &&\n    error.message.toLowerCase().includes('document is not focused')\n  ) {\n    toastMessage(\n      'You need to activate the \"Emulate a focused page\" setting in the \"Rendering\" panel of devtools.',\n      'warn'\n    )\n    return true\n  }\n  return false\n}\n\nexport async function actionGlobalCopyState(pinia: Pinia) {\n  if (checkClipboardAccess()) return\n  try {\n    await navigator.clipboard.writeText(JSON.stringify(pinia.state.value))\n    toastMessage('Global state copied to clipboard.')\n  } catch (error) {\n    if (checkNotFocusedError(error)) return\n    toastMessage(\n      `Failed to serialize the state. Check the console for more details.`,\n      'error'\n    )\n    console.error(error)\n  }\n}\n\nexport async function actionGlobalPasteState(pinia: Pinia) {\n  if (checkClipboardAccess()) return\n  try {\n    loadStoresState(pinia, JSON.parse(await navigator.clipboard.readText()))\n    toastMessage('Global state pasted from clipboard.')\n  } catch (error) {\n    if (checkNotFocusedError(error)) return\n    toastMessage(\n      `Failed to deserialize the state from clipboard. Check the console for more details.`,\n      'error'\n    )\n    console.error(error)\n  }\n}\n\nexport async function actionGlobalSaveState(pinia: Pinia) {\n  try {\n    saveAs(\n      new Blob([JSON.stringify(pinia.state.value)], {\n        type: 'text/plain;charset=utf-8',\n      }),\n      'pinia-state.json'\n    )\n  } catch (error) {\n    toastMessage(\n      `Failed to export the state as JSON. Check the console for more details.`,\n      'error'\n    )\n    console.error(error)\n  }\n}\n\nlet fileInput: HTMLInputElement | undefined\nfunction getFileOpener() {\n  if (!fileInput) {\n    fileInput = document.createElement('input')\n    fileInput.type = 'file'\n    fileInput.accept = '.json'\n  }\n\n  function openFile(): Promise<null | { text: string; file: File }> {\n    return new Promise((resolve, reject) => {\n      fileInput!.onchange = async () => {\n        const files = fileInput!.files\n        if (!files) return resolve(null)\n        const file = files.item(0)\n        if (!file) return resolve(null)\n        return resolve({ text: await file.text(), file })\n      }\n      // @ts-ignore: TODO: changed from 4.3 to 4.4\n      fileInput!.oncancel = () => resolve(null)\n      fileInput!.onerror = reject\n      fileInput!.click()\n    })\n  }\n  return openFile\n}\n\nexport async function actionGlobalOpenStateFile(pinia: Pinia) {\n  try {\n    const open = getFileOpener()\n    const result = await open()\n    if (!result) return\n    const { text, file } = result\n    loadStoresState(pinia, JSON.parse(text))\n    toastMessage(`Global state imported from \"${file.name}\".`)\n  } catch (error) {\n    toastMessage(\n      `Failed to import the state from JSON. Check the console for more details.`,\n      'error'\n    )\n    console.error(error)\n  }\n}\n\nfunction loadStoresState(pinia: Pinia, state: Record<string, unknown>) {\n  for (const key in state) {\n    const storeState = pinia.state.value[key]\n    // store is already instantiated, patch it\n    if (storeState) {\n      Object.assign(storeState, state[key])\n    } else {\n      // store is not instantiated, set the initial state\n      pinia.state.value[key] = state[key] as any\n    }\n  }\n}\n"
  },
  {
    "path": "packages/pinia/src/devtools/file-saver.ts",
    "content": "/*\n * FileSaver.js A saveAs() FileSaver implementation.\n *\n * Originally by Eli Grey, adapted as an ESM module by Eduardo San Martin\n * Morote.\n *\n * License : MIT\n */\n\nimport { IS_CLIENT } from '../env'\n\n// The one and only way of getting global scope in all environments\n// https://stackoverflow.com/q/3277182/1008999\nconst _global = /*#__PURE__*/ (() =>\n  typeof window === 'object' && window.window === window\n    ? window\n    : typeof self === 'object' && self.self === self\n      ? self\n      : typeof global === 'object' && global.global === global\n        ? global\n        : typeof globalThis === 'object'\n          ? globalThis\n          : { HTMLElement: null })()\n\nexport interface Options {\n  autoBom?: boolean\n}\n\nfunction bom(blob: Blob, { autoBom = false }: Options = {}) {\n  // prepend BOM for UTF-8 XML and text/* types (including HTML)\n  // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF\n  if (\n    autoBom &&\n    /^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(\n      blob.type\n    )\n  ) {\n    return new Blob([String.fromCharCode(0xfeff), blob], { type: blob.type })\n  }\n  return blob\n}\n\nfunction download(url: string, name: string, opts?: Options) {\n  const xhr = new XMLHttpRequest()\n  xhr.open('GET', url)\n  xhr.responseType = 'blob'\n  xhr.onload = function () {\n    saveAs(xhr.response, name, opts)\n  }\n  xhr.onerror = function () {\n    console.error('could not download file')\n  }\n  xhr.send()\n}\n\nfunction corsEnabled(url: string) {\n  const xhr = new XMLHttpRequest()\n  // use sync to avoid popup blocker\n  xhr.open('HEAD', url, false)\n  try {\n    xhr.send()\n  } catch (e) {}\n  return xhr.status >= 200 && xhr.status <= 299\n}\n\n// `a.click()` doesn't work for all browsers (#465)\nfunction click(node: Element) {\n  try {\n    node.dispatchEvent(new MouseEvent('click'))\n  } catch (e) {\n    const evt = new MouseEvent('click', {\n      bubbles: true,\n      cancelable: true,\n      view: window,\n      detail: 0,\n      screenX: 80,\n      screenY: 20,\n      clientX: 80,\n      clientY: 20,\n      ctrlKey: false,\n      altKey: false,\n      shiftKey: false,\n      metaKey: false,\n      button: 0,\n      relatedTarget: null,\n    })\n    node.dispatchEvent(evt)\n  }\n}\n\nconst _navigator = typeof navigator === 'object' ? navigator : { userAgent: '' }\n\n// Detect WebView inside a native macOS app by ruling out all browsers\n// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too\n// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos\nconst isMacOSWebView = /*#__PURE__*/ (() =>\n  /Macintosh/.test(_navigator.userAgent) &&\n  /AppleWebKit/.test(_navigator.userAgent) &&\n  !/Safari/.test(_navigator.userAgent))()\n\nexport type SaveAs =\n  | ((blob: Blob, name?: string, opts?: Options) => void)\n  | ((\n      blob: Blob,\n      name: string,\n      opts?: Options | undefined,\n      popup?: Window | null | undefined\n    ) => void)\n\nexport const saveAs: SaveAs = !IS_CLIENT\n  ? () => {} // noop\n  : // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView or mini program\n    typeof HTMLAnchorElement !== 'undefined' &&\n      'download' in HTMLAnchorElement.prototype &&\n      !isMacOSWebView\n    ? downloadSaveAs\n    : // Use msSaveOrOpenBlob as a second approach\n      'msSaveOrOpenBlob' in _navigator\n      ? msSaveAs\n      : // Fallback to using FileReader and a popup\n        fileSaverSaveAs\n\nfunction downloadSaveAs(blob: Blob, name: string = 'download', opts?: Options) {\n  const a = document.createElement('a')\n\n  a.download = name\n  a.rel = 'noopener' // tabnabbing\n\n  // TODO: detect chrome extensions & packaged apps\n  // a.target = '_blank'\n\n  if (typeof blob === 'string') {\n    // Support regular links\n    a.href = blob\n    if (a.origin !== location.origin) {\n      if (corsEnabled(a.href)) {\n        download(blob, name, opts)\n      } else {\n        a.target = '_blank'\n        click(a)\n      }\n    } else {\n      click(a)\n    }\n  } else {\n    // Support blobs\n    a.href = URL.createObjectURL(blob)\n    setTimeout(function () {\n      URL.revokeObjectURL(a.href)\n    }, 4e4) // 40s\n    setTimeout(function () {\n      click(a)\n    }, 0)\n  }\n}\n\nfunction msSaveAs(blob: Blob, name: string = 'download', opts?: Options) {\n  if (typeof blob === 'string') {\n    if (corsEnabled(blob)) {\n      download(blob, name, opts)\n    } else {\n      const a = document.createElement('a')\n      a.href = blob\n      a.target = '_blank'\n      setTimeout(function () {\n        click(a)\n      })\n    }\n  } else {\n    // @ts-ignore: works on windows\n    navigator.msSaveOrOpenBlob(bom(blob, opts), name)\n  }\n}\n\nfunction fileSaverSaveAs(\n  blob: Blob,\n  name: string,\n  opts?: Options,\n  popup?: Window | null\n) {\n  // Open a popup immediately do go around popup blocker\n  // Mostly only available on user interaction and the fileReader is async so...\n  popup = popup || open('', '_blank')\n  if (popup) {\n    popup.document.title = popup.document.body.innerText = 'downloading...'\n  }\n\n  if (typeof blob === 'string') return download(blob, name, opts)\n\n  const force = blob.type === 'application/octet-stream'\n  const isSafari =\n    /constructor/i.test(String(_global.HTMLElement)) || 'safari' in _global\n  const isChromeIOS = /CriOS\\/[\\d]+/.test(navigator.userAgent)\n\n  if (\n    (isChromeIOS || (force && isSafari) || isMacOSWebView) &&\n    typeof FileReader !== 'undefined'\n  ) {\n    // Safari doesn't allow downloading of blob URLs\n    const reader = new FileReader()\n    reader.onloadend = function () {\n      let url = reader.result\n      if (typeof url !== 'string') {\n        popup = null\n        throw new Error('Wrong reader.result type')\n      }\n      url = isChromeIOS\n        ? url\n        : url.replace(/^data:[^;]*;/, 'data:attachment/file;')\n      if (popup) {\n        popup.location.href = url\n      } else {\n        location.assign(url)\n      }\n      popup = null // reverse-tabnabbing #460\n    }\n    reader.readAsDataURL(blob)\n  } else {\n    const url = URL.createObjectURL(blob)\n    if (popup) popup.location.assign(url)\n    else location.href = url\n    popup = null // reverse-tabnabbing #460\n    setTimeout(function () {\n      URL.revokeObjectURL(url)\n    }, 4e4) // 40s\n  }\n}\n"
  },
  {
    "path": "packages/pinia/src/devtools/formatting.ts",
    "content": "import { MutationType, StoreGeneric } from '../types'\nimport { DebuggerEvent } from 'vue'\nimport { Pinia } from '../rootStore'\nimport { isPinia } from './utils'\n\n// types from devtools-api\ninterface StateBase {\n  key: string\n  value: any\n  editable?: boolean\n  objectType?: 'ref' | 'reactive' | 'computed' | 'other'\n  raw?: string\n}\ninterface ComponentStateBase extends StateBase {\n  type: string\n}\ntype ComponentBuiltinCustomStateTypes =\n  | 'function'\n  | 'map'\n  | 'set'\n  | 'reference'\n  | 'component'\n  | 'component-definition'\n  | 'router'\n  | 'store'\ninterface CustomState {\n  _custom: {\n    type: ComponentBuiltinCustomStateTypes | string\n    objectType?: string\n    display?: string\n    tooltip?: string\n    value?: any\n    abstract?: boolean\n    file?: string\n    uid?: number\n    readOnly?: boolean\n    /** Configure immediate child fields */\n    fields?: {\n      abstract?: boolean\n    }\n    id?: any\n    actions?: {\n      icon: string\n      tooltip?: string\n      action: () => void | Promise<void>\n    }[]\n    /** internal */\n    _reviveId?: number\n  }\n}\ninterface ComponentPropState extends ComponentStateBase {\n  meta?: {\n    type: string\n    required: boolean\n    /** Vue 1 only */\n    mode?: 'default' | 'sync' | 'once'\n  }\n}\ntype ComponentState =\n  | ComponentStateBase\n  | ComponentPropState\n  | ComponentCustomState\ninterface ComponentCustomState extends ComponentStateBase {\n  value: CustomState\n}\n\ninterface InspectorNodeTag {\n  label: string\n  textColor: number\n  backgroundColor: number\n  tooltip?: string\n}\ninterface CustomInspectorNode {\n  id: string\n  label: string\n  children?: CustomInspectorNode[]\n  tags?: InspectorNodeTag[]\n  name?: string\n  file?: string\n}\n\ninterface CustomInspectorState {\n  [key: string]: (StateBase | Omit<ComponentState, 'type'>)[]\n}\n\nexport function formatDisplay(display: string) {\n  return {\n    _custom: {\n      display,\n    },\n  }\n}\n\nexport const PINIA_ROOT_LABEL = '🍍 Pinia (root)'\nexport const PINIA_ROOT_ID = '_root'\n\nexport function formatStoreForInspectorTree(\n  store: StoreGeneric | Pinia\n): CustomInspectorNode {\n  return isPinia(store)\n    ? {\n        id: PINIA_ROOT_ID,\n        label: PINIA_ROOT_LABEL,\n      }\n    : {\n        id: store.$id,\n        label: store.$id,\n      }\n}\n\nexport function formatStoreForInspectorState(\n  store: StoreGeneric | Pinia\n): CustomInspectorState {\n  if (isPinia(store)) {\n    const storeNames = Array.from(store._s.keys())\n    const storeMap = store._s\n    const state: CustomInspectorState = {\n      state: storeNames.map((storeId) => ({\n        editable: true,\n        key: storeId,\n        value: store.state.value[storeId],\n      })),\n      getters: storeNames\n        .filter((id) => storeMap.get(id)!._getters)\n        .map((id) => {\n          const store = storeMap.get(id)!\n\n          return {\n            editable: false,\n            key: id,\n            value: store._getters!.reduce(\n              (getters, key) => {\n                getters[key] = store[key]\n                return getters\n              },\n              {} as Record<string, any>\n            ),\n          }\n        }),\n    }\n\n    return state\n  }\n\n  const state: CustomInspectorState | ComponentCustomState = {\n    state: Object.keys(store.$state).map((key) => ({\n      editable: true,\n      key,\n      value: store.$state[key],\n    })),\n  }\n\n  // avoid adding empty getters\n  if (store._getters && store._getters.length) {\n    state.getters = store._getters.map((getterName) => ({\n      editable: false,\n      key: getterName,\n      value: store[getterName],\n    }))\n  }\n\n  if (store._customProperties.size) {\n    state.customProperties = Array.from(store._customProperties).map((key) => ({\n      editable: true,\n      key,\n      value: store[key],\n    }))\n  }\n\n  return state\n}\n\nexport function formatEventData(\n  events: DebuggerEvent[] | DebuggerEvent | undefined\n) {\n  if (!events) return {}\n  if (Array.isArray(events)) {\n    // TODO: handle add and delete for arrays and objects\n    return events.reduce(\n      (data, event) => {\n        data.keys.push(event.key)\n        data.operations.push(event.type)\n        data.oldValue[event.key] = event.oldValue\n        data.newValue[event.key] = event.newValue\n        return data\n      },\n      {\n        oldValue: {} as Record<string, any>,\n        keys: [] as string[],\n        operations: [] as string[],\n        newValue: {} as Record<string, any>,\n      }\n    )\n  } else {\n    return {\n      operation: formatDisplay(events.type),\n      key: formatDisplay(events.key),\n      oldValue: events.oldValue,\n      newValue: events.newValue,\n    }\n  }\n}\n\nexport function formatMutationType(type: MutationType): string {\n  switch (type) {\n    case MutationType.direct:\n      return 'mutation'\n    case MutationType.patchFunction:\n      return '$patch'\n    case MutationType.patchObject:\n      return '$patch'\n    default:\n      return 'unknown'\n  }\n}\n"
  },
  {
    "path": "packages/pinia/src/devtools/index.ts",
    "content": "export { devtoolsPlugin, registerPiniaDevtools } from './plugin'\n"
  },
  {
    "path": "packages/pinia/src/devtools/plugin.ts",
    "content": "import { setupDevtoolsPlugin } from '@vue/devtools-api'\nimport { App, ComponentPublicInstance, markRaw, toRaw, unref, watch } from 'vue'\nimport { Pinia, PiniaPluginContext } from '../rootStore'\nimport {\n  _GettersTree,\n  MutationType,\n  StateTree,\n  _ActionsTree,\n  StoreGeneric,\n} from '../types'\nimport {\n  actionGlobalCopyState,\n  actionGlobalPasteState,\n  actionGlobalSaveState,\n  actionGlobalOpenStateFile,\n} from './actions'\nimport {\n  formatDisplay,\n  formatEventData,\n  formatMutationType,\n  formatStoreForInspectorState,\n  formatStoreForInspectorTree,\n  PINIA_ROOT_ID,\n  PINIA_ROOT_LABEL,\n} from './formatting'\nimport { isPinia, toastMessage } from './utils'\n\n// timeline can be paused when directly changing the state\nlet isTimelineActive = true\nconst componentStateTypes: string[] = []\n\nconst MUTATIONS_LAYER_ID = 'pinia:mutations'\nconst INSPECTOR_ID = 'pinia'\nconst { assign } = Object\n\n// copied from devtools\ninterface TimelineEvent<TData = any, TMeta = any> {\n  time: number\n  data: TData\n  logType?: 'default' | 'warning' | 'error'\n  meta?: TMeta\n  groupId?: number | string\n  title?: string\n  subtitle?: string\n}\n\n/**\n * Gets the displayed name of a store in devtools\n *\n * @param id - id of the store\n * @returns a formatted string\n */\nconst getStoreType = (id: string) => '🍍 ' + id\n\n/**\n * Add the pinia plugin without any store. Allows displaying a Pinia plugin tab\n * as soon as it is added to the application.\n *\n * @param app - Vue application\n * @param pinia - pinia instance\n */\nexport function registerPiniaDevtools(app: App, pinia: Pinia) {\n  setupDevtoolsPlugin(\n    {\n      id: 'dev.esm.pinia',\n      label: 'Pinia 🍍',\n      logo: 'https://pinia.vuejs.org/logo.svg',\n      packageName: 'pinia',\n      homepage: 'https://pinia.vuejs.org',\n      componentStateTypes,\n      app,\n    },\n    (api) => {\n      if (typeof api.now !== 'function') {\n        toastMessage(\n          'You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'\n        )\n      }\n\n      api.addTimelineLayer({\n        id: MUTATIONS_LAYER_ID,\n        label: `Pinia 🍍`,\n        color: 0xe5df88,\n      })\n\n      api.addInspector({\n        id: INSPECTOR_ID,\n        label: 'Pinia 🍍',\n        icon: 'storage',\n        treeFilterPlaceholder: 'Search stores',\n        actions: [\n          {\n            icon: 'content_copy',\n            action: () => {\n              actionGlobalCopyState(pinia)\n            },\n            tooltip: 'Serialize and copy the state',\n          },\n          {\n            icon: 'content_paste',\n            action: async () => {\n              await actionGlobalPasteState(pinia)\n              api.sendInspectorTree(INSPECTOR_ID)\n              api.sendInspectorState(INSPECTOR_ID)\n            },\n            tooltip: 'Replace the state with the content of your clipboard',\n          },\n          {\n            icon: 'save',\n            action: () => {\n              actionGlobalSaveState(pinia)\n            },\n            tooltip: 'Save the state as a JSON file',\n          },\n          {\n            icon: 'folder_open',\n            action: async () => {\n              await actionGlobalOpenStateFile(pinia)\n              api.sendInspectorTree(INSPECTOR_ID)\n              api.sendInspectorState(INSPECTOR_ID)\n            },\n            tooltip: 'Import the state from a JSON file',\n          },\n        ],\n        nodeActions: [\n          {\n            icon: 'restore',\n            tooltip: 'Reset the state (with \"$reset\")',\n            action: (nodeId) => {\n              const store = pinia._s.get(nodeId)\n              if (!store) {\n                toastMessage(\n                  `Cannot reset \"${nodeId}\" store because it wasn't found.`,\n                  'warn'\n                )\n              } else if (typeof store.$reset !== 'function') {\n                toastMessage(\n                  `Cannot reset \"${nodeId}\" store because it doesn't have a \"$reset\" method implemented.`,\n                  'warn'\n                )\n              } else {\n                store.$reset()\n                toastMessage(`Store \"${nodeId}\" reset.`)\n              }\n            },\n          },\n        ],\n      })\n\n      api.on.inspectComponent((payload) => {\n        const proxy = (payload.componentInstance &&\n          payload.componentInstance.proxy) as\n          | ComponentPublicInstance\n          | undefined\n        if (proxy && proxy._pStores) {\n          const piniaStores = (\n            payload.componentInstance.proxy as ComponentPublicInstance\n          )._pStores!\n\n          Object.values(piniaStores).forEach((store) => {\n            payload.instanceData.state.push({\n              type: getStoreType(store.$id),\n              key: 'state',\n              editable: true,\n              value: store._isOptionsAPI\n                ? {\n                    _custom: {\n                      value: toRaw(store.$state),\n                      actions: [\n                        {\n                          icon: 'restore',\n                          tooltip: 'Reset the state of this store',\n                          action: () => store.$reset(),\n                        },\n                      ],\n                    },\n                  }\n                : // NOTE: workaround to unwrap transferred refs\n                  Object.keys(store.$state).reduce((state, key) => {\n                    state[key] = store.$state[key]\n                    return state\n                  }, {} as StateTree),\n            })\n\n            if (store._getters && store._getters.length) {\n              payload.instanceData.state.push({\n                type: getStoreType(store.$id),\n                key: 'getters',\n                editable: false,\n                value: store._getters.reduce((getters, key) => {\n                  try {\n                    getters[key] = store[key]\n                  } catch (error) {\n                    // @ts-expect-error: we just want to show it in devtools\n                    getters[key] = error\n                  }\n                  return getters\n                }, {} as _GettersTree<StateTree>),\n              })\n            }\n          })\n        }\n      })\n\n      api.on.getInspectorTree((payload) => {\n        if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {\n          let stores: Array<StoreGeneric | Pinia> = [pinia]\n          stores = stores.concat(Array.from(pinia._s.values()))\n\n          payload.rootNodes = (\n            payload.filter\n              ? stores.filter((store) =>\n                  '$id' in store\n                    ? store.$id\n                        .toLowerCase()\n                        .includes(payload.filter.toLowerCase())\n                    : PINIA_ROOT_LABEL.toLowerCase().includes(\n                        payload.filter.toLowerCase()\n                      )\n                )\n              : stores\n          ).map(formatStoreForInspectorTree)\n        }\n      })\n\n      // Expose pinia instance as $pinia to window\n      globalThis.$pinia = pinia\n\n      api.on.getInspectorState((payload) => {\n        if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {\n          const inspectedStore =\n            payload.nodeId === PINIA_ROOT_ID\n              ? pinia\n              : pinia._s.get(payload.nodeId)\n\n          if (!inspectedStore) {\n            // this could be the selected store restored for a different project\n            // so it's better not to say anything here\n            return\n          }\n\n          if (inspectedStore) {\n            // Expose selected store as $store to window\n            if (payload.nodeId !== PINIA_ROOT_ID)\n              globalThis.$store = toRaw(inspectedStore as StoreGeneric)\n            payload.state = formatStoreForInspectorState(inspectedStore)\n          }\n        }\n      })\n\n      api.on.editInspectorState((payload) => {\n        if (payload.app === app && payload.inspectorId === INSPECTOR_ID) {\n          const inspectedStore =\n            payload.nodeId === PINIA_ROOT_ID\n              ? pinia\n              : pinia._s.get(payload.nodeId)\n\n          if (!inspectedStore) {\n            return toastMessage(`store \"${payload.nodeId}\" not found`, 'error')\n          }\n\n          const { path } = payload\n\n          if (!isPinia(inspectedStore)) {\n            // access only the state\n            if (\n              path.length !== 1 ||\n              !inspectedStore._customProperties.has(path[0]) ||\n              path[0] in inspectedStore.$state\n            ) {\n              path.unshift('$state')\n            }\n          } else {\n            // Root access, we can omit the `.value` because the devtools API does it for us\n            path.unshift('state')\n          }\n          isTimelineActive = false\n          payload.set(inspectedStore, path, payload.state.value)\n          isTimelineActive = true\n        }\n      })\n\n      api.on.editComponentState((payload) => {\n        if (payload.type.startsWith('🍍')) {\n          const storeId = payload.type.replace(/^🍍\\s*/, '')\n          const store = pinia._s.get(storeId)\n\n          if (!store) {\n            return toastMessage(`store \"${storeId}\" not found`, 'error')\n          }\n\n          const { path } = payload\n          if (path[0] !== 'state') {\n            return toastMessage(\n              `Invalid path for store \"${storeId}\":\\n${path}\\nOnly state can be modified.`\n            )\n          }\n\n          // rewrite the first entry to be able to directly set the state as\n          // well as any other path\n          path[0] = '$state'\n          isTimelineActive = false\n          payload.set(store, path, payload.state.value)\n          isTimelineActive = true\n        }\n      })\n    }\n  )\n}\n\nfunction addStoreToDevtools(app: App, store: StoreGeneric) {\n  if (!componentStateTypes.includes(getStoreType(store.$id))) {\n    componentStateTypes.push(getStoreType(store.$id))\n  }\n\n  setupDevtoolsPlugin(\n    {\n      id: 'dev.esm.pinia',\n      label: 'Pinia 🍍',\n      logo: 'https://pinia.vuejs.org/logo.svg',\n      packageName: 'pinia',\n      homepage: 'https://pinia.vuejs.org',\n      componentStateTypes,\n      app,\n      settings: {\n        logStoreChanges: {\n          label: 'Notify about new/deleted stores',\n          type: 'boolean',\n          defaultValue: true,\n        },\n        // useEmojis: {\n        //   label: 'Use emojis in messages ⚡️',\n        //   type: 'boolean',\n        //   defaultValue: true,\n        // },\n      },\n    },\n    (api) => {\n      // gracefully handle errors\n      const now = typeof api.now === 'function' ? api.now.bind(api) : Date.now\n\n      store.$onAction(({ after, onError, name, args }) => {\n        const groupId = runningActionId++\n\n        api.addTimelineEvent({\n          layerId: MUTATIONS_LAYER_ID,\n          event: {\n            time: now(),\n            title: '🛫 ' + name,\n            subtitle: 'start',\n            data: {\n              store: formatDisplay(store.$id),\n              action: formatDisplay(name),\n              args,\n            },\n            groupId,\n          },\n        })\n\n        after((result) => {\n          activeAction = undefined\n          api.addTimelineEvent({\n            layerId: MUTATIONS_LAYER_ID,\n            event: {\n              time: now(),\n              title: '🛬 ' + name,\n              subtitle: 'end',\n              data: {\n                store: formatDisplay(store.$id),\n                action: formatDisplay(name),\n                args,\n                result,\n              },\n              groupId,\n            },\n          })\n        })\n\n        onError((error) => {\n          activeAction = undefined\n          api.addTimelineEvent({\n            layerId: MUTATIONS_LAYER_ID,\n            event: {\n              time: now(),\n              logType: 'error',\n              title: '💥 ' + name,\n              subtitle: 'end',\n              data: {\n                store: formatDisplay(store.$id),\n                action: formatDisplay(name),\n                args,\n                error,\n              },\n              groupId,\n            },\n          })\n        })\n      }, true)\n\n      store._customProperties.forEach((name) => {\n        watch(\n          () => unref<unknown>(store[name]),\n          (newValue, oldValue) => {\n            api.notifyComponentUpdate()\n            api.sendInspectorState(INSPECTOR_ID)\n            if (isTimelineActive) {\n              api.addTimelineEvent({\n                layerId: MUTATIONS_LAYER_ID,\n                event: {\n                  time: now(),\n                  title: 'Change',\n                  subtitle: name,\n                  data: {\n                    newValue,\n                    oldValue,\n                  },\n                  groupId: activeAction,\n                },\n              })\n            }\n          },\n          { deep: true }\n        )\n      })\n\n      store.$subscribe(\n        ({ events, type }, state) => {\n          api.notifyComponentUpdate()\n          api.sendInspectorState(INSPECTOR_ID)\n\n          if (!isTimelineActive) return\n          // rootStore.state[store.id] = state\n\n          const eventData: TimelineEvent = {\n            time: now(),\n            title: formatMutationType(type),\n            data: assign(\n              { store: formatDisplay(store.$id) },\n              formatEventData(events)\n            ),\n            groupId: activeAction,\n          }\n\n          if (type === MutationType.patchFunction) {\n            eventData.subtitle = '⤵️'\n          } else if (type === MutationType.patchObject) {\n            eventData.subtitle = '🧩'\n          } else if (events && !Array.isArray(events)) {\n            eventData.subtitle = events.type\n          }\n\n          if (events) {\n            eventData.data['rawEvent(s)'] = {\n              _custom: {\n                display: 'DebuggerEvent',\n                type: 'object',\n                tooltip: 'raw DebuggerEvent[]',\n                value: events,\n              },\n            }\n          }\n\n          api.addTimelineEvent({\n            layerId: MUTATIONS_LAYER_ID,\n            event: eventData,\n          })\n        },\n        { detached: true, flush: 'sync' }\n      )\n\n      const hotUpdate = store._hotUpdate\n      store._hotUpdate = markRaw((newStore) => {\n        hotUpdate(newStore)\n        api.addTimelineEvent({\n          layerId: MUTATIONS_LAYER_ID,\n          event: {\n            time: now(),\n            title: '🔥 ' + store.$id,\n            subtitle: 'HMR update',\n            data: {\n              store: formatDisplay(store.$id),\n              info: formatDisplay(`HMR update`),\n            },\n          },\n        })\n        // update the devtools too\n        api.notifyComponentUpdate()\n        api.sendInspectorTree(INSPECTOR_ID)\n        api.sendInspectorState(INSPECTOR_ID)\n      })\n\n      const { $dispose } = store\n      store.$dispose = () => {\n        $dispose()\n        api.notifyComponentUpdate()\n        api.sendInspectorTree(INSPECTOR_ID)\n        api.sendInspectorState(INSPECTOR_ID)\n        api.getSettings().logStoreChanges &&\n          toastMessage(`Disposed \"${store.$id}\" store 🗑`)\n      }\n\n      // trigger an update so it can display new registered stores\n      api.notifyComponentUpdate()\n      api.sendInspectorTree(INSPECTOR_ID)\n      api.sendInspectorState(INSPECTOR_ID)\n      api.getSettings().logStoreChanges &&\n        toastMessage(`\"${store.$id}\" store installed 🆕`)\n    }\n  )\n}\n\nlet runningActionId = 0\nlet activeAction: number | undefined\n\n/**\n * Patches a store to enable action grouping in devtools by wrapping the store with a Proxy that is passed as the\n * context of all actions, allowing us to set `runningAction` on each access and effectively associating any state\n * mutation to the action.\n *\n * @param store - store to patch\n * @param actionNames - list of actionst to patch\n */\nfunction patchActionForGrouping(\n  store: StoreGeneric,\n  actionNames: string[],\n  wrapWithProxy: boolean\n) {\n  // original actions of the store as they are given by pinia. We are going to override them\n  const actions = actionNames.reduce((storeActions, actionName) => {\n    // use toRaw to avoid tracking #541\n    storeActions[actionName] = toRaw(store)[actionName]\n    return storeActions\n  }, {} as _ActionsTree)\n\n  for (const actionName in actions) {\n    store[actionName] = function () {\n      // the running action id is incremented in a before action hook\n      const _actionId = runningActionId\n      const trackedStore = wrapWithProxy\n        ? new Proxy(store, {\n            get(...args) {\n              activeAction = _actionId\n              return Reflect.get(...args)\n            },\n            set(...args) {\n              activeAction = _actionId\n              return Reflect.set(...args)\n            },\n          })\n        : store\n\n      // For Setup Stores we need https://github.com/tc39/proposal-async-context\n      activeAction = _actionId\n      const retValue = actions[actionName].apply(\n        trackedStore,\n        arguments as unknown as any[]\n      )\n      // this is safer as async actions in Setup Stores would associate mutations done outside of the action\n      activeAction = undefined\n      return retValue\n    }\n  }\n}\n\n/**\n * pinia.use(devtoolsPlugin)\n */\nexport function devtoolsPlugin<\n  Id extends string = string,\n  S extends StateTree = StateTree,\n  G extends object = _GettersTree<S>,\n  A extends object = _ActionsTree,\n>({ app, store, options }: PiniaPluginContext<Id, S, G, A>) {\n  // HMR module\n  if (store.$id.startsWith('__hot:')) {\n    return\n  }\n\n  // detect option api vs setup api\n  store._isOptionsAPI = !!options.state\n\n  // Do not overwrite actions mocked by @pinia/testing (#2298)\n  if (!store._p._testing) {\n    patchActionForGrouping(\n      store as StoreGeneric,\n      Object.keys(options.actions),\n      store._isOptionsAPI\n    )\n\n    // Upgrade the HMR to also update the new actions\n    const originalHotUpdate = store._hotUpdate\n    toRaw(store)._hotUpdate = function (newStore) {\n      originalHotUpdate.apply(this, arguments as any)\n      patchActionForGrouping(\n        store as StoreGeneric,\n        Object.keys(newStore._hmrPayload.actions),\n        !!store._isOptionsAPI\n      )\n    }\n  }\n\n  addStoreToDevtools(\n    app,\n    // FIXME: is there a way to allow the assignment from Store<Id, S, G, A> to StoreGeneric?\n    store as StoreGeneric\n  )\n}\n\ndeclare global {\n  /**\n   * Exposes the `pinia` instance when Devtools are opened.\n   */\n  var $pinia: Pinia | undefined\n  /**\n   * Exposes the current store when Devtools are opened.\n   */\n  var $store: StoreGeneric | undefined\n}\n"
  },
  {
    "path": "packages/pinia/src/devtools/utils.ts",
    "content": "import { Pinia } from '../rootStore'\n\n/**\n * Shows a toast or console.log\n *\n * @param message - message to log\n * @param type - different color of the tooltip\n */\nexport function toastMessage(\n  message: string,\n  type?: 'normal' | 'error' | 'warn' | undefined\n) {\n  const piniaMessage = '🍍 ' + message\n\n  if (type === 'error') {\n    console.error(piniaMessage)\n  } else if (type === 'warn') {\n    console.warn(piniaMessage)\n  } else {\n    console.debug(piniaMessage)\n  }\n}\n\nexport function isPinia(o: any): o is Pinia {\n  return '_a' in o && 'install' in o\n}\n"
  },
  {
    "path": "packages/pinia/src/env.ts",
    "content": "export const IS_CLIENT = typeof window !== 'undefined'\n"
  },
  {
    "path": "packages/pinia/src/global.d.ts",
    "content": "// Global compile-time constants\ndeclare var __DEV__: boolean\ndeclare var __TEST__: boolean\ndeclare var __USE_DEVTOOLS__: boolean\ndeclare var __VUE_DEVTOOLS_TOAST__: (\n  message: string,\n  type?: 'normal' | 'error' | 'warn'\n) => void\n"
  },
  {
    "path": "packages/pinia/src/globalExtensions.ts",
    "content": "import type { Pinia } from './rootStore'\nimport type { StoreGeneric } from './types'\n\ndeclare module 'vue' {\n  // This seems to be needed to not break auto import types based on the order\n  // https://github.com/vuejs/pinia/pull/2730\n  interface GlobalComponents {}\n  interface ComponentCustomProperties {\n    /**\n     * Access to the application's Pinia\n     */\n    $pinia: Pinia\n\n    /**\n     * Cache of stores instantiated by the current instance. Used by devtools to\n     * list currently used stores. Used internally by Pinia.\n     *\n     * @internal\n     */\n    _pStores?: Record<string, StoreGeneric>\n  }\n}\n\n// normally this is only needed in .d.ts files\nexport {}\n"
  },
  {
    "path": "packages/pinia/src/hmr.ts",
    "content": "import { isRef, isReactive } from 'vue'\nimport { Pinia } from './rootStore'\nimport {\n  isPlainObject,\n  StateTree,\n  StoreDefinition,\n  StoreGeneric,\n  _GettersTree,\n  _Method,\n  _ActionsTree,\n} from './types'\n\n/**\n * Checks if a function is a `StoreDefinition`.\n *\n * @param fn - object to test\n * @returns true if `fn` is a StoreDefinition\n */\nexport const isUseStore = (fn: any): fn is StoreDefinition => {\n  return typeof fn === 'function' && typeof fn.$id === 'string'\n}\n\n/**\n * Mutates in place `newState` with `oldState` to _hot update_ it. It will\n * remove any key not existing in `newState` and recursively merge plain\n * objects.\n *\n * @param newState - new state object to be patched\n * @param oldState - old state that should be used to patch newState\n * @returns - newState\n */\nexport function patchObject(\n  newState: Record<string, any>,\n  oldState: Record<string, any>\n): Record<string, any> {\n  // no need to go through symbols because they cannot be serialized anyway\n  for (const key in oldState) {\n    const subPatch = oldState[key]\n\n    // skip the whole sub tree\n    if (!(key in newState)) {\n      continue\n    }\n\n    const targetValue = newState[key]\n    if (\n      isPlainObject(targetValue) &&\n      isPlainObject(subPatch) &&\n      !isRef(subPatch) &&\n      !isReactive(subPatch)\n    ) {\n      newState[key] = patchObject(targetValue, subPatch)\n    } else {\n      // objects are either a bit more complex (e.g. refs) or primitives, so we\n      // just set the whole thing\n      newState[key] = subPatch\n    }\n  }\n\n  return newState\n}\n\n/**\n * Creates an _accept_ function to pass to `import.meta.hot` in Vite applications.\n *\n * @example\n * ```js\n * const useUser = defineStore(...)\n * if (import.meta.hot) {\n *   import.meta.hot.accept(acceptHMRUpdate(useUser, import.meta.hot))\n * }\n * ```\n *\n * @param initialUseStore - return of the defineStore to hot update\n * @param hot - `import.meta.hot`\n */\nexport function acceptHMRUpdate<\n  Id extends string = string,\n  S extends StateTree = StateTree,\n  G extends _GettersTree<S> = _GettersTree<S>,\n  A = _ActionsTree,\n>(initialUseStore: StoreDefinition<Id, S, G, A>, hot: any) {\n  // strip as much as possible from iife.prod\n  if (!__DEV__) {\n    return () => {}\n  }\n  return (newModule: any) => {\n    const pinia: Pinia | undefined = hot.data.pinia || initialUseStore._pinia\n\n    if (!pinia) {\n      // this store is still not used\n      return\n    }\n\n    // preserve the pinia instance across loads\n    hot.data.pinia = pinia\n\n    // console.log('got data', newStore)\n    for (const exportName in newModule) {\n      const useStore = newModule[exportName]\n      // console.log('checking for', exportName)\n      if (isUseStore(useStore) && pinia._s.has(useStore.$id)) {\n        // console.log('Accepting update for', useStore.$id)\n        const id = useStore.$id\n\n        if (id !== initialUseStore.$id) {\n          console.warn(\n            `The id of the store changed from \"${initialUseStore.$id}\" to \"${id}\". Reloading.`\n          )\n          // return import.meta.hot.invalidate()\n          return hot.invalidate()\n        }\n\n        const existingStore: StoreGeneric = pinia._s.get(id)!\n        if (!existingStore) {\n          console.log(`[Pinia]: skipping hmr because store doesn't exist yet`)\n          return\n        }\n        useStore(pinia, existingStore)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/pinia/src/index.ts",
    "content": "/**\n * @module pinia\n */\nexport { setActivePinia, getActivePinia } from './rootStore'\nexport { createPinia, disposePinia } from './createPinia'\nexport type { Pinia, PiniaPlugin, PiniaPluginContext } from './rootStore'\n\nexport { defineStore, skipHydrate, shouldHydrate } from './store'\nexport type {\n  StoreActions,\n  StoreGetters,\n  StoreState,\n  SetupStoreDefinition,\n} from './store'\n\nexport type {\n  StateTree,\n  Store,\n  StoreGeneric,\n  StoreDefinition,\n  _StoreWithGetters,\n  _GettersTree,\n  _ActionsTree,\n  _Method,\n  _StoreWithActions,\n  _StoreWithState,\n  StoreProperties,\n  StoreOnActionListener,\n  _StoreOnActionListenerContext,\n  StoreOnActionListenerContext,\n  SubscriptionCallback,\n  SubscriptionCallbackMutation,\n  SubscriptionCallbackMutationDirect,\n  SubscriptionCallbackMutationPatchFunction,\n  SubscriptionCallbackMutationPatchObject,\n  _SubscriptionCallbackMutationBase,\n  PiniaCustomProperties,\n  PiniaCustomStateProperties,\n  DefineStoreOptionsBase,\n  DefineStoreOptions,\n  DefineSetupStoreOptions,\n  DefineStoreOptionsInPlugin,\n  _ExtractActionsFromSetupStore,\n  _ExtractGettersFromSetupStore,\n  _ExtractStateFromSetupStore,\n  _DeepPartial,\n  _ExtractActionsFromSetupStore_Keys,\n  _ExtractGettersFromSetupStore_Keys,\n  _ExtractStateFromSetupStore_Keys,\n  _UnwrapAll,\n} from './types'\nexport { MutationType } from './types'\n\nexport {\n  mapActions,\n  mapStores,\n  mapState,\n  mapWritableState,\n  mapGetters,\n  setMapStoreSuffix,\n} from './mapHelpers'\n\nexport { storeToRefs } from './storeToRefs'\n\nexport type {\n  MapStoresCustomization,\n  _MapActionsObjectReturn,\n  _MapActionsReturn,\n  _MapStateObjectReturn,\n  _MapStateReturn,\n  _MapWritableStateObjectReturn,\n  _MapWritableStateReturn,\n  _Spread,\n  _StoreObject,\n} from './mapHelpers'\n\nexport { acceptHMRUpdate } from './hmr'\n\nexport * from './globalExtensions'\n"
  },
  {
    "path": "packages/pinia/src/mapHelpers.ts",
    "content": "import type { ComponentPublicInstance, ComputedRef, UnwrapRef } from 'vue'\nimport type {\n  _GettersTree,\n  _StoreWithGetters_Writable,\n  StateTree,\n  Store,\n  StoreDefinition,\n} from './types'\n\n/**\n * Interface to allow customizing map helpers. Extend this interface with the\n * following properties:\n *\n * - `suffix`: string. Affects the suffix of `mapStores()`, defaults to `Store`.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface MapStoresCustomization {\n  // cannot be added or it wouldn't be able to be extended\n  // suffix?: string\n}\n\n/**\n * For internal use **only**.\n */\nexport type _StoreObject<S> =\n  S extends StoreDefinition<\n    infer Ids,\n    infer State,\n    infer Getters,\n    infer Actions\n  >\n    ? {\n        [Id in `${Ids}${MapStoresCustomization extends Record<\n          'suffix',\n          infer Suffix\n        >\n          ? Suffix\n          : 'Store'}`]: () => Store<\n          Id extends `${infer RealId}${MapStoresCustomization extends Record<\n            'suffix',\n            infer Suffix\n          >\n            ? Suffix\n            : 'Store'}`\n            ? RealId\n            : string,\n          State,\n          Getters,\n          Actions\n        >\n      }\n    : {}\n\n/**\n * For internal use **only**.\n */\nexport type _Spread<A extends readonly any[]> = A extends [infer L, ...infer R]\n  ? _StoreObject<L> & _Spread<R>\n  : unknown\n\nexport let mapStoreSuffix = 'Store'\n\n/**\n * Changes the suffix added by `mapStores()`. Can be set to an empty string.\n * Defaults to `\"Store\"`. Make sure to extend the MapStoresCustomization\n * interface if you are using TypeScript.\n *\n * @param suffix - new suffix\n */\nexport function setMapStoreSuffix(\n  suffix: MapStoresCustomization extends Record<'suffix', infer Suffix>\n    ? Suffix\n    : string // could be 'Store' but that would be annoying for JS\n): void {\n  mapStoreSuffix = suffix\n}\n\n/**\n * Allows using stores without the composition API (`setup()`) by generating an\n * object to be spread in the `computed` field of a component. It accepts a list\n * of store definitions.\n *\n * @example\n * ```js\n * export default {\n *   computed: {\n *     // other computed properties\n *     ...mapStores(useUserStore, useCartStore)\n *   },\n *\n *   created() {\n *     this.userStore // store with id \"user\"\n *     this.cartStore // store with id \"cart\"\n *   }\n * }\n * ```\n *\n * @param stores - list of stores to map to an object\n */\nexport function mapStores<Stores extends any[]>(\n  ...stores: [...Stores]\n): _Spread<Stores> {\n  if (__DEV__ && Array.isArray(stores[0])) {\n    console.warn(\n      `[🍍]: Directly pass all stores to \"mapStores()\" without putting them in an array:\\n` +\n        `Replace\\n` +\n        `\\tmapStores([useAuthStore, useCartStore])\\n` +\n        `with\\n` +\n        `\\tmapStores(useAuthStore, useCartStore)\\n` +\n        `This will fail in production if not fixed.`\n    )\n    stores = stores[0]\n  }\n\n  return stores.reduce((reduced, useStore) => {\n    // @ts-expect-error: $id is added by defineStore\n    reduced[useStore.$id + mapStoreSuffix] = function (\n      this: ComponentPublicInstance\n    ) {\n      return useStore(this.$pinia)\n    }\n    return reduced\n  }, {} as _Spread<Stores>)\n}\n\n/**\n * For internal use **only**\n */\nexport type _MapStateReturn<\n  S extends StateTree,\n  G extends _GettersTree<S> | { [key: string]: ComputedRef },\n  Keys extends keyof S | keyof G = keyof S | keyof G,\n> = {\n  // [key in keyof S | keyof G]: () => key extends keyof S\n  //   ? S[key]\n  //   : key extends keyof G\n  //   ? G[key]\n  //   : never\n  [key in Keys]: key extends keyof Store<string, S, G, {}>\n    ? () => Store<string, S, G, {}>[key]\n    : never\n}\n\n/**\n * For internal use **only**\n */\nexport type _MapStateObjectReturn<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S> | { [key: string]: ComputedRef },\n  A,\n  T extends Record<\n    string,\n    keyof S | keyof G | ((store: Store<Id, S, G, A>) => any)\n  > = {},\n> = {\n  [key in keyof T]: () => T[key] extends (store: any) => infer R\n    ? R\n    : T[key] extends keyof Store<Id, S, G, A>\n      ? Store<Id, S, G, A>[T[key]]\n      : never\n}\n\n/**\n * Allows using state and getters from one store without using the composition\n * API (`setup()`) by generating an object to be spread in the `computed` field\n * of a component. The values of the object are the state properties/getters\n * while the keys are the names of the resulting computed properties.\n * Optionally, you can also pass a custom function that will receive the store\n * as its first argument. Note that while it has access to the component\n * instance via `this`, it won't be typed.\n *\n * @example\n * ```js\n * export default {\n *   computed: {\n *     // other computed properties\n *     // useCounterStore has a state property named `count` and a getter `double`\n *     ...mapState(useCounterStore, {\n *       n: 'count',\n *       triple: store => store.n * 3,\n *       // note we can't use an arrow function if we want to use `this`\n *       custom(store) {\n *         return this.someComponentValue + store.n\n *       },\n *       doubleN: 'double'\n *     })\n *   },\n *\n *   created() {\n *     this.n // 2\n *     this.doubleN // 4\n *   }\n * }\n * ```\n *\n * @param useStore - store to map from\n * @param keyMapper - object of state properties or getters\n */\nexport function mapState<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S> | { [key: string]: ComputedRef },\n  A,\n  KeyMapper extends Record<\n    string,\n    keyof S | keyof G | ((store: Store<Id, S, G, A>) => any)\n  >,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keyMapper: KeyMapper\n): _MapStateObjectReturn<Id, S, G, A, KeyMapper>\n\n/**\n * Allows using state and getters from one store without using the composition\n * API (`setup()`) by generating an object to be spread in the `computed` field\n * of a component.\n *\n * @example\n * ```js\n * export default {\n *   computed: {\n *     // other computed properties\n *     ...mapState(useCounterStore, ['count', 'double'])\n *   },\n *\n *   created() {\n *     this.count // 2\n *     this.double // 4\n *   }\n * }\n * ```\n *\n * @param useStore - store to map from\n * @param keys - array of state properties or getters\n */\nexport function mapState<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S> | { [key: string]: ComputedRef },\n  A,\n  Keys extends keyof S | keyof G,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keys: readonly Keys[]\n): _MapStateReturn<S, G, Keys>\n\n/**\n * Allows using state and getters from one store without using the composition\n * API (`setup()`) by generating an object to be spread in the `computed` field\n * of a component.\n *\n * @param useStore - store to map from\n * @param keysOrMapper - array or object\n */\nexport function mapState<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S> | { [key: string]: ComputedRef },\n  A,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keysOrMapper: any\n): _MapStateReturn<S, G> | _MapStateObjectReturn<Id, S, G, A> {\n  return Array.isArray(keysOrMapper)\n    ? keysOrMapper.reduce(\n        (reduced, key) => {\n          reduced[key] = function (this: ComponentPublicInstance) {\n            // @ts-expect-error: FIXME: should work?\n            return useStore(this.$pinia)[key]\n          } as () => any\n          return reduced\n        },\n        {} as _MapStateReturn<S, G>\n      )\n    : Object.keys(keysOrMapper).reduce(\n        (reduced, key: string) => {\n          // @ts-expect-error\n          reduced[key] = function (this: ComponentPublicInstance) {\n            const store = useStore(this.$pinia)\n            const storeKey = keysOrMapper[key]\n            // for some reason TS is unable to infer the type of storeKey to be a\n            // function\n            return typeof storeKey === 'function'\n              ? (storeKey as (store: Store<Id, S, G, A>) => any).call(\n                  this,\n                  store\n                )\n              : // @ts-expect-error: FIXME: should work?\n                store[storeKey]\n          }\n          return reduced\n        },\n        {} as _MapStateObjectReturn<Id, S, G, A>\n      )\n}\n\n/**\n * Alias for `mapState()`. You should use `mapState()` instead.\n * @deprecated use `mapState()` instead.\n */\nexport const mapGetters = mapState\n\n/**\n * For internal use **only**\n */\nexport type _MapActionsReturn<A> = {\n  [key in keyof A]: A[key]\n}\n\n/**\n * For internal use **only**\n */\nexport type _MapActionsObjectReturn<A, T extends Record<string, keyof A>> = {\n  [key in keyof T]: A[T[key]]\n}\n\n/**\n * Allows directly using actions from your store without using the composition\n * API (`setup()`) by generating an object to be spread in the `methods` field\n * of a component. The values of the object are the actions while the keys are\n * the names of the resulting methods.\n *\n * @example\n * ```js\n * export default {\n *   methods: {\n *     // other methods properties\n *     // useCounterStore has two actions named `increment` and `setCount`\n *     ...mapActions(useCounterStore, { more: 'increment', setIt: 'setCount' })\n *   },\n *\n *   created() {\n *     this.more()\n *     this.setIt(2)\n *   }\n * }\n * ```\n *\n * @param useStore - store to map from\n * @param keyMapper - object to define new names for the actions\n */\nexport function mapActions<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S>,\n  A,\n  KeyMapper extends Record<string, keyof A>,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keyMapper: KeyMapper\n): _MapActionsObjectReturn<A, KeyMapper>\n/**\n * Allows directly using actions from your store without using the composition\n * API (`setup()`) by generating an object to be spread in the `methods` field\n * of a component.\n *\n * @example\n * ```js\n * export default {\n *   methods: {\n *     // other methods properties\n *     ...mapActions(useCounterStore, ['increment', 'setCount'])\n *   },\n *\n *   created() {\n *     this.increment()\n *     this.setCount(2) // pass arguments as usual\n *   }\n * }\n * ```\n *\n * @param useStore - store to map from\n * @param keys - array of action names to map\n */\nexport function mapActions<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S>,\n  A,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keys: Array<keyof A>\n): _MapActionsReturn<A>\n/**\n * Allows directly using actions from your store without using the composition\n * API (`setup()`) by generating an object to be spread in the `methods` field\n * of a component.\n *\n * @param useStore - store to map from\n * @param keysOrMapper - array or object\n */\nexport function mapActions<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S>,\n  A,\n  KeyMapper extends Record<string, keyof A>,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keysOrMapper: Array<keyof A> | KeyMapper\n): _MapActionsReturn<A> | _MapActionsObjectReturn<A, KeyMapper> {\n  return Array.isArray(keysOrMapper)\n    ? keysOrMapper.reduce((reduced, key) => {\n        // @ts-expect-error\n        reduced[key] = function (\n          this: ComponentPublicInstance,\n          ...args: any[]\n        ) {\n          // @ts-expect-error: FIXME: should work?\n          return useStore(this.$pinia)[key](...args)\n        }\n        return reduced\n      }, {} as _MapActionsReturn<A>)\n    : Object.keys(keysOrMapper).reduce(\n        (reduced, key: keyof KeyMapper) => {\n          // @ts-expect-error\n          reduced[key] = function (\n            this: ComponentPublicInstance,\n            ...args: any[]\n          ) {\n            // @ts-expect-error: FIXME: should work?\n            return useStore(this.$pinia)[keysOrMapper[key]](...args)\n          }\n          return reduced\n        },\n        {} as _MapActionsObjectReturn<A, KeyMapper>\n      )\n}\n\n/**\n * For internal use **only**\n */\nexport type _MapWritableStateKeys<S extends StateTree, G> =\n  | keyof UnwrapRef<S>\n  | keyof _StoreWithGetters_Writable<G>\n\n/**\n * For internal use **only**\n */\nexport type _MapWritableStateReturn<\n  S extends StateTree,\n  G,\n  Keys extends _MapWritableStateKeys<S, G>,\n> = {\n  [key in Keys]: {\n    get: () => UnwrapRef<(S & G)[key]>\n    set: (value: UnwrapRef<(S & G)[key]>) => any\n  }\n}\n\n/**\n * For internal use **only**\n */\nexport type _MapWritableStateObjectReturn<\n  S extends StateTree,\n  G,\n  KeyMapper extends Record<string, _MapWritableStateKeys<S, G>>,\n> = {\n  [key in keyof KeyMapper]: {\n    get: () => UnwrapRef<(S & G)[KeyMapper[key]]>\n    set: (value: UnwrapRef<(S & G)[KeyMapper[key]]>) => any\n  }\n}\n\n/**\n * Same as `mapState()` but creates computed setters as well so the state can be\n * modified. Differently from `mapState()`, only `state` properties can be\n * added.\n *\n * @param useStore - store to map from\n * @param keyMapper - object of state properties\n */\nexport function mapWritableState<\n  Id extends string,\n  S extends StateTree,\n  G,\n  A,\n  KeyMapper extends Record<string, _MapWritableStateKeys<S, G>>,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keyMapper: KeyMapper\n): _MapWritableStateObjectReturn<S, G, KeyMapper>\n/**\n * Allows using state and getters from one store without using the composition\n * API (`setup()`) by generating an object to be spread in the `computed` field\n * of a component.\n *\n * @param useStore - store to map from\n * @param keys - array of state properties\n */\nexport function mapWritableState<\n  Id extends string,\n  S extends StateTree,\n  G,\n  A,\n  Keys extends _MapWritableStateKeys<S, G>,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keys: readonly Keys[]\n): Pick<_MapWritableStateReturn<S, G, Keys>, Keys>\n/**\n * Allows using state and getters from one store without using the composition\n * API (`setup()`) by generating an object to be spread in the `computed` field\n * of a component.\n *\n * @param useStore - store to map from\n * @param keysOrMapper - array or object\n */\nexport function mapWritableState<\n  Id extends string,\n  S extends StateTree,\n  G,\n  A,\n  Keys extends _MapWritableStateKeys<S, G>,\n  KeyArr extends Keys[],\n  KeyMapper extends Record<string, Keys>,\n>(\n  useStore: StoreDefinition<Id, S, G, A>,\n  keysOrMapper: KeyArr | KeyMapper\n):\n  | _MapWritableStateReturn<S, G, Keys>\n  | _MapWritableStateObjectReturn<S, G, KeyMapper> {\n  return Array.isArray(keysOrMapper)\n    ? keysOrMapper.reduce(\n        (reduced, key) => {\n          reduced[key] = {\n            get(this: ComponentPublicInstance) {\n              return useStore(this.$pinia)[key] as (S & G)[typeof key]\n            },\n            set(\n              this: ComponentPublicInstance,\n              value: Store<Id, S, G, A>[typeof key]\n            ) {\n              return (useStore(this.$pinia)[key] = value)\n            },\n          }\n          return reduced\n        },\n        {} as _MapWritableStateReturn<S, G, Keys>\n      )\n    : Object.keys(keysOrMapper).reduce(\n        (reduced, key: keyof KeyMapper) => {\n          reduced[key] = {\n            get(this: ComponentPublicInstance) {\n              return useStore(this.$pinia)[keysOrMapper[key]] as (S &\n                G)[KeyMapper[typeof key]]\n            },\n            set(\n              this: ComponentPublicInstance,\n              value: Store<Id, S, G, A>[KeyMapper[typeof key]]\n            ) {\n              return (useStore(this.$pinia)[keysOrMapper[key]] = value)\n            },\n          }\n          return reduced\n        },\n        {} as _MapWritableStateObjectReturn<S, G, KeyMapper>\n      )\n}\n"
  },
  {
    "path": "packages/pinia/src/rootStore.ts",
    "content": "import {\n  App,\n  EffectScope,\n  inject,\n  hasInjectionContext,\n  InjectionKey,\n  Ref,\n} from 'vue'\nimport {\n  StateTree,\n  PiniaCustomProperties,\n  _Method,\n  Store,\n  _GettersTree,\n  _ActionsTree,\n  PiniaCustomStateProperties,\n  DefineStoreOptionsInPlugin,\n  StoreGeneric,\n} from './types'\nimport { IS_CLIENT } from './env'\n\n/**\n * setActivePinia must be called to handle SSR at the top of functions like\n * `fetch`, `setup`, `serverPrefetch` and others\n */\nexport let activePinia: Pinia | undefined\n\n/**\n * Sets or unsets the active pinia. Used in SSR and internally when calling\n * actions and getters\n *\n * @param pinia - Pinia instance\n */\n// @ts-expect-error: cannot constrain the type of the return\nexport const setActivePinia: _SetActivePinia = (pinia) => (activePinia = pinia)\n\ninterface _SetActivePinia {\n  (pinia: Pinia): Pinia\n  (pinia: undefined): undefined\n  (pinia: Pinia | undefined): Pinia | undefined\n}\n\n/**\n * Get the currently active pinia if there is any.\n */\nexport const getActivePinia = __DEV__\n  ? (): Pinia | undefined => {\n      const pinia = hasInjectionContext() && inject(piniaSymbol)\n\n      if (!pinia && !IS_CLIENT) {\n        console.error(\n          `[🍍]: Pinia instance not found in context. This falls back to the global activePinia which exposes you to cross-request pollution on the server. Most of the time, it means you are calling \"useStore()\" in the wrong place.\\n` +\n            `Read https://vuejs.org/guide/reusability/composables.html to learn more`\n        )\n      }\n\n      return pinia || activePinia\n    }\n  : (): Pinia | undefined =>\n      (hasInjectionContext() && inject(piniaSymbol)) || activePinia\n\n/**\n * Every application must own its own pinia to be able to create stores\n */\nexport interface Pinia {\n  install: (app: App) => void\n\n  /**\n   * root state\n   */\n  state: Ref<Record<string, StateTree>>\n\n  /**\n   * Adds a store plugin to extend every store\n   *\n   * @param plugin - store plugin to add\n   */\n  use(plugin: PiniaPlugin): Pinia\n\n  /**\n   * Installed store plugins\n   *\n   * @internal\n   */\n  _p: PiniaPlugin[]\n\n  /**\n   * App linked to this Pinia instance\n   *\n   * @internal\n   */\n  _a: App\n\n  /**\n   * Effect scope the pinia is attached to\n   *\n   * @internal\n   */\n  _e: EffectScope\n\n  /**\n   * Registry of stores used by this pinia.\n   *\n   * @internal\n   */\n  _s: Map<string, StoreGeneric>\n\n  /**\n   * Added by `createTestingPinia()` to bypass `useStore(pinia)`.\n   *\n   * @internal\n   */\n  _testing?: boolean\n}\n\nexport const piniaSymbol = (\n  __DEV__ ? Symbol('pinia') : /* istanbul ignore next */ Symbol()\n) as InjectionKey<Pinia>\n\n/**\n * Context argument passed to Pinia plugins.\n */\nexport interface PiniaPluginContext<\n  Id extends string = string,\n  S extends StateTree = StateTree,\n  G /* extends _GettersTree<S> */ = _GettersTree<S>,\n  A /* extends _ActionsTree */ = _ActionsTree,\n> {\n  /**\n   * pinia instance.\n   */\n  pinia: Pinia\n\n  /**\n   * Current app created with `Vue.createApp()`.\n   */\n  app: App\n\n  /**\n   * Current store being extended.\n   */\n  store: Store<Id, S, G, A>\n\n  /**\n   * Initial options defining the store when calling `defineStore()`.\n   */\n  options: DefineStoreOptionsInPlugin<Id, S, G, A>\n}\n\n/**\n * Plugin to extend every store.\n */\nexport interface PiniaPlugin {\n  /**\n   * Plugin to extend every store. Returns an object to extend the store or\n   * nothing.\n   *\n   * @param context - Context\n   */\n  (\n    context: PiniaPluginContext\n  ): Partial<PiniaCustomProperties & PiniaCustomStateProperties> | void\n}\n"
  },
  {
    "path": "packages/pinia/src/store.ts",
    "content": "import {\n  watch,\n  computed,\n  inject,\n  hasInjectionContext,\n  getCurrentInstance,\n  reactive,\n  DebuggerEvent,\n  WatchOptions,\n  UnwrapRef,\n  markRaw,\n  isRef,\n  isReactive,\n  effectScope,\n  EffectScope,\n  ComputedRef,\n  toRaw,\n  toRef,\n  toRefs,\n  Ref,\n  ref,\n  nextTick,\n} from 'vue'\nimport {\n  StateTree,\n  SubscriptionCallback,\n  _DeepPartial,\n  isPlainObject,\n  Store,\n  _Method,\n  DefineStoreOptions,\n  StoreDefinition,\n  _GettersTree,\n  MutationType,\n  StoreOnActionListener,\n  _ActionsTree,\n  SubscriptionCallbackMutation,\n  DefineSetupStoreOptions,\n  DefineStoreOptionsInPlugin,\n  StoreGeneric,\n  _StoreWithGetters,\n  _StoreWithGetters_Readonly,\n  _StoreWithGetters_Writable,\n  _ExtractActionsFromSetupStore,\n  _ExtractGettersFromSetupStore,\n  _ExtractStateFromSetupStore,\n  _StoreWithState,\n} from './types'\nimport { setActivePinia, piniaSymbol, Pinia, activePinia } from './rootStore'\nimport { IS_CLIENT } from './env'\nimport { patchObject } from './hmr'\nimport { addSubscription, triggerSubscriptions, noop } from './subscriptions'\n\nconst fallbackRunWithContext = (fn: () => unknown) => fn()\n\ntype _SetType<AT> = AT extends Set<infer T> ? T : never\n\n/**\n * Marks a function as an action for `$onAction`\n * @internal\n */\nconst ACTION_MARKER = Symbol()\n/**\n * Action name symbol. Allows to add a name to an action after defining it\n * @internal\n */\nconst ACTION_NAME = Symbol()\n/**\n * Function type extended with action markers\n * @internal\n */\ninterface MarkedAction<Fn extends _Method = _Method> {\n  (...args: Parameters<Fn>): ReturnType<Fn>\n  [ACTION_MARKER]: boolean\n  [ACTION_NAME]: string\n}\n\nfunction mergeReactiveObjects<\n  T extends Record<any, unknown> | Map<unknown, unknown> | Set<unknown>,\n>(target: T, patchToApply: _DeepPartial<T>): T {\n  // Handle Map instances\n  if (target instanceof Map && patchToApply instanceof Map) {\n    patchToApply.forEach((value, key) => target.set(key, value))\n  } else if (target instanceof Set && patchToApply instanceof Set) {\n    // Handle Set instances\n    patchToApply.forEach(target.add, target)\n  }\n\n  // no need to go through symbols because they cannot be serialized anyway\n  for (const key in patchToApply) {\n    if (!Object.hasOwn(patchToApply, key)) continue\n    const subPatch = patchToApply[key]\n    const targetValue = target[key]\n    if (\n      isPlainObject(targetValue) &&\n      isPlainObject(subPatch) &&\n      Object.hasOwn(target, key) &&\n      !isRef(subPatch) &&\n      !isReactive(subPatch)\n    ) {\n      // NOTE: here I wanted to warn about inconsistent types but it's not possible because in setup stores one might\n      // start the value of a property as a certain type e.g. a Map, and then for some reason, during SSR, change that\n      // to `undefined`. When trying to hydrate, we want to override the Map with `undefined`.\n      target[key] = mergeReactiveObjects(targetValue, subPatch)\n    } else {\n      // @ts-expect-error: subPatch is a valid value\n      target[key] = subPatch\n    }\n  }\n\n  return target\n}\n\nconst skipHydrateSymbol = __DEV__\n  ? Symbol('pinia:skipHydration')\n  : /* istanbul ignore next */ Symbol()\n\n/**\n * Tells Pinia to skip the hydration process of a given object. This is useful in setup stores (only) when you return a\n * stateful object in the store but it isn't really state. e.g. returning a router instance in a setup store.\n *\n * @param obj - target object\n * @returns obj\n */\nexport function skipHydrate<T = any>(obj: T): T {\n  return Object.defineProperty(obj, skipHydrateSymbol, {})\n}\n\n/**\n * Returns whether a value should be hydrated\n *\n * @param obj - target variable\n * @returns true if `obj` should be hydrated\n */\nexport function shouldHydrate(obj: any) {\n  return !isPlainObject(obj) || !Object.hasOwn(obj, skipHydrateSymbol)\n}\n\nconst { assign } = Object\n\nfunction isComputed<T>(value: ComputedRef<T> | unknown): value is ComputedRef<T>\nfunction isComputed(o: any): o is ComputedRef {\n  return !!(isRef(o) && (o as any).effect)\n}\n\nfunction createOptionsStore<\n  Id extends string,\n  S extends StateTree,\n  G extends _GettersTree<S>,\n  A extends _ActionsTree,\n>(\n  id: Id,\n  options: DefineStoreOptions<Id, S, G, A>,\n  pinia: Pinia,\n  hot?: boolean\n): Store<Id, S, G, A> {\n  const { state, actions, getters } = options\n\n  const initialState: StateTree | undefined = pinia.state.value[id]\n\n  let store: Store<Id, S, G, A>\n\n  function setup() {\n    if (!initialState && (!__DEV__ || !hot)) {\n      /* istanbul ignore if */\n      pinia.state.value[id] = state ? state() : {}\n    }\n\n    // avoid creating a state in pinia.state.value\n    const localState =\n      __DEV__ && hot\n        ? // use ref() to unwrap refs inside state TODO: check if this is still necessary\n          toRefs(ref(state ? state() : {}).value)\n        : toRefs(pinia.state.value[id])\n\n    return assign(\n      localState,\n      actions,\n      Object.keys(getters || {}).reduce(\n        (computedGetters, name) => {\n          if (__DEV__ && name in localState) {\n            console.warn(\n              `[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with \"${name}\" in store \"${id}\".`\n            )\n          }\n\n          computedGetters[name] = markRaw(\n            computed(() => {\n              setActivePinia(pinia)\n              // it was created just before\n              const store = pinia._s.get(id)!\n\n              // allow cross using stores\n\n              // @ts-expect-error\n              // return getters![name].call(context, context)\n              // TODO: avoid reading the getter while assigning with a global variable\n              return getters![name].call(store, store)\n            })\n          )\n          return computedGetters\n        },\n        {} as Record<string, ComputedRef>\n      )\n    )\n  }\n\n  store = createSetupStore(id, setup, options, pinia, hot, true)\n\n  return store as any\n}\n\nfunction createSetupStore<\n  Id extends string,\n  SS extends Record<any, unknown>,\n  S extends StateTree,\n  G extends Record<string, _Method>,\n  A extends _ActionsTree,\n>(\n  $id: Id,\n  setup: (helpers: SetupStoreHelpers) => SS,\n  options:\n    | DefineSetupStoreOptions<Id, S, G, A>\n    | DefineStoreOptions<Id, S, G, A> = {},\n  pinia: Pinia,\n  hot?: boolean,\n  isOptionsStore?: boolean\n): Store<Id, S, G, A> {\n  let scope!: EffectScope\n\n  const optionsForPlugin: DefineStoreOptionsInPlugin<Id, S, G, A> = assign(\n    { actions: {} as A },\n    options\n  )\n\n  /* istanbul ignore if */\n  if (__DEV__ && !pinia._e.active) {\n    throw new Error('Pinia destroyed')\n  }\n\n  // watcher options for $subscribe\n  const $subscribeOptions: WatchOptions = { deep: true }\n  /* istanbul ignore else */\n  if (__DEV__) {\n    $subscribeOptions.onTrigger = (event) => {\n      /* istanbul ignore else */\n      if (isListening) {\n        debuggerEvents = event\n        // avoid triggering this while the store is being built and the state is being set in pinia\n      } else if (isListening === false && !store._hotUpdating) {\n        // let patch send all the events together later\n        /* istanbul ignore else */\n        if (Array.isArray(debuggerEvents)) {\n          debuggerEvents.push(event)\n        } else {\n          console.error(\n            '🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug.'\n          )\n        }\n      }\n    }\n  }\n\n  // internal state\n  let isListening: boolean // set to true at the end\n  let isSyncListening: boolean // set to true at the end\n  let subscriptions: Set<SubscriptionCallback<S>> = new Set()\n  let actionSubscriptions: Set<StoreOnActionListener<Id, S, G, A>> = new Set()\n  let debuggerEvents: DebuggerEvent[] | DebuggerEvent\n  const initialState = pinia.state.value[$id] as UnwrapRef<S> | undefined\n\n  // avoid setting the state for option stores if it is set\n  // by the setup\n  if (!isOptionsStore && !initialState && (!__DEV__ || !hot)) {\n    /* istanbul ignore if */\n    pinia.state.value[$id] = {}\n  }\n\n  const hotState = /*#__PURE__*/ ref({} as S)\n\n  // avoid triggering too many listeners\n  // https://github.com/vuejs/pinia/issues/1129\n  let activeListener: symbol | undefined\n  function $patch(stateMutation: (state: UnwrapRef<S>) => void): void\n  function $patch(partialState: _DeepPartial<UnwrapRef<S>>): void\n  function $patch(\n    partialStateOrMutator:\n      | _DeepPartial<UnwrapRef<S>>\n      | ((state: UnwrapRef<S>) => void)\n  ): void {\n    let subscriptionMutation: SubscriptionCallbackMutation<S>\n    isListening = isSyncListening = false\n    // reset the debugger events since patches are sync\n    /* istanbul ignore else */\n    if (__DEV__) {\n      debuggerEvents = []\n    }\n    if (typeof partialStateOrMutator === 'function') {\n      partialStateOrMutator(pinia.state.value[$id] as UnwrapRef<S>)\n      subscriptionMutation = {\n        type: MutationType.patchFunction,\n        storeId: $id,\n        events: debuggerEvents as DebuggerEvent[],\n      }\n    } else {\n      mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator)\n      subscriptionMutation = {\n        type: MutationType.patchObject,\n        payload: partialStateOrMutator,\n        storeId: $id,\n        events: debuggerEvents as DebuggerEvent[],\n      }\n    }\n    const myListenerId = (activeListener = Symbol())\n    nextTick().then(() => {\n      if (activeListener === myListenerId) {\n        isListening = true\n      }\n    })\n    isSyncListening = true\n    // because we paused the watcher, we need to manually call the subscriptions\n    triggerSubscriptions(\n      subscriptions,\n      subscriptionMutation,\n      pinia.state.value[$id] as UnwrapRef<S>\n    )\n  }\n\n  const $reset = isOptionsStore\n    ? function $reset(this: _StoreWithState<Id, S, G, A>) {\n        const { state } = options as DefineStoreOptions<Id, S, G, A>\n        const newState: _DeepPartial<UnwrapRef<S>> = state ? state() : {}\n        // we use a patch to group all changes into one single subscription\n        this.$patch(($state) => {\n          // @ts-expect-error: FIXME: shouldn't error?\n          assign($state, newState)\n        })\n      }\n    : /* istanbul ignore next */\n      __DEV__\n      ? () => {\n          throw new Error(\n            `🍍: Store \"${$id}\" is built using the setup syntax and does not implement $reset().`\n          )\n        }\n      : noop\n\n  function $dispose() {\n    scope.stop()\n    subscriptions.clear()\n    actionSubscriptions.clear()\n    pinia._s.delete($id)\n  }\n\n  /**\n   * Helper that wraps function so it can be tracked with $onAction\n   * @param fn - action to wrap\n   * @param name - name of the action\n   */\n  const action = <Fn extends _Method>(fn: Fn, name: string = ''): Fn => {\n    if (ACTION_MARKER in fn) {\n      // we ensure the name is set from the returned function\n      ;(fn as unknown as MarkedAction<Fn>)[ACTION_NAME] = name\n      return fn\n    }\n\n    const wrappedAction = function (this: any) {\n      setActivePinia(pinia)\n      const args = Array.from(arguments)\n\n      const afterCallbackSet: Set<(resolvedReturn: any) => any> = new Set()\n      const onErrorCallbackSet: Set<(error: unknown) => unknown> = new Set()\n      function after(callback: _SetType<typeof afterCallbackSet>) {\n        afterCallbackSet.add(callback)\n      }\n      function onError(callback: _SetType<typeof onErrorCallbackSet>) {\n        onErrorCallbackSet.add(callback)\n      }\n\n      // @ts-expect-error\n      triggerSubscriptions(actionSubscriptions, {\n        args,\n        name: wrappedAction[ACTION_NAME],\n        store,\n        after,\n        onError,\n      })\n\n      let ret: unknown\n      try {\n        ret = fn.apply(this && this.$id === $id ? this : store, args)\n        // handle sync errors\n      } catch (error) {\n        triggerSubscriptions(onErrorCallbackSet, error)\n        throw error\n      }\n\n      if (ret instanceof Promise) {\n        return ret\n          .then((value) => {\n            triggerSubscriptions(afterCallbackSet, value)\n            return value\n          })\n          .catch((error) => {\n            triggerSubscriptions(onErrorCallbackSet, error)\n            return Promise.reject(error)\n          })\n      }\n\n      // trigger after callbacks\n      triggerSubscriptions(afterCallbackSet, ret)\n      return ret\n    } as MarkedAction<Fn>\n\n    wrappedAction[ACTION_MARKER] = true\n    wrappedAction[ACTION_NAME] = name // will be set later\n\n    // @ts-expect-error: we are intentionally limiting the returned type to just Fn\n    // because all the added properties are internals that are exposed through `$onAction()` only\n    return wrappedAction\n  }\n\n  const _hmrPayload = /*#__PURE__*/ markRaw({\n    actions: {} as Record<string, any>,\n    getters: {} as Record<string, Ref>,\n    state: [] as string[],\n    hotState,\n  })\n\n  const partialStore = {\n    _p: pinia,\n    // _s: scope,\n    $id,\n    $onAction: addSubscription.bind(null, actionSubscriptions),\n    $patch,\n    $reset,\n    $subscribe(callback, options = {}) {\n      const removeSubscription = addSubscription(\n        subscriptions,\n        callback,\n        options.detached,\n        () => stopWatcher()\n      )\n      const stopWatcher = scope.run(() =>\n        watch(\n          () => pinia.state.value[$id] as UnwrapRef<S>,\n          (state) => {\n            if (options.flush === 'sync' ? isSyncListening : isListening) {\n              callback(\n                {\n                  storeId: $id,\n                  type: MutationType.direct,\n                  events: debuggerEvents as DebuggerEvent,\n                },\n                state\n              )\n            }\n          },\n          assign({}, $subscribeOptions, options)\n        )\n      )!\n\n      return removeSubscription\n    },\n    $dispose,\n  } as _StoreWithState<Id, S, G, A>\n\n  const store: Store<Id, S, G, A> = reactive(\n    __DEV__ || (__USE_DEVTOOLS__ && IS_CLIENT)\n      ? assign(\n          {\n            _hmrPayload,\n            _customProperties: markRaw(new Set<string>()), // devtools custom properties\n          },\n          partialStore\n          // must be added later\n          // setupStore\n        )\n      : partialStore\n  ) as unknown as Store<Id, S, G, A>\n\n  // store the partial store now so the setup of stores can instantiate each other before they are finished without\n  // creating infinite loops.\n  pinia._s.set($id, store as Store)\n\n  const runWithContext =\n    (pinia._a && pinia._a.runWithContext) || fallbackRunWithContext\n\n  // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped\n  const setupStore = runWithContext(() =>\n    pinia._e.run(() => (scope = effectScope()).run(() => setup({ action }))!)\n  )!\n\n  // overwrite existing actions to support $onAction\n  for (const key in setupStore) {\n    const prop = setupStore[key]\n\n    if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) {\n      // mark it as a piece of state to be serialized\n      if (__DEV__ && hot) {\n        hotState.value[key] = toRef(setupStore, key)\n        // createOptionStore directly sets the state in pinia.state.value so we\n        // can just skip that\n      } else if (!isOptionsStore) {\n        // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created\n        if (initialState && shouldHydrate(prop)) {\n          if (isRef(prop)) {\n            prop.value = initialState[key as keyof UnwrapRef<S>]\n          } else {\n            // probably a reactive object, lets recursively assign\n            // @ts-expect-error: prop is unknown\n            mergeReactiveObjects(prop, initialState[key])\n          }\n        }\n        // transfer the ref to the pinia state to keep everything in sync\n        pinia.state.value[$id][key] = prop\n      }\n\n      /* istanbul ignore else */\n      if (__DEV__) {\n        _hmrPayload.state.push(key)\n      }\n      // action\n    } else if (typeof prop === 'function') {\n      const actionValue = __DEV__ && hot ? prop : action(prop as _Method, key)\n      // this a hot module replacement store because the hotUpdate method needs\n      // to do it with the right context\n      // @ts-expect-error\n      setupStore[key] = actionValue\n\n      /* istanbul ignore else */\n      if (__DEV__) {\n        _hmrPayload.actions[key] = prop\n      }\n\n      // list actions so they can be used in plugins\n      // @ts-expect-error\n      optionsForPlugin.actions[key] = prop\n    } else if (__DEV__) {\n      // add getters for devtools\n      if (isComputed(prop)) {\n        _hmrPayload.getters[key] = isOptionsStore\n          ? // @ts-expect-error\n            options.getters[key]\n          : prop\n        if (IS_CLIENT) {\n          const getters: string[] =\n            (setupStore._getters as string[]) ||\n            // @ts-expect-error: same\n            ((setupStore._getters = markRaw([])) as string[])\n          getters.push(key)\n        }\n      }\n    }\n  }\n\n  // add the state, getters, and action properties\n  /* istanbul ignore if */\n  assign(store, setupStore)\n  // allows retrieving reactive objects with `storeToRefs()`. Must be called after assigning to the reactive object.\n  // Make `storeToRefs()` work with `reactive()` #799\n  assign(toRaw(store), setupStore)\n\n  // use this instead of a computed with setter to be able to create it anywhere\n  // without linking the computed lifespan to wherever the store is first\n  // created.\n  Object.defineProperty(store, '$state', {\n    get: () => (__DEV__ && hot ? hotState.value : pinia.state.value[$id]),\n    set: (state) => {\n      /* istanbul ignore if */\n      if (__DEV__ && hot) {\n        throw new Error('cannot set hotState')\n      }\n      $patch(($state) => {\n        // @ts-expect-error: FIXME: shouldn't error?\n        assign($state, state)\n      })\n    },\n  })\n\n  // add the hotUpdate before plugins to allow them to override it\n  /* istanbul ignore else */\n  if (__DEV__) {\n    store._hotUpdate = markRaw((newStore) => {\n      store._hotUpdating = true\n      newStore._hmrPayload.state.forEach((stateKey) => {\n        if (stateKey in store.$state) {\n          const newStateTarget = newStore.$state[stateKey]\n          const oldStateSource = store.$state[stateKey as keyof UnwrapRef<S>]\n          if (\n            typeof newStateTarget === 'object' &&\n            isPlainObject(newStateTarget) &&\n            isPlainObject(oldStateSource)\n          ) {\n            patchObject(newStateTarget, oldStateSource)\n          } else {\n            // transfer the ref\n            newStore.$state[stateKey] = oldStateSource\n          }\n        }\n        // patch direct access properties to allow store.stateProperty to work as\n        // store.$state.stateProperty\n        // @ts-expect-error: any type\n        store[stateKey] = toRef(newStore.$state, stateKey)\n      })\n\n      // remove deleted state properties\n      Object.keys(store.$state).forEach((stateKey) => {\n        if (!(stateKey in newStore.$state)) {\n          // @ts-expect-error: noop if doesn't exist\n          delete store[stateKey]\n        }\n      })\n\n      // avoid devtools logging this as a mutation\n      isListening = false\n      isSyncListening = false\n      pinia.state.value[$id] = toRef(newStore._hmrPayload, 'hotState')\n      isSyncListening = true\n      nextTick().then(() => {\n        isListening = true\n      })\n\n      for (const actionName in newStore._hmrPayload.actions) {\n        const actionFn: _Method = newStore[actionName]\n\n        // @ts-expect-error: actionName is a string\n        store[actionName] =\n          //\n          action(actionFn, actionName)\n      }\n\n      // TODO: does this work in both setup and option store?\n      for (const getterName in newStore._hmrPayload.getters) {\n        const getter: _Method = newStore._hmrPayload.getters[getterName]\n        const getterValue = isOptionsStore\n          ? // special handling of options api\n            computed(() => {\n              setActivePinia(pinia)\n              return getter.call(store, store)\n            })\n          : getter\n\n        // @ts-expect-error: getterName is a string\n        store[getterName] =\n          //\n          getterValue\n      }\n\n      // remove deleted getters\n      Object.keys(store._hmrPayload.getters).forEach((key) => {\n        if (!(key in newStore._hmrPayload.getters)) {\n          // @ts-expect-error: noop if doesn't exist\n          delete store[key]\n        }\n      })\n\n      // remove old actions\n      Object.keys(store._hmrPayload.actions).forEach((key) => {\n        if (!(key in newStore._hmrPayload.actions)) {\n          // @ts-expect-error: noop if doesn't exist\n          delete store[key]\n        }\n      })\n\n      // update the values used in devtools and to allow deleting new properties later on\n      store._hmrPayload = newStore._hmrPayload\n      store._getters = newStore._getters\n      store._hotUpdating = false\n    })\n  }\n\n  if (__USE_DEVTOOLS__ && IS_CLIENT) {\n    const nonEnumerable = {\n      writable: true,\n      configurable: true,\n      // avoid warning on devtools trying to display this property\n      enumerable: false,\n    }\n\n    // avoid listing internal properties in devtools\n    ;(['_p', '_hmrPayload', '_getters', '_customProperties'] as const).forEach(\n      (p) => {\n        Object.defineProperty(\n          store,\n          p,\n          assign({ value: store[p] }, nonEnumerable)\n        )\n      }\n    )\n  }\n\n  // apply all plugins\n  pinia._p.forEach((extender) => {\n    /* istanbul ignore else */\n    if (__USE_DEVTOOLS__ && IS_CLIENT) {\n      const extensions = scope.run(() =>\n        extender({\n          store: store as Store,\n          app: pinia._a,\n          pinia,\n          options: optionsForPlugin,\n        })\n      )!\n      Object.keys(extensions || {}).forEach((key) =>\n        store._customProperties.add(key)\n      )\n      assign(store, extensions)\n    } else {\n      assign(\n        store,\n        scope.run(() =>\n          extender({\n            store: store as Store,\n            app: pinia._a,\n            pinia,\n            options: optionsForPlugin,\n          })\n        )!\n      )\n    }\n  })\n\n  if (\n    __DEV__ &&\n    store.$state &&\n    typeof store.$state === 'object' &&\n    typeof store.$state.constructor === 'function' &&\n    !store.$state.constructor.toString().includes('[native code]')\n  ) {\n    console.warn(\n      `[🍍]: The \"state\" must be a plain object. It cannot be\\n` +\n        `\\tstate: () => new MyClass()\\n` +\n        `Found in store \"${store.$id}\".`\n    )\n  }\n\n  // only apply hydrate to option stores with an initial state in pinia\n  if (\n    initialState &&\n    isOptionsStore &&\n    (options as DefineStoreOptions<Id, S, G, A>).hydrate\n  ) {\n    ;(options as DefineStoreOptions<Id, S, G, A>).hydrate!(\n      store.$state,\n      initialState\n    )\n  }\n\n  isListening = true\n  isSyncListening = true\n  return store\n}\n\n/**\n * Extract the actions of a store type. Works with both a Setup Store or an\n * Options Store.\n */\nexport type StoreActions<SS> =\n  SS extends Store<string, StateTree, _GettersTree<StateTree>, infer A>\n    ? A\n    : _ExtractActionsFromSetupStore<SS>\n\n/**\n * Extract the getters of a store type. Works with both a Setup Store or an\n * Options Store.\n */\nexport type StoreGetters<SS> =\n  SS extends Store<string, StateTree, infer G, _ActionsTree>\n    ? _StoreWithGetters<G>\n    : _ExtractGettersFromSetupStore<SS>\n\n/**\n * Extract the state of a store type. Works with both a Setup Store or an\n * Options Store. Note this unwraps refs.\n */\nexport type StoreState<SS> =\n  SS extends Store<string, infer S, _GettersTree<StateTree>, _ActionsTree>\n    ? UnwrapRef<S>\n    : _ExtractStateFromSetupStore<SS>\n\nexport interface SetupStoreHelpers {\n  /**\n   * Helper that wraps function so it can be tracked with $onAction when the\n   * action is called **within the store**. This helper is rarely needed in\n   * applications. It's intended for advanced use cases like Pinia Colada.\n   *\n   * @param fn - action to wrap\n   * @param name - name of the action. Will be picked up by the store at creation\n   */\n  action: <Fn extends _Method>(fn: Fn, name?: string) => Fn\n}\n\n/**\n * Creates a `useStore` function that retrieves the store instance\n *\n * @param id - id of the store (must be unique)\n * @param options - options to define the store\n */\nexport function defineStore<\n  Id extends string,\n  S extends StateTree = {},\n  G extends _GettersTree<S> = {},\n  // cannot extends ActionsTree because we loose the typings\n  A /* extends ActionsTree */ = {},\n>(\n  id: Id,\n  options: Omit<DefineStoreOptions<Id, S, G, A>, 'id'>\n): StoreDefinition<Id, S, G, A>\n\n/**\n * Creates a `useStore` function that retrieves the store instance\n *\n * @param id - id of the store (must be unique)\n * @param storeSetup - function that defines the store\n * @param options - extra options\n */\nexport function defineStore<Id extends string, SS>(\n  id: Id,\n  storeSetup: (helpers: SetupStoreHelpers) => SS,\n  options?: DefineSetupStoreOptions<\n    Id,\n    _ExtractStateFromSetupStore<SS>,\n    _ExtractGettersFromSetupStore<SS>,\n    _ExtractActionsFromSetupStore<SS>\n  >\n): StoreDefinition<\n  Id,\n  _ExtractStateFromSetupStore<SS>,\n  _ExtractGettersFromSetupStore<SS>,\n  _ExtractActionsFromSetupStore<SS>\n>\n// allows unused stores to be tree shaken\n/*! #__NO_SIDE_EFFECTS__ */\nexport function defineStore(\n  // TODO: add proper types from above\n  id: any,\n  setup?: any,\n  setupOptions?: any\n): StoreDefinition {\n  let options:\n    | DefineStoreOptions<\n        string,\n        StateTree,\n        _GettersTree<StateTree>,\n        _ActionsTree\n      >\n    | DefineSetupStoreOptions<\n        string,\n        StateTree,\n        _GettersTree<StateTree>,\n        _ActionsTree\n      >\n\n  const isSetupStore = typeof setup === 'function'\n  // the option store setup will contain the actual options in this case\n  options = isSetupStore ? setupOptions : setup\n\n  function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {\n    const hasContext = hasInjectionContext()\n    pinia =\n      // in test mode, ignore the argument provided as we can always retrieve a\n      // pinia instance with getActivePinia()\n      (__TEST__ && activePinia && activePinia._testing ? null : pinia) ||\n      (hasContext ? inject(piniaSymbol, null) : null)\n    if (pinia) setActivePinia(pinia)\n\n    if (__DEV__ && !activePinia) {\n      throw new Error(\n        `[🍍]: \"getActivePinia()\" was called but there was no active Pinia. Are you trying to use a store before calling \"app.use(pinia)\"?\\n` +\n          `See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.\\n` +\n          `This will fail in production.`\n      )\n    }\n\n    pinia = activePinia!\n\n    if (!pinia._s.has(id)) {\n      // creating the store registers it in `pinia._s`\n      if (isSetupStore) {\n        createSetupStore(id, setup, options, pinia)\n      } else {\n        createOptionsStore(id, options as any, pinia)\n      }\n\n      /* istanbul ignore else */\n      if (__DEV__) {\n        // @ts-expect-error: not the right inferred type\n        useStore._pinia = pinia\n      }\n    }\n\n    const store: StoreGeneric = pinia._s.get(id)!\n\n    if (__DEV__ && hot) {\n      const hotId = '__hot:' + id\n      const newStore = isSetupStore\n        ? createSetupStore(hotId, setup, options, pinia, true)\n        : createOptionsStore(hotId, assign({}, options) as any, pinia, true)\n\n      hot._hotUpdate(newStore)\n\n      // cleanup the state properties and the store from the cache\n      delete pinia.state.value[hotId]\n      pinia._s.delete(hotId)\n    }\n\n    if (__DEV__ && IS_CLIENT) {\n      const currentInstance = getCurrentInstance()\n      // save stores in instances to access them devtools\n      if (\n        currentInstance &&\n        currentInstance.proxy &&\n        // avoid adding stores that are just built for hot module replacement\n        !hot\n      ) {\n        const vm = currentInstance.proxy\n        const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})\n        cache[id] = store\n      }\n    }\n\n    // StoreGeneric cannot be casted towards Store\n    return store as any\n  }\n\n  useStore.$id = id\n\n  return useStore\n}\n\n/**\n * Return type of `defineStore()` with a setup function.\n * - `Id` is a string literal of the store's name\n * - `SS` is the return type of the setup function\n * @see {@link StoreDefinition}\n */\nexport interface SetupStoreDefinition<\n  Id extends string,\n  SS,\n> extends StoreDefinition<\n  Id,\n  _ExtractStateFromSetupStore<SS>,\n  _ExtractGettersFromSetupStore<SS>,\n  _ExtractActionsFromSetupStore<SS>\n> {}\n"
  },
  {
    "path": "packages/pinia/src/storeToRefs.ts",
    "content": "import {\n  computed,\n  ComputedRef,\n  isReactive,\n  isRef,\n  toRaw,\n  ToRef,\n  toRef,\n  ToRefs,\n  WritableComputedRef,\n} from 'vue'\nimport { StoreGetters, StoreState } from './store'\nimport type {\n  _ActionsTree,\n  _GettersTree,\n  _UnwrapAll,\n  PiniaCustomStateProperties,\n  StateTree,\n  Store,\n  StoreGeneric,\n} from './types'\n\n/**\n * Internal utility type\n */\ntype _IfEquals<X, Y, A = true, B = false> =\n  (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B\n\n/**\n * Internal utility type\n */\ntype _IsReadonly<T, K extends keyof T> = _IfEquals<\n  { [P in K]: T[P] },\n  { -readonly [P in K]: T[P] },\n  false, // Property is not readonly if they are the same\n  true // Property is readonly if they differ\n>\n\n/**\n * Extracts the getters of a store while keeping writable and readonly properties. **Internal type DO NOT USE**.\n */\ntype _ToComputedRefs<SS> = {\n  [K in keyof SS]: true extends _IsReadonly<SS, K>\n    ? ComputedRef<SS[K]>\n    : WritableComputedRef<SS[K]>\n}\n\n/**\n * Extracts the refs of a state object from a store. If the state value is a Ref or type that extends ref, it will be kept as is.\n * Otherwise, it will be converted into a Ref. **Internal type DO NOT USE**.\n */\ntype _ToStateRefs<SS> =\n  SS extends Store<\n    string,\n    infer UnwrappedState,\n    _GettersTree<StateTree>,\n    _ActionsTree\n  >\n    ? UnwrappedState extends _UnwrapAll<Pick<infer State, infer Key>>\n      ? {\n          [K in Key]: ToRef<State[K]>\n        }\n      : ToRefs<UnwrappedState>\n    : ToRefs<StoreState<SS>>\n\n/**\n * Extracts the return type for `storeToRefs`.\n * Will convert any `getters` into `ComputedRef`.\n */\nexport type StoreToRefs<SS extends StoreGeneric> =\n  // NOTE: always trues but the conditional makes the type distributive\n  // https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types\n  SS extends unknown\n    ? _ToStateRefs<SS> &\n        ToRefs<PiniaCustomStateProperties<StoreState<SS>>> &\n        _ToComputedRefs<StoreGetters<SS>>\n    : never\n\n/**\n * Creates an object of references with all the state, getters, and plugin-added\n * state properties of the store. Similar to `toRefs()` but specifically\n * designed for Pinia stores so methods and non reactive properties are\n * completely ignored.\n *\n * @param store - store to extract the refs from\n */\nexport function storeToRefs<SS extends StoreGeneric>(\n  store: SS\n): StoreToRefs<SS> {\n  const rawStore = toRaw(store)\n\n  const refs = {} as StoreToRefs<SS>\n  for (const key in rawStore) {\n    const value = rawStore[key]\n    // There is no native method to check for a computed\n    // https://github.com/vuejs/core/pull/4165\n    if (value.effect) {\n      // @ts-expect-error: too hard to type correctly\n      refs[key] =\n        // ...\n        computed({\n          get: () => store[key],\n          set(value) {\n            store[key] = value\n          },\n        })\n    } else if (isRef(value) || isReactive(value)) {\n      // @ts-expect-error: the key is state or getter\n      refs[key] =\n        // ---\n        toRef(store, key)\n    }\n  }\n\n  return refs\n}\n"
  },
  {
    "path": "packages/pinia/src/subscriptions.ts",
    "content": "import { getCurrentScope, onScopeDispose } from 'vue'\nimport { _Method } from './types'\n\nexport const noop = () => {}\n\nexport function addSubscription<T extends _Method>(\n  subscriptions: Set<T>,\n  callback: T,\n  detached?: boolean,\n  onCleanup: () => void = noop\n) {\n  subscriptions.add(callback)\n\n  const removeSubscription = () => {\n    const isDel = subscriptions.delete(callback)\n    isDel && onCleanup()\n  }\n\n  if (!detached && getCurrentScope()) {\n    onScopeDispose(removeSubscription)\n  }\n\n  return removeSubscription\n}\n\nexport function triggerSubscriptions<T extends _Method>(\n  subscriptions: Set<T>,\n  ...args: Parameters<T>\n) {\n  subscriptions.forEach((callback) => {\n    callback(...args)\n  })\n}\n"
  },
  {
    "path": "packages/pinia/src/types.ts",
    "content": "import type {\n  ComputedRef,\n  DebuggerEvent,\n  Ref,\n  UnwrapRef,\n  WatchOptions,\n  WritableComputedRef,\n} from 'vue'\nimport { Pinia } from './rootStore'\n\n/**\n * Generic state of a Store\n */\nexport type StateTree = Record<PropertyKey, any>\n\nexport function isPlainObject<S extends StateTree>(\n  value: S | unknown\n): value is S\nexport function isPlainObject(\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  o: any\n): o is StateTree {\n  return (\n    o &&\n    typeof o === 'object' &&\n    Object.prototype.toString.call(o) === '[object Object]' &&\n    typeof o.toJSON !== 'function'\n  )\n}\n\n/**\n * Recursive `Partial<T>`. Used by {@link Store['$patch']}.\n *\n * For internal use **only**\n */\nexport type _DeepPartial<T> = { [K in keyof T]?: _DeepPartial<T[K]> }\n// type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> }\n\n// TODO: can we change these to numbers?\n/**\n * Possible types for SubscriptionCallback\n */\nexport enum MutationType {\n  /**\n   * Direct mutation of the state:\n   *\n   * - `store.name = 'new name'`\n   * - `store.$state.name = 'new name'`\n   * - `store.list.push('new item')`\n   */\n  direct = 'direct',\n\n  /**\n   * Mutated the state with `$patch` and an object\n   *\n   * - `store.$patch({ name: 'newName' })`\n   */\n  patchObject = 'patch object',\n\n  /**\n   * Mutated the state with `$patch` and a function\n   *\n   * - `store.$patch(state => state.name = 'newName')`\n   */\n  patchFunction = 'patch function',\n\n  // maybe reset? for $state = {} and $reset\n}\n\n/**\n * Base type for the context passed to a subscription callback. Internal type.\n */\nexport interface _SubscriptionCallbackMutationBase {\n  /**\n   * Type of the mutation.\n   */\n  type: MutationType\n\n  /**\n   * `id` of the store doing the mutation.\n   */\n  storeId: string\n\n  /**\n   * 🔴 DEV ONLY, DO NOT use for production code. Different mutation calls. Comes from\n   * https://vuejs.org/guide/extras/reactivity-in-depth.html#reactivity-debugging and allows to track mutations in\n   * devtools and plugins **during development only**.\n   */\n  events?: DebuggerEvent[] | DebuggerEvent\n}\n\n/**\n * Context passed to a subscription callback when directly mutating the state of\n * a store with `store.someState = newValue` or `store.$state.someState =\n * newValue`.\n */\nexport interface SubscriptionCallbackMutationDirect extends _SubscriptionCallbackMutationBase {\n  type: MutationType.direct\n\n  events: DebuggerEvent\n}\n\n/**\n * Context passed to a subscription callback when `store.$patch()` is called\n * with an object.\n */\nexport interface SubscriptionCallbackMutationPatchObject<\n  S,\n> extends _SubscriptionCallbackMutationBase {\n  type: MutationType.patchObject\n\n  events: DebuggerEvent[]\n\n  /**\n   * Object passed to `store.$patch()`.\n   */\n  payload: _DeepPartial<UnwrapRef<S>>\n}\n\n/**\n * Context passed to a subscription callback when `store.$patch()` is called\n * with a function.\n */\nexport interface SubscriptionCallbackMutationPatchFunction extends _SubscriptionCallbackMutationBase {\n  type: MutationType.patchFunction\n\n  events: DebuggerEvent[]\n\n  /**\n   * Object passed to `store.$patch()`.\n   */\n  // payload: DeepPartial<UnwrapRef<S>>\n}\n\n/**\n * Context object passed to a subscription callback.\n */\nexport type SubscriptionCallbackMutation<S> =\n  | SubscriptionCallbackMutationDirect\n  | SubscriptionCallbackMutationPatchObject<S>\n  | SubscriptionCallbackMutationPatchFunction\n\n/**\n * Callback of a subscription\n */\nexport type SubscriptionCallback<S> = (\n  /**\n   * Object with information relative to the store mutation that triggered the\n   * subscription.\n   */\n  mutation: SubscriptionCallbackMutation<S>,\n\n  /**\n   * State of the store when the subscription is triggered. Same as\n   * `store.$state`.\n   */\n  state: UnwrapRef<S>\n) => void\n\n/**\n * Actual type for {@link StoreOnActionListenerContext}. Exists for refactoring\n * purposes. For internal use only.\n * For internal use **only**\n */\nexport interface _StoreOnActionListenerContext<\n  Store,\n  ActionName extends string,\n  A,\n> {\n  /**\n   * Name of the action\n   */\n  name: ActionName\n\n  /**\n   * Store that is invoking the action\n   */\n  store: Store\n\n  /**\n   * Parameters passed to the action\n   */\n  args: A extends Record<ActionName, _Method>\n    ? Parameters<A[ActionName]>\n    : unknown[]\n\n  /**\n   * Sets up a hook once the action is finished. It receives the return value\n   * of the action, if it's a Promise, it will be unwrapped.\n   */\n  after: (\n    callback: A extends Record<ActionName, _Method>\n      ? (resolvedReturn: Awaited<ReturnType<A[ActionName]>>) => void\n      : () => void\n  ) => void\n\n  /**\n   * Sets up a hook if the action fails. Return `false` to catch the error and\n   * stop it from propagating.\n   */\n  onError: (callback: (error: unknown) => void) => void\n}\n\n/**\n * Context object passed to callbacks of `store.$onAction(context => {})`\n * TODO: should have only the Id, the Store and Actions to generate the proper object\n */\nexport type StoreOnActionListenerContext<\n  Id extends string,\n  S extends StateTree,\n  G /* extends GettersTree<S> */,\n  A /* extends ActionsTree */,\n> = _ActionsTree extends A\n  ? _StoreOnActionListenerContext<StoreGeneric, string, _ActionsTree>\n  : {\n      [Name in keyof A]: Name extends string\n        ? _StoreOnActionListenerContext<Store<Id, S, G, A>, Name, A>\n        : never\n    }[keyof A]\n\n/**\n * Argument of `store.$onAction()`\n */\nexport type StoreOnActionListener<\n  Id extends string,\n  S extends StateTree,\n  G /* extends GettersTree<S> */,\n  A /* extends ActionsTree */,\n> = (\n  context: StoreOnActionListenerContext<\n    Id,\n    S,\n    G,\n    // {} creates a type of never due to how StoreOnActionListenerContext is defined\n    {} extends A ? _ActionsTree : A\n  >\n) => void\n\n/**\n * Properties of a store.\n */\nexport interface StoreProperties<Id extends string> {\n  /**\n   * Unique identifier of the store\n   */\n  $id: Id\n\n  /**\n   * Private property defining the pinia the store is attached to.\n   *\n   * @internal\n   */\n  _p: Pinia\n\n  /**\n   * Used by devtools plugin to retrieve getters. Removed in production.\n   *\n   * @internal\n   */\n  _getters?: string[]\n\n  /**\n   * Used (and added) by devtools plugin to detect Setup vs Options API usage.\n   *\n   * @internal\n   */\n  _isOptionsAPI?: boolean\n\n  /**\n   * Used by devtools plugin to retrieve properties added with plugins. Removed\n   * in production. Can be used by the user to add property keys of the store\n   * that should be displayed in devtools.\n   */\n  _customProperties: Set<string>\n\n  /**\n   * Handles a HMR replacement of this store. Dev Only.\n   *\n   * @internal\n   */\n  _hotUpdate(useStore: StoreGeneric): void\n\n  /**\n   * Allows pausing some of the watching mechanisms while the store is being\n   * patched with a newer version.\n   *\n   * @internal\n   */\n  _hotUpdating: boolean\n\n  /**\n   * Payload of the hmr update. Dev only.\n   *\n   * @internal\n   */\n  _hmrPayload: {\n    state: string[]\n    hotState: Ref<StateTree>\n    actions: _ActionsTree\n    getters: _ActionsTree\n  }\n}\n\n/**\n * Base store with state and functions. Should not be used directly.\n */\nexport interface _StoreWithState<\n  Id extends string,\n  S extends StateTree,\n  G /* extends GettersTree<StateTree> */,\n  A /* extends ActionsTree */,\n> extends StoreProperties<Id> {\n  /**\n   * State of the Store. Setting it will internally call `$patch()` to update the state.\n   */\n  $state: UnwrapRef<S> & PiniaCustomStateProperties<S>\n\n  /**\n   * Applies a state patch to current state. Allows passing nested values\n   *\n   * @param partialState - patch to apply to the state\n   */\n  $patch(partialState: _DeepPartial<UnwrapRef<S>>): void\n\n  /**\n   * Group multiple changes into one function. Useful when mutating objects like\n   * Sets or arrays and applying an object patch isn't practical, e.g. appending\n   * to an array. The function passed to `$patch()` **must be synchronous**.\n   *\n   * @param stateMutator - function that mutates `state`, cannot be asynchronous\n   */\n  $patch<F extends (state: UnwrapRef<S>) => any>(\n    // this prevents the user from using `async` which isn't allowed\n    stateMutator: ReturnType<F> extends Promise<any> ? never : F\n  ): void\n\n  /**\n   * Resets the store to its initial state by building a new state object.\n   */\n  $reset(): void\n\n  /**\n   * Setups a callback to be called whenever the state changes. It also returns a function to remove the callback. Note\n   * that when calling `store.$subscribe()` inside of a component, it will be automatically cleaned up when the\n   * component gets unmounted unless `detached` is set to true.\n   *\n   * @param callback - callback passed to the watcher\n   * @param options - `watch` options + `detached` to detach the subscription from the context (usually a component)\n   * this is called from. Note that the `flush` option does not affect calls to `store.$patch()`.\n   * @returns function that removes the watcher\n   */\n  $subscribe(\n    callback: SubscriptionCallback<S>,\n    options?: { detached?: boolean } & WatchOptions\n  ): () => void\n\n  /**\n   * Setups a callback to be called every time an action is about to get\n   * invoked. The callback receives an object with all the relevant information\n   * of the invoked action:\n   * - `store`: the store it is invoked on\n   * - `name`: The name of the action\n   * - `args`: The parameters passed to the action\n   *\n   * On top of these, it receives two functions that allow setting up a callback\n   * once the action finishes or when it fails.\n   *\n   * It also returns a function to remove the callback. Note that when calling\n   * `store.$onAction()` inside of a component, it will be automatically cleaned\n   * up when the component gets unmounted unless `detached` is set to true.\n   *\n   * @example\n   *\n   *```js\n   *store.$onAction(({ after, onError }) => {\n   *  // Here you could share variables between all of the hooks as well as\n   *  // setting up watchers and clean them up\n   *  after((resolvedValue) => {\n   *    // can be used to cleanup side effects\n   * .  // `resolvedValue` is the value returned by the action, if it's a\n   * .  // Promise, it will be the resolved value instead of the Promise\n   *  })\n   *  onError((error) => {\n   *    // can be used to pass up errors\n   *  })\n   *})\n   *```\n   *\n   * @param callback - callback called before every action\n   * @param detached - detach the subscription from the context this is called from\n   * @returns function that removes the watcher\n   */\n  $onAction(\n    callback: StoreOnActionListener<Id, S, G, A>,\n    detached?: boolean\n  ): () => void\n\n  /**\n   * Stops the associated effect scope of the store and remove it from the store\n   * registry. Plugins can override this method to cleanup any added effects.\n   * e.g. devtools plugin stops displaying disposed stores from devtools.\n   * Note this doesn't delete the state of the store, you have to do it manually with\n   * `delete pinia.state.value[store.$id]` if you want to. If you don't and the\n   * store is used again, it will reuse the previous state.\n   */\n  $dispose(): void\n}\n\n/**\n * Generic type for a function that can infer arguments and return type\n *\n * For internal use **only**\n */\nexport type _Method = (...args: any[]) => any\n\n// export type StoreAction<P extends any[], R> = (...args: P) => R\n// export interface StoreAction<P, R> {\n//   (...args: P[]): R\n// }\n\n// in this type we forget about this because otherwise the type is recursive\n/**\n * Store augmented for actions. For internal usage only.\n * For internal use **only**\n */\nexport type _StoreWithActions<A> = {\n  [k in keyof A]: A[k] extends (...args: infer P) => infer R\n    ? (...args: P) => R\n    : never\n}\n\n/**\n * Store augmented with getters. For internal usage only.\n * For internal use **only**\n */\nexport type _StoreWithGetters<G> = _StoreWithGetters_Readonly<G> &\n  _StoreWithGetters_Writable<G>\n\n/**\n * Store augmented with readonly getters. For internal usage **only**.\n */\nexport type _StoreWithGetters_Readonly<G> = {\n  readonly [K in keyof G as G[K] extends (...args: any[]) => any\n    ? K\n    : ComputedRef extends G[K]\n      ? K\n      : never]: G[K] extends (...args: any[]) => infer R ? R : UnwrapRef<G[K]>\n}\n\n/**\n * Store augmented with writable getters. For internal usage **only**.\n */\nexport type _StoreWithGetters_Writable<G> = {\n  [K in keyof G as G[K] extends WritableComputedRef<any>\n    ? K\n    : // NOTE: there is still no way to have a different type for a setter and a getter in TS with dynamic keys\n      // https://github.com/microsoft/TypeScript/issues/43826\n      never]: G[K] extends Readonly<WritableComputedRef<infer R>> ? R : never\n}\n\n/**\n * Store type to build a store.\n */\nexport type Store<\n  Id extends string = string,\n  S extends StateTree = {},\n  G /* extends GettersTree<S>*/ = {},\n  // has the actions without the context (this) for typings\n  A /* extends ActionsTree */ = {},\n> = _StoreWithState<Id, S, G, A> &\n  UnwrapRef<S> &\n  _StoreWithGetters<G> &\n  // StoreWithActions<A> &\n  (_ActionsTree extends A ? {} : A) &\n  PiniaCustomProperties<Id, S, G, A> &\n  PiniaCustomStateProperties<S>\n\n/**\n * Generic and type-unsafe version of Store. Doesn't fail on access with\n * strings, making it much easier to write generic functions that do not care\n * about the kind of store that is passed.\n */\nexport type StoreGeneric = Store<\n  string,\n  StateTree,\n  _GettersTree<StateTree>,\n  _ActionsTree\n>\n\n/**\n * Return type of `defineStore()`. Function that allows instantiating a store.\n */\nexport interface StoreDefinition<\n  Id extends string = string,\n  S extends StateTree = StateTree,\n  G /* extends GettersTree<S>*/ = _GettersTree<S>,\n  A /* extends ActionsTree */ = _ActionsTree,\n> {\n  /**\n   * Returns a store, creates it if necessary.\n   *\n   * @param pinia - Pinia instance to retrieve the store\n   * @param hot - dev only hot module replacement\n   */\n  (pinia?: Pinia | null | undefined, hot?: StoreGeneric): Store<Id, S, G, A>\n\n  /**\n   * Id of the store. Used by map helpers.\n   */\n  $id: Id\n\n  /**\n   * Dev only pinia for HMR.\n   *\n   * @internal\n   */\n  _pinia?: Pinia\n}\n\n/**\n * Interface to be extended by the user when they add properties through plugins.\n */\nexport interface PiniaCustomProperties<\n  Id extends string = string,\n  S extends StateTree = StateTree,\n  G /* extends GettersTree<S> */ = _GettersTree<S>,\n  A /* extends ActionsTree */ = _ActionsTree,\n> {}\n\n/**\n * Properties that are added to every `store.$state` by `pinia.use()`.\n */\nexport interface PiniaCustomStateProperties<S extends StateTree = StateTree> {}\n\n/**\n * Type of an object of Getters that infers the argument. For internal usage only.\n * For internal use **only**\n */\nexport type _GettersTree<S extends StateTree> = Record<\n  string,\n  | ((state: UnwrapRef<S> & UnwrapRef<PiniaCustomStateProperties<S>>) => any)\n  | (() => any)\n>\n\n/**\n * Type of an object of Actions. For internal usage only.\n * For internal use **only**\n */\nexport type _ActionsTree = Record<string, _Method>\n\n/**\n * Type that enables refactoring through IDE.\n * For internal use **only**\n */\nexport type _ExtractStateFromSetupStore_Keys<SS> = keyof {\n  [K in keyof SS as SS[K] extends _Method | ComputedRef ? never : K]: any\n}\n\n/**\n * Type that enables refactoring through IDE.\n * For internal use **only**\n */\nexport type _ExtractActionsFromSetupStore_Keys<SS> = keyof {\n  [K in keyof SS as SS[K] extends _Method ? K : never]: any\n}\n\n/**\n * Type that enables refactoring through IDE.\n * For internal use **only**\n */\nexport type _ExtractGettersFromSetupStore_Keys<SS> = keyof {\n  [K in keyof SS as SS[K] extends ComputedRef ? K : never]: any\n}\n\n/**\n * Type that enables refactoring through IDE.\n * For internal use **only**\n */\nexport type _UnwrapAll<SS> = { [K in keyof SS]: UnwrapRef<SS[K]> }\n\n/**\n * For internal use **only**\n */\nexport type _ExtractStateFromSetupStore<SS> = SS extends undefined | void\n  ? {}\n  : Pick<SS, _ExtractStateFromSetupStore_Keys<SS>>\n\n/**\n * For internal use **only**\n */\nexport type _ExtractActionsFromSetupStore<SS> = SS extends undefined | void\n  ? {}\n  : Pick<SS, _ExtractActionsFromSetupStore_Keys<SS>>\n\n/**\n * For internal use **only**\n */\nexport type _ExtractGettersFromSetupStore<SS> = SS extends undefined | void\n  ? {}\n  : Pick<SS, _ExtractGettersFromSetupStore_Keys<SS>>\n\n/**\n * Options passed to `defineStore()` that are common between option and setup\n * stores. Extend this interface if you want to add custom options to both kinds\n * of stores.\n */\nexport interface DefineStoreOptionsBase<S extends StateTree, Store> {}\n\n/**\n * Options parameter of `defineStore()` for option stores. Can be extended to\n * augment stores with the plugin API. @see {@link DefineStoreOptionsBase}.\n */\nexport interface DefineStoreOptions<\n  Id extends string,\n  S extends StateTree,\n  G /* extends GettersTree<S> */,\n  A /* extends Record<string, StoreAction> */,\n> extends DefineStoreOptionsBase<S, Store<Id, S, G, A>> {\n  /**\n   * Unique string key to identify the store across the application.\n   */\n  id: Id\n\n  /**\n   * Function to create a fresh state. **Must be an arrow function** to ensure\n   * correct typings!\n   */\n  state?: () => S\n\n  /**\n   * Optional object of getters.\n   */\n  getters?: G &\n    ThisType<UnwrapRef<S> & _StoreWithGetters<G> & PiniaCustomProperties> &\n    _GettersTree<S>\n\n  /**\n   * Optional object of actions.\n   */\n  actions?: A &\n    ThisType<\n      A &\n        UnwrapRef<S> &\n        _StoreWithState<Id, S, G, A> &\n        _StoreWithGetters<G> &\n        PiniaCustomProperties\n    >\n\n  /**\n   * Allows hydrating the store during SSR when complex state (like client side only refs) are used in the store\n   * definition and copying the value from `pinia.state` isn't enough.\n   *\n   * @example\n   * If in your `state`, you use any `customRef`s, any `computed`s, or any `ref`s that have a different value on\n   * Server and Client, you need to manually hydrate them. e.g., a custom ref that is stored in the local\n   * storage:\n   *\n   * ```ts\n   * const useStore = defineStore('main', {\n   *   state: () => ({\n   *     n: useLocalStorage('key', 0)\n   *   }),\n   *   hydrate(storeState, initialState) {\n   *     // @ts-expect-error: https://github.com/microsoft/TypeScript/issues/43826\n   *     storeState.n = useLocalStorage('key', 0)\n   *   }\n   * })\n   * ```\n   *\n   * @param storeState - the current state in the store\n   * @param initialState - initialState\n   */\n  hydrate?(storeState: UnwrapRef<S>, initialState: UnwrapRef<S>): void\n}\n\n/**\n * Options parameter of `defineStore()` for setup stores. Can be extended to\n * augment stores with the plugin API. @see {@link DefineStoreOptionsBase}.\n */\nexport interface DefineSetupStoreOptions<\n  Id extends string,\n  // NOTE: Passing SS seems to make TS crash\n  S extends StateTree,\n  G,\n  A /* extends ActionsTree */,\n> extends DefineStoreOptionsBase<S, Store<Id, S, G, A>> {\n  /**\n   * Extracted actions. Added by useStore(). SHOULD NOT be added by the user when\n   * creating the store. Can be used in plugins to get the list of actions in a\n   * store defined with a setup function. Note this is always defined\n   */\n  actions?: A\n}\n\n/**\n * Available `options` when creating a pinia plugin.\n */\nexport interface DefineStoreOptionsInPlugin<\n  Id extends string,\n  S extends StateTree,\n  G,\n  A,\n> extends Omit<DefineStoreOptions<Id, S, G, A>, 'id' | 'actions'> {\n  /**\n   * Extracted object of actions. Added by useStore() when the store is built\n   * using the setup API, otherwise uses the one passed to `defineStore()`.\n   * Defaults to an empty object if no actions are defined.\n   */\n  actions: A\n}\n\n/**\n * Utility type. For internal use **only**\n */\nexport interface _Empty {}\n\n/**\n * Merges type objects for better readability in the code.\n * Utility type. For internal use **only**\n */\nexport type _Simplify<T> = _Empty extends T\n  ? _Empty\n  : { [key in keyof T]: T[key] } & {}\n"
  },
  {
    "path": "packages/pinia/test-dts/actions.test-d.ts",
    "content": "import { defineStore, expectType } from './'\n\nconst useStore = defineStore('name', {\n  state: () => ({ count: 0 }),\n  actions: {\n    useOtherAction() {\n      return this.returnStuff()\n    },\n    useExternalFunction() {\n      return outer(this as any)\n    },\n    returnStuff() {\n      this.useOtherAction()\n      return this.count * 2\n    },\n    // return type is necessary\n    factorial(n?: number): number {\n      n ??= this.count\n      return n > 1 ? this.factorial(n - 1) * n : 1\n    },\n    crossA(): 'A' | 'B' {\n      if (this.count < 3) {\n        return 'A'\n      } else {\n        return this.crossB()\n      }\n    },\n    crossB() {\n      if (this.count > 2) {\n        return this.crossA()\n      } else {\n        return 'B'\n      }\n    },\n  },\n})\n\nfunction outer(store: ReturnType<typeof useStore>): number {\n  return store.count * 2\n}\n\nconst store = useStore()\n\nexpectType<'A' | 'B'>(store.crossA())\nexpectType<'A' | 'B'>(store.crossB())\nexpectType<number>(store.factorial())\nexpectType<number>(store.returnStuff())\nexpectType<number>(store.useExternalFunction())\nexpectType<number>(store.useOtherAction())\nexpectType<number>(outer(store))\n"
  },
  {
    "path": "packages/pinia/test-dts/customizations.test-d.ts",
    "content": "import {\n  expectType,\n  createPinia,\n  defineStore,\n  mapStores,\n  type _ActionsTree,\n  type StoreActions,\n  storeToRefs,\n} from './'\nimport { App, computed, ComputedRef, ref, Ref } from 'vue'\n\ndeclare module '../dist/pinia' {\n  export interface MapStoresCustomization {\n    suffix: 'Store'\n  }\n\n  export interface PiniaCustomProperties<Id, S, G, A> {\n    $actions: Array<keyof A>\n    myState: number\n\n    set canBeARef(value: number | Ref<number>)\n    get canBeARef(): number\n  }\n\n  export interface PiniaCustomStateProperties<S> {\n    myState: number\n    stateOnly: number\n  }\n\n  export interface DefineStoreOptionsBase<S, Store> {\n    debounce?: Partial<Record<keyof StoreActions<Store>, number>>\n  }\n\n  // export interface DefineStoreOptions<Id, S, G, A> {\n  //   debounce?: Partial<Record<keyof A, number>>\n  // }\n}\n\nconst pinia = createPinia()\n\npinia.use((context) => {\n  expectType<_ActionsTree>(context.options.actions)\n  expectType<string>(context.store.$id)\n  expectType<App>(context.app)\n\n  expectType<number>(context.store.$state.myState)\n  expectType<number>(context.store.myState)\n\n  expectType<number>(context.store.canBeARef)\n  // it can be set to both a ref and a number\n  context.store.canBeARef = ref(2)\n  context.store.canBeARef = 3\n  // @ts-expect-error\n  context.store.canBeARef = 'eou'\n\n  return {\n    $actions: Object.keys(context.options.actions || {}),\n  }\n})\n\nconst useStore = defineStore('main', {\n  actions: {\n    one() {},\n    two() {\n      this.one()\n      expectType<number>(this.$state.myState)\n      expectType<number>(this.myState)\n    },\n    three() {\n      this.two()\n    },\n  },\n\n  getters: {\n    two(state): boolean {\n      expectType<number>(this.myState)\n      expectType<number>(state.myState)\n      expectType<number>(state.stateOnly)\n\n      // @ts-expect-error\n      this.stateOnly\n\n      return true\n    },\n  },\n\n  debounce: {\n    one: 200,\n    two: 300,\n    // three: 100\n  },\n})\n\ndefineStore(\n  'withSetup',\n  () => {\n    function one() {}\n    function two() {}\n    function three() {}\n\n    return { one, two, three }\n  },\n  {\n    debounce: {\n      one: 200,\n      two: 300,\n    },\n  }\n)\n\ntype Procedure = (...args: any[]) => any\n\nfunction debounce<F extends Procedure>(fn: F, time: number = 200) {\n  return fn\n}\n\nexpectType<{\n  mainStore: () => ReturnType<typeof useStore>\n}>(mapStores(useStore))\n\npinia.use(({ options, store }) => {\n  const { debounce: debounceOptions } = options\n  if (debounceOptions) {\n    return Object.keys(debounceOptions).reduce(\n      (debouncedActions, action) => {\n        debouncedActions[action] = debounce(\n          store[action],\n          debounceOptions[action]\n        )\n        return debouncedActions\n      },\n      {} as Record<string, (...args: any[]) => any>\n    )\n  }\n})\n\nexpectType<{ myState: Ref<number>; stateOnly: Ref<number> }>(\n  storeToRefs(defineStore('a', {})())\n)\n\nexpectType<{\n  a: Ref<boolean>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  // @ts-expect-error: no a\n  storeToRefs(defineStore('a', {})())\n)\n\nexpectType<{\n  $onAction: Ref<unknown>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  // @ts-expect-error: doesn't add store methods\n  storeToRefs(defineStore('a', {})())\n)\n\nexpectType<{ a: Ref<boolean>; myState: Ref<number>; stateOnly: Ref<number> }>(\n  storeToRefs(defineStore('a', { state: () => ({ a: true }) })())\n)\n\nexpectType<{\n  n: Ref<number>\n  double: ComputedRef<number>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  storeToRefs(\n    defineStore('a', {\n      state: () => ({ n: 1 }),\n      getters: {\n        double: (state) => state.n * 2,\n      },\n    })()\n  )\n)\n\nexpectType<{\n  n: Ref<number>\n  double: ComputedRef<number>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  storeToRefs(\n    defineStore('a', () => {\n      const n = ref(1)\n      const double = computed(() => n.value * 2)\n      return {\n        n,\n        double,\n      }\n    })()\n  )\n)\n\nexpectType<{\n  n: Ref<number>\n  customN: Ref<number> & { plusOne: () => void }\n  double: ComputedRef<number>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  storeToRefs(\n    defineStore('a', () => {\n      const n = ref(1)\n      const customN = ref(1) as Ref<number> & { plusOne: () => void }\n      const double = computed(() => n.value * 2)\n      return {\n        n,\n        customN,\n        double,\n      }\n    })()\n  )\n)\n\nexpectType<{\n  n: Ref<number>\n  customN: Ref<number> & { plusOne: () => void }\n  double: ComputedRef<number>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  storeToRefs(\n    defineStore('a', () => {\n      const n = ref(1)\n      const customN = ref(1) as Ref<number> & { plusOne: () => void }\n      const double = computed(() => n.value * 2)\n\n      function plusOne() {\n        customN.value++\n      }\n\n      return {\n        n,\n        customN,\n        double,\n        plusOne,\n      }\n    })()\n  )\n)\n\nexpectType<{\n  n: Ref<number>\n  customN: Ref<number> & { plusOne: () => void }\n  double: ComputedRef<number>\n  myState: Ref<number>\n  stateOnly: Ref<number>\n}>(\n  storeToRefs(\n    defineStore('a', {\n      state: () => ({\n        n: 1,\n        customN: ref(1) as Ref<number> & { plusOne: () => void },\n      }),\n      getters: {\n        double: (state) => state.n * 2,\n      },\n      actions: {\n        plusOne() {\n          this.n++\n        },\n      },\n    })()\n  )\n)\n"
  },
  {
    "path": "packages/pinia/test-dts/index.d.ts",
    "content": "export * from '../dist/pinia'\n// export * from '../src'\n\nexport type TypeEqual<Target, Value> =\n  (<T>() => T extends Target ? 1 : 2) extends <T>() => T extends Value ? 1 : 2\n    ? true\n    : false\nexport function describe(_name: string, _fn: () => void): void\nexport function expectType<T>(value: T): void\nexport function expectError<T>(value: T): void\nexport function expectAssignable<T, T2 extends T = T>(value: T2): void\n"
  },
  {
    "path": "packages/pinia/test-dts/mapHelpers.test-d.ts",
    "content": "import { computed, ref } from 'vue'\nimport {\n  defineStore,\n  mapStores,\n  mapActions,\n  mapState,\n  mapWritableState,\n} from './'\nimport { describe, it, expectTypeOf } from 'vitest'\n\ndescribe('mapHelpers', () => {\n  const useOptionsStore = defineStore('name', {\n    state: () => ({ a: 'on' as 'on' | 'off', nested: { counter: 0 } }),\n    getters: {\n      upper: (state) => state.a.toUpperCase(),\n    },\n    actions: {\n      toggleA() {\n        this.a = this.a === 'off' ? 'on' : 'off'\n      },\n\n      setToggle(a: 'on' | 'off') {\n        return (this.a = a)\n      },\n    },\n  })\n\n  const useSetupStore = defineStore('setupStore', () => {\n    const a = ref('on' as 'on' | 'off')\n    const upper = computed(() => a.value.toUpperCase())\n    const writableUpper = computed({\n      get: () => a.value.toUpperCase(),\n      set: (v: 'on' | 'off') => (a.value = v),\n    })\n    function toggleA() {\n      a.value = a.value === 'off' ? 'on' : 'off'\n    }\n    function setToggle(aVal: 'on' | 'off') {\n      return (a.value = aVal)\n    }\n    return { a, upper, writableUpper, toggleA, setToggle }\n  })\n\n  const useCounter = defineStore('counter', { state: () => ({ n: 0 }) })\n\n  const useStoreDos = defineStore('dos', { state: () => ({}) })\n\n  type MainStore = ReturnType<typeof useOptionsStore>\n  type DosStore = ReturnType<typeof useStoreDos>\n  type CounterStore = ReturnType<typeof useCounter>\n\n  describe('mapStores', () => {\n    it('should map stores correctly', () => {\n      const computedStores = mapStores(useOptionsStore, useStoreDos, useCounter)\n      expectTypeOf<{\n        nameStore: () => MainStore\n        dosStore: () => DosStore\n        counterStore: () => CounterStore\n      }>(computedStores)\n    })\n  })\n\n  describe('mapState', () => {\n    it('should map state correctly for store with no getters', () => {\n      expectTypeOf<{\n        n: () => number\n      }>(mapState(useCounter, ['n']))\n    })\n\n    it('should map state correctly for store with getters', () => {\n      expectTypeOf<{\n        a: () => 'on' | 'off'\n        upper: () => string\n      }>(mapState(useOptionsStore, ['a', 'upper']))\n    })\n\n    it('should map state with new keys', () => {\n      expectTypeOf<{\n        newA: () => 'on' | 'off'\n        newUpper: () => string\n      }>(mapState(useOptionsStore, { newA: 'a', newUpper: 'upper' }))\n    })\n\n    it('should map state with function keys', () => {\n      expectTypeOf<{\n        newA: () => 'on' | 'off'\n        newUpper: () => string\n      }>(\n        mapState(useOptionsStore, {\n          newA: (store) => {\n            expectTypeOf<string>(store.upper)\n            return store.a\n          },\n          newUpper: 'upper',\n        })\n      )\n    })\n\n    it('should map state for setup store', () => {\n      const setupStoreWithState = mapState(useSetupStore, ['a'])\n      expectTypeOf(setupStoreWithState).toEqualTypeOf<{\n        a: () => 'on' | 'off'\n      }>()\n\n      const setupStoreWithGetters = mapState(useSetupStore, ['a', 'upper'])\n      expectTypeOf(setupStoreWithGetters).toEqualTypeOf<{\n        a: () => 'on' | 'off'\n        upper: () => string\n      }>()\n    })\n  })\n\n  describe('mapActions', () => {\n    it('should map actions correctly', () => {\n      expectTypeOf<{\n        setToggle: (a: 'on' | 'off') => 'on' | 'off'\n        toggleA: () => void\n      }>(mapActions(useOptionsStore, ['setToggle', 'toggleA']))\n\n      expectTypeOf<{\n        newSetToggle: (a: 'on' | 'off') => 'on' | 'off'\n        newToggleA: () => void\n      }>(\n        mapActions(useOptionsStore, {\n          newSetToggle: 'setToggle',\n          newToggleA: 'toggleA',\n        })\n      )\n    })\n  })\n\n  describe('mapWritableState', () => {\n    it('should map writable state correctly', () => {\n      expectTypeOf<{\n        a: {\n          get: () => 'on' | 'off'\n          set: (v: 'on' | 'off') => any\n        }\n      }>(mapWritableState(useOptionsStore, ['a']))\n\n      expectTypeOf<{\n        newA: {\n          get: () => 'on' | 'off'\n          set: (v: 'on' | 'off') => any\n        }\n      }>(mapWritableState(useOptionsStore, { newA: 'a' }))\n\n      expectTypeOf<{\n        foo: {\n          get: () => 'on' | 'off'\n          set: (v: 'on' | 'off') => any\n        }\n      }>(mapWritableState(useSetupStore, { foo: 'a' }))\n\n      expectTypeOf<{\n        a: {\n          get: () => 'on' | 'off'\n          set: (v: 'on' | 'off') => any\n        }\n      }>(mapWritableState(useSetupStore, ['a']))\n\n      expectTypeOf<{\n        writableUpper: {\n          get: () => string\n          set: (v: 'on' | 'off') => any\n        }\n      }>(mapWritableState(useSetupStore, ['writableUpper']))\n    })\n  })\n})\n"
  },
  {
    "path": "packages/pinia/test-dts/onAction.test-d.ts",
    "content": "import { defineStore, expectType } from './'\n\nconst useStore = defineStore('main', {\n  state: () => ({\n    user: 'Eduardo',\n  }),\n  actions: {\n    direct(name: string) {\n      this.user = name\n    },\n    patchObject(user: string) {\n      this.$patch({ user })\n    },\n    patchFn(name: string) {\n      this.$patch((state) => {\n        state.user = name\n      })\n    },\n    async asyncUpperName() {\n      return this.user.toUpperCase()\n    },\n    upperName() {\n      return this.user.toUpperCase()\n    },\n    throws(e: any) {\n      throw e\n    },\n    async rejects(e: any) {\n      throw e\n    },\n  },\n})\n\nlet store = useStore()\n\nstore.$onAction((context) => {\n  expectType<{ user: string }>(context.store.$state)\n  expectType<(name: string) => void>(context.store.direct)\n\n  if (context.name === 'upperName') {\n    expectType<[]>(context.args)\n    context.after((ret) => {\n      expectType<string>(ret)\n    })\n  } else if (context.name === 'asyncUpperName') {\n    context.after((ret) => {\n      expectType<string>(ret)\n    })\n  } else if (context.name === 'throws') {\n    context.after((ret) => {\n      expectType<never>(ret)\n    })\n    expectType<[any]>(context.args)\n  } else if (context.name === 'direct') {\n    expectType<[string]>(context.args)\n  }\n})\n"
  },
  {
    "path": "packages/pinia/test-dts/plugins.test-d.ts",
    "content": "import { App } from 'vue'\nimport {\n  expectType,\n  createPinia,\n  StoreGeneric,\n  Pinia,\n  StateTree,\n  DefineStoreOptionsInPlugin,\n} from './'\n\nconst pinia = createPinia()\n\npinia.use(({ store, app, options, pinia }) => {\n  expectType<StoreGeneric>(store)\n  expectType<Pinia>(pinia)\n  expectType<App>(app)\n  expectType<\n    DefineStoreOptionsInPlugin<\n      string,\n      StateTree,\n      Record<string, any>,\n      Record<string, any>\n    >\n  >(options)\n})\n"
  },
  {
    "path": "packages/pinia/test-dts/state.test-d.ts",
    "content": "import { computed, type Ref, ref, shallowRef } from 'vue'\nimport { defineStore, expectType } from './'\n\nconst name = ref('Eduardo')\nconst counter = ref(0)\nconst double = computed({\n  get: () => counter.value * 2,\n  set(val) {\n    counter.value = val / 2\n  },\n})\nconst nestedRef = ref({ a: ref(0) })\n\nconst useStore = defineStore('name', {\n  state: () => ({\n    n: 0,\n    name,\n    double,\n    counter,\n    aRef: ref(0),\n    aShallowRef: shallowRef({ msg: 'hi' }),\n    anotherShallowRef: shallowRef({ aRef: ref('hello') }),\n    nestedRef,\n  }),\n\n  getters: {\n    myDouble: (state) => {\n      expectType<number>(state.double)\n      expectType<number>(state.counter)\n      expectType<number>(state.nestedRef.a)\n      return state.n * 2\n    },\n    other(): undefined {\n      expectType<number>(this.double)\n      expectType<number>(this.counter)\n      return undefined\n    },\n\n    fromARef: (state) => state.aRef,\n  },\n\n  actions: {\n    some() {\n      expectType<number>(this.$state.double)\n      expectType<number>(this.$state.counter)\n      expectType<number>(this.double)\n      expectType<number>(this.counter)\n\n      this.$patch({ counter: 2 })\n      this.$patch((state) => {\n        expectType<number>(state.counter)\n      })\n    },\n  },\n})\n\nconst store = useStore()\n\nstore.$patch({ counter: 2 })\nstore.$patch((state) => {\n  expectType<number>(state.counter)\n})\n\nexpectType<number>(store.$state.counter)\nexpectType<number>(store.$state.double)\n\nexpectType<number>(store.aRef)\nexpectType<number>(store.$state.aRef)\nexpectType<number>(store.fromARef)\n\nexpectType<{ msg: string }>(store.aShallowRef)\nexpectType<{ msg: string }>(store.$state.aShallowRef)\nexpectType<{ aRef: Ref<string> }>(store.anotherShallowRef)\nexpectType<{ aRef: Ref<string> }>(store.$state.anotherShallowRef)\n\nconst onlyState = defineStore('main', {\n  state: () => ({\n    // counter: 0,\n    // TODO: having only name fails...\n    name: 'hey',\n    some: 'hello',\n  }),\n})()\n\nonlyState.$patch({ some: 'other' })\nonlyState.$patch((state) => {\n  expectType<string>(state.some)\n  expectType<string>(state.name)\n})\n\nconst useSetupStore = defineStore('composition', () => ({\n  anotherShallowRef: shallowRef({ aRef: ref('hello') }),\n}))\n\nconst setupStore = useSetupStore()\nexpectType<{ aRef: Ref<string> }>(setupStore.anotherShallowRef)\nexpectType<{ aRef: Ref<string> }>(setupStore.$state.anotherShallowRef)\n"
  },
  {
    "path": "packages/pinia/test-dts/store.test-d.ts",
    "content": "import {\n  StoreGeneric,\n  acceptHMRUpdate,\n  defineStore,\n  expectType,\n  storeToRefs,\n} from './'\nimport { computed, Ref, ref, UnwrapRef, watch, WritableComputedRef } from 'vue'\n\nconst useStore = defineStore('name', {\n  state: () => ({ a: 'on' as 'on' | 'off', nested: { counter: 0 } }),\n  getters: {\n    upper: (state) => {\n      expectType<'on' | 'off'>(state.a)\n      return state.a.toUpperCase() as 'ON' | 'OFF'\n    },\n    upperThis(): 'ON' | 'OFF' {\n      expectType<'on' | 'off'>(this.a)\n      return this.a.toUpperCase() as 'ON' | 'OFF'\n    },\n    other(): false {\n      expectType<string>(this.upper)\n      return false\n    },\n\n    doubleCounter: (state) => {\n      expectType<number>(state.nested.counter)\n      return state.nested.counter * 2\n    },\n  },\n  actions: {\n    doStuff() {\n      // @ts-expect-error\n      this.notExisting\n      expectType<string>(this.upper)\n      expectType<false>(this.other)\n    },\n    otherOne() {\n      expectType<() => void>(this.doStuff)\n    },\n  },\n})\n\ndefineStore('name', {})\n// @ts-expect-error\ndefineStore('name')\ndefineStore('name', {\n  state: () => ({}),\n})\n\n// actions on not existing properties\ndefineStore('', {\n  actions: {\n    a() {\n      // @ts-expect-error\n      this.notExisting\n    },\n  },\n})\n\ndefineStore('', {\n  state: () => ({}),\n  actions: {\n    a() {\n      // @ts-expect-error\n      this.notExisting\n    },\n  },\n})\n\ndefineStore('', {\n  getters: {},\n  actions: {\n    a() {\n      // @ts-expect-error\n      this.notExisting\n    },\n  },\n})\n\ninterface Model {\n  id: number\n}\n\n// Define generic factory function\nexport function init<User extends Model>(name = 'settings') {\n  return defineStore(name, {\n    state: () => {\n      return {\n        // Set one of the properties to the generic type\n        user: {} as User,\n      }\n    },\n    actions: {\n      // Add action which accepts argument with our generic type\n      set(u: UnwrapRef<User>) {\n        // See linter error when trying to assign arg value to the state\n        this.user = u\n      },\n    },\n  })\n}\n\nconst s = init()()\ns.set({ id: 1 })\n\n// getters on not existing properties\ndefineStore('', {\n  getters: {\n    a(): number {\n      // @ts-expect-error\n      this.notExisting\n      return 2\n    },\n    b: (state) => {\n      // @ts-expect-error\n      state.notExisting\n      return\n    },\n  },\n})\n\ndefineStore('', {\n  state: () => ({}),\n  getters: {\n    a(): number {\n      // @ts-expect-error\n      this.notExisting\n      return 2\n    },\n    b: (state) => {\n      // @ts-expect-error\n      state.notExisting\n      return\n    },\n  },\n})\n\nconst store = useStore()\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useStore, import.meta.hot))\n}\n\nexpectType<{ a: 'on' | 'off' }>(store.$state)\nexpectType<number>(store.nested.counter)\nexpectType<'on' | 'off'>(store.a)\nexpectType<'ON' | 'OFF'>(store.upper)\n\n// @ts-expect-error\nstore.nonExistant\n\n// @ts-expect-error\nstore.upper = 'thing'\n\n// @ts-expect-error\nstore.nonExistant.stuff\n\n// @ts-expect-error cannot return a value\nstore.$patch(async () => {})\nstore.$patch(() => {})\nstore.$patch(() => {\n  // return earlier\n  return\n})\n\nconst useNoSAG = defineStore('noSAG', {})\nconst useNoAG = defineStore('noAG', { state: () => ({}) })\nconst useNoSG = defineStore('noAG', { actions: {} })\nconst useNoSA = defineStore('noAG', { getters: {} })\nconst useNoS = defineStore('noAG', { actions: {}, getters: {} })\nconst useNoA = defineStore('noAG', { state: () => ({}), getters: {} })\nconst useNoG = defineStore('noAG', { state: () => ({}), actions: {} })\n\nconst noSAG = useNoSAG()\nconst noSA = useNoSA()\nconst noAG = useNoAG()\nconst noSG = useNoSG()\nconst noS = useNoS()\nconst noA = useNoA()\nconst noG = useNoG()\n\n// @ts-expect-error\nstore.notExisting\n\n// @ts-expect-error\nnoSAG.notExisting\n// @ts-expect-error\nnoSAG.$state.hey\n\n// @ts-expect-error\nnoSA.notExisting\n// @ts-expect-error\nnoSA.notExisting\n// @ts-expect-error\nnoAG.notExisting\n// @ts-expect-error\nnoSG.notExisting\n// @ts-expect-error\nnoS.notExisting\n// @ts-expect-error\nnoA.notExisting\n// @ts-expect-error\nnoG.notExisting\n\nfunction takeStore<TStore extends StoreGeneric>(store: TStore): TStore['$id'] {\n  return store.$id\n}\n\nexport const useSyncValueToStore = <\n  TStore extends StoreGeneric,\n  TKey extends keyof TStore['$state'],\n>(\n  propGetter: () => TStore[TKey],\n  store: TStore,\n  key: TKey\n): void => {\n  watch(\n    propGetter,\n    (propValue) => {\n      store[key] = propValue\n    },\n    {\n      immediate: true,\n    }\n  )\n}\n\nuseSyncValueToStore(() => 'on' as const, store, 'a')\n// @ts-expect-error\nuseSyncValueToStore(() => true, store, 'a')\ntakeStore(store)\ntakeStore(noSAG)\n// @ts-expect-error\nuseSyncValueToStore(() => 2, noSAG, 'nope')\n// @ts-expect-error\nuseSyncValueToStore(() => null, noSAG, 'myState')\ntakeStore(noSA)\ntakeStore(noAG)\nuseSyncValueToStore(() => 2, noAG, 'myState')\ntakeStore(noSG)\ntakeStore(noS)\ntakeStore(noA)\nuseSyncValueToStore(() => 2, noA, 'myState')\ntakeStore(noG)\nuseSyncValueToStore(() => 2, noG, 'myState')\n\ndeclare var genericStore: StoreGeneric\n\n// should not fail like it does with Store\nexpectType<any>(genericStore.thing)\nexpectType<any>(genericStore.$state.thing)\ntakeStore(genericStore)\nuseSyncValueToStore(() => 2, genericStore, 'myState')\n// @ts-expect-error: this type is known so it should yield an error\nuseSyncValueToStore(() => false, genericStore, 'myState')\nuseSyncValueToStore(() => 2, genericStore, 'random')\n\nconst writableComputedStore = defineStore('computed-writable', () => {\n  const fruitsBasket = ref(['banana', 'apple', 'banana', 'orange'])\n  const total = computed(() => fruitsBasket.value.length)\n  const bananasAmount = computed<number>({\n    get: () => fruitsBasket.value.filter((fruit) => fruit === 'banana').length,\n    set: (newAmount) => {\n      fruitsBasket.value = fruitsBasket.value.filter(\n        (fruit) => fruit !== 'banana'\n      )\n      fruitsBasket.value.push(...Array(newAmount).fill('banana'))\n    },\n  })\n  const bananas = computed({\n    get: () => fruitsBasket.value.filter((fruit) => fruit === 'banana'),\n    set: (newFruit: string) =>\n      (fruitsBasket.value = fruitsBasket.value.map((fruit) =>\n        fruit === 'banana' ? newFruit : fruit\n      )),\n  })\n  bananas.value = 'hello' // TS ok\n  return { fruitsBasket, bananas, bananasAmount, total }\n})()\n\nexpectType<number>(writableComputedStore.bananasAmount)\n// should allow writing to it\nwritableComputedStore.bananasAmount = 0\n// @ts-expect-error: this one is readonly\nwritableComputedStore.total = 0\nexpectType<string[]>(writableComputedStore.bananas)\n// should allow setting a different type\n// @ts-expect-error: still not doable\nwritableComputedStore.bananas = 'hello'\n\nconst refs = storeToRefs(writableComputedStore)\nexpectType<string[]>(refs.bananas.value)\nexpectType<number>(refs.bananasAmount.value)\nrefs.bananasAmount.value = 0\n// @ts-expect-error: this one is readonly\nrefs.total.value = 0\n\nconst refStore = defineStore('ref-bananas', () => {\n  const bananas = ref(['banana1', 'banana2'])\n  return { bananas }\n})()\ndeclare const conditionalStore: typeof refStore | typeof writableComputedStore\nexpectType<Ref<string[]> | WritableComputedRef<'banana'[]>>(\n  storeToRefs(conditionalStore).bananas\n)\n"
  },
  {
    "path": "packages/pinia/test-dts/storeSetup.test-d.ts",
    "content": "import { computed, ref } from 'vue'\nimport { defineStore, expectType } from './'\n\nconst useSetupStore = defineStore('name', () => {\n  const count = ref(0)\n  const double = computed(() => count.value * 2)\n  const triple = computed({\n    get: () => count.value * 3,\n    set: (tripled) => {\n      count.value = Math.round(tripled / 3)\n    },\n  })\n\n  const multiply = computed(() => (n: number) => {\n    return count.value * n\n  })\n\n  function increment(amount = 1) {\n    count.value += amount\n\n    return count.value\n  }\n\n  return { count, double, increment, triple, multiply }\n})\n\nconst setupStore = useSetupStore()\nexpectType<'name'>(setupStore.$id)\nexpectType<number>(setupStore.count)\nexpectType<number>(setupStore.$state.count)\nexpectType<number>(setupStore.double)\nexpectType<number>(setupStore.triple)\nexpectType<(n: number) => number>(setupStore.multiply)\nexpectType<(amount?: number) => number>(setupStore.increment)\n"
  },
  {
    "path": "packages/pinia/test-dts/storeToRefs.test-d.ts",
    "content": "import { expectType, defineStore, storeToRefs } from './'\nimport { computed, ComputedRef, ref, Ref, shallowRef } from 'vue'\n\nconst useOptionsStore = defineStore('main', {\n  state: () => ({\n    n: 0,\n    ref: ref({\n      n: 0,\n      ref: ref(0),\n    }),\n    shallowRef: shallowRef({\n      n: 0,\n      ref: ref(0),\n    }),\n  }),\n})\n\nconst optionsStore = useOptionsStore()\nconst optionsRefs = storeToRefs(optionsStore)\n\nexpectType<Ref<number>>(optionsRefs.n)\nexpectType<Ref<{ n: number; ref: number }>>(optionsRefs.ref)\nexpectType<Ref<{ n: number; ref: Ref<number> }>>(optionsRefs.shallowRef)\n\nconst useSetupStore = defineStore('main', () => {\n  return {\n    n: ref(0),\n    ref: ref({\n      n: 0,\n      ref: ref(0),\n    }),\n    shallowRef: shallowRef({\n      n: 0,\n      ref: ref(0),\n    }),\n    // https://github.com/vuejs/pinia/issues/2658\n    accidentallyNestedComputed: computed(() => computed(() => 'a')),\n    computedRef: computed(() => ref(0)),\n  }\n})\n\nconst setupStore = useSetupStore()\nconst setupRefs = storeToRefs(setupStore)\n\nexpectType<Ref<number>>(setupRefs.n)\nexpectType<Ref<{ n: number; ref: number }>>(setupRefs.ref)\nexpectType<Ref<{ n: number; ref: Ref<number> }>>(setupRefs.shallowRef)\nexpectType<ComputedRef<string>>(setupRefs.accidentallyNestedComputed.value)\nexpectType<Ref<number>>(setupRefs.computedRef.value)\n"
  },
  {
    "path": "packages/pinia/test-dts/tsconfig.json",
    "content": "{\n  \"extends\": \"../../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": true,\n    \"declaration\": true,\n    \"noImplicitReturns\": false\n  },\n  \"include\": [\"./\"],\n  \"exclude\": [\"../__tests__\", \"../src\"]\n}\n"
  },
  {
    "path": "packages/pinia/test-dts/typeHelpers.test-d.ts",
    "content": "import { ComputedRef, Ref, computed, ref } from 'vue'\nimport {\n  StoreDefinition,\n  SetupStoreDefinition,\n  StoreState,\n  StoreGetters,\n  StoreActions,\n  defineStore,\n  expectType,\n} from './'\n\nconst useSetupStore = defineStore('main', () => {\n  const n = ref(0)\n\n  const double = computed(() => n.value * 2)\n\n  function increment(amount = 1) {\n    n.value += amount\n  }\n\n  return { n, increment, double }\n})\n\nconst useOptionsStore = defineStore('main', {\n  state: () => ({ n: 0 }),\n  getters: {\n    double: (state) => state.n * 2,\n  },\n  actions: {\n    increment(amount = 1) {\n      this.n += amount\n    },\n  },\n})\n\ndeclare function storeActions<T extends StoreDefinition>(\n  useStore: T\n): StoreActions<ReturnType<T>>\n\ndeclare function storeState<T extends StoreDefinition>(\n  useStore: T\n): StoreState<ReturnType<T>>\n\ndeclare function storeGetters<T extends StoreDefinition>(\n  useStore: T\n): StoreGetters<ReturnType<T>>\n\nexpectType<{\n  increment: (amount?: number) => void\n}>(storeActions(useSetupStore))\n\nexpectType<{ n: number }>(storeState(useSetupStore))\n\nexpectType<{ double: number }>(storeGetters(useSetupStore))\n\nexpectType<{\n  increment: (amount?: number) => void\n}>(storeActions(useOptionsStore))\n\nexpectType<{ n: number }>(storeState(useOptionsStore))\n\nexpectType<{ double: number }>(storeGetters(useOptionsStore))\n\nexpectType<\n  SetupStoreDefinition<\n    'a',\n    {\n      n: Ref<number>\n      double: ComputedRef<number>\n      increment: () => void\n    }\n  >\n>(\n  defineStore('a', () => {\n    const n = ref(0)\n    const double = computed(() => n.value * 2)\n    function increment() {\n      n.value++\n    }\n    return {\n      double,\n      increment,\n      n,\n    }\n  })\n)\n"
  },
  {
    "path": "packages/pinia/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown'\nimport pkg from './package.json' with { type: 'json' }\n\nconst banner = `\n/*!\n * ${pkg.name} v${pkg.version}\n * (c) ${new Date().getFullYear()} Eduardo San Martin Morote\n * @license MIT\n */\n`.trim()\n\nconst __DEV__ = `(process.env.NODE_ENV !== 'production')`\nconst __TEST__ = `(process.env.NODE_ENV === 'test')`\n\nconst commonOptions = defineConfig({\n  banner,\n  format: ['esm'],\n  skipNodeModulesBundle: true,\n  entry: {\n    pinia: './src/index.ts',\n  },\n  define: {\n    __DEV__,\n    __TEST__,\n    // __VUE_PROD_DEVTOOLS__ is replaced by the vite vue plugin\n    __USE_DEVTOOLS__: `((${__DEV__} || __VUE_PROD_DEVTOOLS__) && !${__TEST__})`,\n  },\n  dts: false,\n})\n\nconst esm = defineConfig({\n  ...commonOptions,\n  platform: 'neutral',\n  exports: true,\n  dts: true,\n  outputOptions: {\n    entryFileNames: ({ name }) => `${name}.mjs`.replace('.d.mjs', '.d.ts'),\n  },\n})\n\nconst esmBrowser = defineConfig({\n  ...commonOptions,\n  outputOptions: {\n    entryFileNames: '[name].esm-browser.js',\n  },\n  define: {\n    ...commonOptions.define,\n    __DEV__: 'true',\n    __TEST__: 'false',\n    __USE_DEVTOOLS__: 'true',\n  },\n})\n\nconst esmBrowserProd = defineConfig({\n  ...esmBrowser,\n  target: 'es2015',\n  minify: true,\n  outputOptions: {\n    entryFileNames: '[name].esm-browser.prod.js',\n  },\n  define: {\n    ...esmBrowser.define,\n    __DEV__: 'false',\n    __USE_DEVTOOLS__: 'false',\n  },\n})\n\nconst iife = defineConfig({\n  ...commonOptions,\n  format: 'iife',\n  outputOptions: {\n    name: 'Pinia',\n    globals: {\n      vue: 'Vue',\n      '@vue/devtools-api': 'VueDevToolsApi',\n    },\n  },\n  define: {\n    ...commonOptions.define,\n    __DEV__: 'true',\n    __TEST__: 'false',\n    __USE_DEVTOOLS__: 'false',\n  },\n})\n\nconst iifeProd = defineConfig({\n  ...iife,\n  target: 'es2015',\n  minify: true,\n  outputOptions: {\n    ...iife.outputOptions,\n    entryFileNames: '[name].iife.prod.js',\n  },\n  define: {\n    ...iife.define,\n    __DEV__: 'false',\n    __USE_DEVTOOLS__: 'false',\n  },\n})\n\nexport default [\n  //\n  esm,\n  esmBrowser,\n  esmBrowserProd,\n  iife,\n  iifeProd,\n]\n"
  },
  {
    "path": "packages/playground/.gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "packages/playground/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>🍍 Pinia playground</title>\n\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/@exampledev/new.css@1/new.min.css\"\n    />\n    <link rel=\"stylesheet\" href=\"https://fonts.xz.style/serve/inter.css\" />\n    <style>\n      @keyframes spinner {\n        to {\n          transform: rotate(360deg);\n        }\n      }\n\n      .spinner:before {\n        content: '';\n        box-sizing: border-box;\n        position: absolute;\n        top: 50%;\n        left: 50%;\n        width: 30px;\n        height: 30px;\n        margin-top: -15px;\n        margin-left: -15px;\n        border-radius: 50%;\n        border: 1px solid #ccc;\n        border-top-color: #07d;\n        animation: spinner 0.6s linear infinite;\n      }\n    </style>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/playground/package.json",
    "content": "{\n  \"name\": \"@pinia/private-playground\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"play\": \"vite\",\n    \"play:build\": \"vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@vueuse/core\": \"^14.0.0\",\n    \"mande\": \"^2.0.9\",\n    \"pinia\": \"workspace:*\",\n    \"swrv\": \"^1.1.0\",\n    \"vue-promised\": \"^2.2.0\",\n    \"vue-router\": \"^4.6.3\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^6.0.4\",\n    \"vite\": \"^7.3.1\",\n    \"vite-plugin-vue-devtools\": \"^8.0.5\"\n  }\n}\n"
  },
  {
    "path": "packages/playground/src/App.vue",
    "content": "<template>\n  <header>\n    <h1>🍍 Pinia playground</h1>\n    <nav>\n      <template v-for=\"(page, i) in pages\" :key=\"page.name\">\n        <router-link :to=\"page\" v-slot=\"{ route }\">{{\n          route.fullPath\n        }}</router-link>\n        <template v-if=\"i < pages.length - 1\"> · </template>\n      </template>\n    </nav>\n  </header>\n\n  <section>\n    <router-view />\n  </section>\n\n  <footer>\n    <p>\n      ©2021 Eduardo San Martin Morote\n      <br />\n      <a :href=\"sourceCodeLink\">Source Code</a>\n    </p>\n  </footer>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, provide } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\n\nconst router = useRouter()\nconst route = useRoute()\n\nprovide('hello', 'from component')\n\nconst pages = router\n  .getRoutes()\n  .filter((route) => !route.meta.hide)\n  .map((route) => ({ name: route.name }))\n\nconst sourceCodeLink = computed(() => {\n  if (route.name) {\n    return `https://github.com/vuejs/pinia/blob/v3/packages/playground/src/views/${String(\n      route.name\n    )}.vue`\n  } else {\n    return `https://github.com/vuejs/pinia/blob/v3/packages/playground/src/`\n  }\n})\n</script>\n\n<style>\nbutton {\n  margin-right: 0.5rem;\n  margin-bottom: 0.5rem;\n}\n</style>\n"
  },
  {
    "path": "packages/playground/src/api/jokes.ts",
    "content": "import { mande } from 'mande'\n\nexport const jokes = mande('https://official-joke-api.appspot.com', {\n  headers: {\n    'Content-Type': null,\n  },\n})\n\nexport interface Joke {\n  id: number\n  type: string\n  setup: string\n  punchline: string\n}\n\nexport function getRandomJoke() {\n  return jokes.get<Joke>('/jokes/random')\n}\n"
  },
  {
    "path": "packages/playground/src/api/nasa.ts",
    "content": "import { mande } from 'mande'\n\n/**\n * Go to https://api.nasa.gov/ and generate a key. Put it in your `.env` file\n * next to the `package.json` file:\n *\n * VITE_API_KEY_NASA=<your_key>\n */\nconst API_KEY = import.meta.env.VITE_API_KEY_NASA || 'DEMO_KEY'\n\nconst nasaPlanetary = mande('https://api.nasa.gov/planetary', {\n  query: { api_key: API_KEY, thumbs: true },\n})\n\nexport interface NASAPOD {\n  copyright: string\n  date: string\n  explanation: string\n  hdurl: string\n  media_type: 'image' | 'video'\n  title: string\n  url: string\n}\n\nexport function getNASAPOD(date: Date | string = new Date()) {\n  if (typeof date !== 'string') {\n    date = date.toISOString().slice(0, 10)\n  }\n\n  return nasaPlanetary.get<NASAPOD>('/apod', { query: { date } })\n}\n"
  },
  {
    "path": "packages/playground/src/composables/useCachedRequest.ts",
    "content": "import { unref, ref, watchEffect, Ref, onScopeDispose } from 'vue'\n\nexport function useCachedRequest<T, U>(\n  keySource: Ref<U>,\n  getter: (key: U) => Promise<T>\n) {\n  const data = ref<T>()\n  const isLoading = ref(false)\n  const isReady = ref(false)\n  const error = ref<Error | undefined>()\n\n  const cache = new Map<U, T>()\n\n  onScopeDispose(() => {\n    cache.clear()\n  })\n\n  watchEffect(async () => {\n    const key = unref(keySource)\n    isReady.value = false\n    isLoading.value = true\n\n    if (cache.has(key)) {\n      data.value = cache.get(key)!\n      isReady.value = true\n    }\n\n    getter(key)\n      .then((newData) => {\n        cache.set(key, newData)\n        data.value = newData\n        isReady.value = true\n      })\n      .catch((err) => {\n        error.value = err\n      })\n      .finally(() => {\n        isLoading.value = false\n      })\n  })\n\n  return { data, error, isLoading, isReady }\n}\n"
  },
  {
    "path": "packages/playground/src/main.ts",
    "content": "import { computed, createApp, markRaw, Ref } from 'vue'\nimport App from './App.vue'\nimport { createPinia } from 'pinia'\nimport { router } from './router'\nimport {\n  RouteLocationNormalized,\n  RouteLocationNormalizedLoaded,\n} from 'vue-router'\n\n// for local development\nwindow.__USE_DEVTOOLS__ = true\n\nconst pinia = createPinia()\n\ndeclare module 'pinia' {\n  export interface PiniaCustomProperties {\n    set route(\n      value: RouteLocationNormalizedLoaded | Ref<RouteLocationNormalizedLoaded>\n    )\n    get route(): RouteLocationNormalized\n  }\n}\n\npinia.use(() => ({\n  route: computed(() => markRaw(router.currentRoute.value)),\n}))\n\nif (import.meta.hot) {\n  //   const isUseStore = (fn: any): fn is StoreDefinition => {\n  //     return typeof fn === 'function' && typeof fn.$id === 'string'\n  //   }\n  //   // import.meta.hot.accept(\n  //   //   './stores/counter.ts',\n  //   //   (newStore: Record<string, unknown>) => {\n  //   //     console.log('haha', newStore)\n  //   //   }\n  //   // )\n  //   import.meta.hot.accept('./test.ts', (newTest) => {\n  //     console.log('test updated', newTest)\n  //   })\n  //   const stores = import.meta.glob('./stores/*.ts')\n  //   for (const storeId in stores) {\n  //     console.log('configuring HMR for', storeId)\n  //     const oldUseStore = await stores[storeId]()\n  //     console.log('got', oldUseStore)\n  //     import.meta.hot!.accept(storeId, (newStore: Record<string, unknown>) => {\n  //       console.log('Accepting update for', storeId)\n  //       for (const exportName in newStore) {\n  //         const useStore = newStore[exportName]\n  //         if (isUseStore(useStore) && pinia._s.has(useStore.$id)) {\n  //           const id = useStore.$id\n  //           const existingStore = pinia._s.get(id)!\n  //           // remove the existing store from the cache to force a new one\n  //           pinia._s.delete(id)\n  //           // this adds any new state to pinia and then runs the `hydrate` function\n  //           // which, by default, will reuse the existing state in pinia\n  //           const newStore = useStore(pinia)\n  //           // pinia._s.set(id, existingStore)\n  //         }\n  //       }\n  //     })\n  //   }\n}\n\nconst app = createApp(App)\n  .use(pinia)\n  .use(router)\n  // used in counter setup for tests\n  .provide('injected', 'global')\n\napp.mount('#app')\n"
  },
  {
    "path": "packages/playground/src/router.ts",
    "content": "import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'\nimport kebabcase from 'lodash.kebabcase'\n\nconst viewModules = import.meta.glob('./views/*.vue')\n\nconst nameFromPath = (path: string) => path.replace(/^.*\\/(\\w+)\\.vue$/, '$1')\n\nconst pages: RouteRecordRaw[] = Object.keys(viewModules).map((path) => {\n  const name = nameFromPath(path)\n  return {\n    name,\n    path: name === '404' ? '/:patchMatch(.*)*' : '/' + kebabcase(name),\n    component: viewModules[path],\n    meta: {\n      hide: name === '404',\n    },\n  }\n})\n\nexport const router = createRouter({\n  history: createWebHistory(),\n  routes: [...pages],\n})\n"
  },
  {
    "path": "packages/playground/src/shims-vue.d.ts",
    "content": "declare module '*.vue' {\n  import { DefineComponent } from 'vue'\n  const component: DefineComponent<{}, {}, any>\n  export default component\n}\n"
  },
  {
    "path": "packages/playground/src/stores/cart.ts",
    "content": "import { defineStore } from 'pinia'\nimport { useUserStore } from './user'\n\nexport const useCartStore = defineStore('cart', {\n  state: () => ({\n    rawItems: [] as string[],\n  }),\n  getters: {\n    items: (state) =>\n      state.rawItems.reduce(\n        (items, item) => {\n          const existingItem = items.find((it) => it.name === item)\n\n          if (!existingItem) {\n            items.push({ name: item, amount: 1 })\n          } else {\n            existingItem.amount++\n          }\n\n          return items\n        },\n        [] as Array<{ name: string; amount: number }>\n      ),\n  },\n  actions: {\n    addItem(name: string) {\n      this.rawItems.push(name)\n    },\n\n    removeItem(name: string) {\n      const i = this.rawItems.lastIndexOf(name)\n      if (i > -1) this.rawItems.splice(i, 1)\n    },\n\n    async purchaseItems() {\n      const user = useUserStore()\n      if (!user.name) return\n\n      console.log('Purchasing', this.items)\n      const n = this.items.length\n      this.rawItems = []\n\n      return n\n    },\n  },\n})\n"
  },
  {
    "path": "packages/playground/src/stores/counter.ts",
    "content": "import { acceptHMRUpdate, defineStore } from 'pinia'\n\nconst delay = (t: number) => new Promise((r) => setTimeout(r, t))\n\nexport const useCounter = defineStore('counter', {\n  state: () => ({\n    n: 2,\n    incrementedTimes: 0,\n    decrementedTimes: 0,\n    numbers: [] as number[],\n  }),\n\n  getters: {\n    double: (state) => state.n * 2,\n  },\n\n  actions: {\n    increment(amount = 1) {\n      if (typeof amount !== 'number') {\n        amount = 1\n      }\n      this.incrementedTimes++\n      this.n += amount\n    },\n\n    changeMe() {\n      console.log('change me to test HMR')\n    },\n\n    async fail() {\n      const n = this.n\n      await delay(1000)\n      this.numbers.push(n)\n      await delay(1000)\n      if (this.n !== n) {\n        throw new Error('Someone changed n!')\n      }\n\n      return n\n    },\n\n    async decrementToZero(interval: number = 300, usePatch = true) {\n      if (this.n <= 0) return\n\n      while (this.n > 0) {\n        if (usePatch) {\n          this.$patch({\n            n: this.n - 1,\n            decrementedTimes: this.decrementedTimes + 1,\n          })\n          // this.$patch(state => {\n          //   state.n--\n          //   state.decrementedTimes++\n          // })\n        } else {\n          this.n -= 1\n        }\n        await delay(interval)\n      }\n    },\n  },\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useCounter, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/counterSetup.ts",
    "content": "import { computed, toRefs, reactive, inject } from 'vue'\nimport { acceptHMRUpdate, defineStore } from 'pinia'\n\nconst delay = (t: number) => new Promise((r) => setTimeout(r, t))\n\nexport const useCounter = defineStore('counter-setup', () => {\n  const state = reactive({\n    n: 0,\n    incrementedTimes: 0,\n    decrementedTimes: 0,\n    numbers: [] as number[],\n  })\n\n  const injected = inject('injected', 'fallback value')\n  console.log('injected (should be global)', injected)\n\n  const double = computed(() => state.n * 2)\n\n  function increment(amount = 1) {\n    if (typeof amount !== 'number') {\n      amount = 1\n    }\n    state.incrementedTimes++\n    state.n += amount\n  }\n\n  function changeMe() {\n    console.log('change me to test HMR')\n  }\n\n  async function fail() {\n    const n = state.n\n    await delay(1000)\n    state.numbers.push(n)\n    await delay(1000)\n    if (state.n !== n) {\n      throw new Error('Someone changed n!')\n    }\n\n    return n\n  }\n\n  async function decrementToZero(interval: number = 300) {\n    if (state.n <= 0) return\n\n    while (state.n > 0) {\n      state.n -= 1\n      state.decrementedTimes += 1\n      await delay(interval)\n    }\n  }\n\n  return {\n    ...toRefs(state),\n    double,\n    increment,\n    fail,\n    changeMe,\n    decrementToZero,\n  }\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useCounter, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/demo-counter.ts",
    "content": "import { defineStore, acceptHMRUpdate } from 'pinia'\n\nconst delay = (t: number) => new Promise((r) => setTimeout(r, t))\n// just to ignore the not used error\ndelay(0)\n\nexport const useCounter = defineStore('demo-counter', {\n  state: () => ({\n    n: 0,\n  }),\n  getters: {\n    double: (state) => state.n * 2,\n  },\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useCounter, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/jokes-swrv.ts",
    "content": "import { ref, toRaw, watch } from 'vue'\nimport { acceptHMRUpdate, defineStore } from 'pinia'\nimport { getRandomJoke, Joke } from '../api/jokes'\nimport useSWRV from 'swrv'\n\nexport const useJokesSetup = defineStore('jokes-swrv-setup', () => {\n  // const current = ref<null | Joke>(null)\n  const history = ref<Joke[]>([])\n\n  const { data, error, mutate } = useSWRV('jokes', getRandomJoke)\n\n  watch(data, (joke) => {\n    console.log('changed from within the store', joke)\n    if (joke) {\n      history.value.push(toRaw(joke))\n    }\n  })\n\n  return { current: data, error, history, fetchJoke: mutate }\n})\n\nif (import.meta.hot) {\n  // import.meta.hot.accept(acceptHMRUpdate(useJokes, import.meta.hot))\n  import.meta.hot.accept(acceptHMRUpdate(useJokesSetup, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/jokes.ts",
    "content": "import { ref, unref } from 'vue'\nimport { acceptHMRUpdate, defineStore } from 'pinia'\nimport { getRandomJoke, Joke } from '../api/jokes'\n\nexport const useJokes = defineStore('jokes', {\n  state: () => ({\n    current: null as null | Joke,\n    jokes: [] as Joke[],\n    // hello: true,\n  }),\n  actions: {\n    async fetchJoke() {\n      if (\n        this.current &&\n        // if the request below fails, avoid adding it twice\n        !this.jokes.includes(this.current)\n      ) {\n        this.jokes.push(this.current)\n      }\n\n      // NOTE: Avoid patching an object because it's recursive\n      // this.$patch({ current: await getRandomJoke() })\n      this.current = await getRandomJoke()\n    },\n  },\n})\n\nexport const useJokesSetup = defineStore('jokes-setup', () => {\n  const current = ref<null | Joke>(null)\n  const history = ref<Joke[]>([])\n\n  async function fetchJoke() {\n    const cur = unref(current.value)\n    if (\n      cur &&\n      // if the request below fails, avoid adding it twice\n      !history.value.find((joke) => joke.id === cur.id)\n    ) {\n      history.value.push(cur)\n    }\n\n    current.value = await getRandomJoke()\n    return current.value\n  }\n\n  return { current, history, fetchJoke }\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useJokes, import.meta.hot))\n  // import.meta.hot.accept(acceptHMRUpdate(useJokesSetup, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/jokesUsePromised.ts",
    "content": "import { acceptHMRUpdate, defineStore } from 'pinia'\nimport { getRandomJoke, Joke } from '../api/jokes'\nimport { usePromise } from 'vue-promised'\nimport { ref, watch } from 'vue'\n\nexport const useJokes = defineStore('jokes-vue-promised', {\n  state: () => {\n    const promise = ref(getRandomJoke())\n\n    watch(promise, () => {\n      console.log('promise changed')\n    })\n\n    const promised = usePromise(promise)\n\n    return {\n      promise,\n      ...promised,\n      history: [] as Joke[],\n    }\n  },\n\n  actions: {\n    waitForJoke() {\n      return this.promise\n    },\n\n    fetchJoke() {\n      if (\n        this.data &&\n        // if the request below fails, avoid adding it twice\n        !this.history.includes(this.data)\n      ) {\n        this.history.push(this.data)\n      }\n\n      console.log('fetching')\n      // this.$state.promise = getRandomJoke()\n      // Will fail because we initially had a ref\n      this.promise = getRandomJoke()\n\n      return this.$state.promise\n    },\n  },\n})\n\nexport const useSetupJokes = defineStore('jokes-setup-vue-promised', () => {\n  const history = ref<Joke[]>([])\n  const promise = ref(getRandomJoke())\n  watch(promise, () => {\n    console.log('promise changed')\n  })\n\n  const promised = usePromise(promise)\n\n  function fetchJoke() {\n    if (\n      promised.data.value &&\n      // if the request below fails, avoid adding it twice\n      !history.value.includes(promised.data.value)\n    ) {\n      history.value.push(promised.data.value)\n    }\n\n    console.log('fetching')\n    // this.$state.promise = getRandomJoke()\n    // Will fail because we initially had a ref\n    promise.value = getRandomJoke()\n\n    promise.value.then((joke) => {\n      console.log('got', joke)\n    })\n\n    return promise.value\n  }\n\n  return {\n    promise,\n    ...promised,\n    history,\n    fetchJoke,\n  }\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useJokes, import.meta.hot))\n  // import.meta.hot.accept(acceptHMRUpdate(useSetupJokes, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/nasa-pod.ts",
    "content": "import { ref, watch } from 'vue'\nimport { acceptHMRUpdate, defineStore } from 'pinia'\nimport { getNASAPOD } from '../api/nasa'\nimport { useCachedRequest } from '../composables/useCachedRequest'\n\nexport const useNasaPOD = defineStore('nasa-pod', () => {\n  // can't go past today\n  const today = new Date().toISOString().slice(0, 10)\n\n  const currentDate = ref(today)\n\n  // const image = ref<NASAPOD | undefined>()\n  // const error = ref<Error | undefined>()\n  // const isLoading = ref(false)\n\n  watch(currentDate, fetchPOD)\n\n  const {\n    data: image,\n    error,\n    isLoading,\n    isReady,\n  } = useCachedRequest(currentDate, getNASAPOD)\n\n  function fetchPOD(date: string) {\n    error.value = undefined\n    isLoading.value = true\n\n    return getNASAPOD(date)\n      .then((imageData) => {\n        image.value = imageData\n      })\n      .catch((err) => {\n        error.value = err\n      })\n      .finally(() => {\n        isLoading.value = false\n      })\n  }\n\n  function incrementDay(date: string) {\n    const from = new Date(date).getTime()\n\n    currentDate.value = new Date(from + 1000 * 60 * 60 * 24)\n      .toISOString()\n      .slice(0, 10)\n  }\n\n  function decrementDay(date: string) {\n    const from = new Date(date).getTime()\n\n    currentDate.value = new Date(from - 1000 * 60 * 60 * 24)\n      .toISOString()\n      .slice(0, 10)\n  }\n\n  return {\n    image,\n    currentDate,\n    incrementDay,\n    decrementDay,\n    error,\n    isLoading,\n    isReady,\n  }\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useNasaPOD, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/nasa.ts",
    "content": "import useSWRV from 'swrv'\nimport { ref } from 'vue'\nimport { acceptHMRUpdate, defineStore } from 'pinia'\nimport { getNASAPOD } from '../api/nasa'\n\nexport const useNasaStore = defineStore('nasa-pod-swrv', ({ action }) => {\n  // can't go past today\n  const today = new Date().toISOString().slice(0, 10)\n\n  // const currentDate = computed<string>({\n  //   get: () => image.value?.date || today,\n  //   set: (date) => {\n  //     // TODO: router push\n  //   }\n  // })\n\n  const currentDate = ref(today)\n\n  const {\n    data: image,\n    error,\n    isValidating,\n  } = useSWRV(\n    () => `nasa-pod-${currentDate.value}`,\n    () => getNASAPOD(currentDate.value),\n    {\n      // refreshInterval: 0,\n      // ttl: 0,\n      revalidateOnFocus: false,\n    }\n  )\n\n  const incrementDay = action((date: string) => {\n    const from = new Date(date).getTime()\n\n    currentDate.value = new Date(from + 1000 * 60 * 60 * 24)\n      .toISOString()\n      .slice(0, 10)\n  })\n\n  const decrementDay = action((date: string) => {\n    const from = new Date(date).getTime()\n\n    currentDate.value = new Date(from - 1000 * 60 * 60 * 24)\n      .toISOString()\n      .slice(0, 10)\n  })\n\n  return { image, currentDate, incrementDay, decrementDay, error, isValidating }\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useNasaStore, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/user.ts",
    "content": "import { defineStore } from 'pinia'\n\nexport const useUserStore = defineStore('user', {\n  state: () => ({\n    name: 'Eduardo',\n    isAdmin: true,\n  }),\n  actions: {\n    /**\n     * Attempt to login a user\n     */\n    async login(user: string, password: string) {\n      const userData = await apiLogin(user, password)\n\n      this.$patch({\n        name: user,\n        ...userData,\n      })\n    },\n    logout() {\n      this.$patch({\n        name: '',\n        isAdmin: false,\n      })\n\n      // we could do other stuff like redirecting the user\n    },\n  },\n})\n\n/**\n * Simulate a login\n */\nfunction apiLogin(a: string, p: string) {\n  if (a === 'ed' && p === 'ed') return Promise.resolve({ isAdmin: true })\n  if (p === 'ed') return Promise.resolve({ isAdmin: false })\n  return Promise.reject(new Error('invalid credentials'))\n}\n"
  },
  {
    "path": "packages/playground/src/stores/wholeStore.ts",
    "content": "interface User {\n  id: string\n}\ntype State<T extends string> = { type: T }\ntype AuthStateLoggingIn = State<'loggingIn'>\ntype AuthStateLoggedIn = State<'loggedIn'> & { user: User }\ntype AuthStateError = State<'error'> & { errorMsg: string }\ntype AuhtStateLoggedOut = State<'loggedOut'>\n\nexport type AuthState =\n  | AuthStateLoggingIn\n  | AuthStateLoggedIn\n  | AuthStateError\n  | AuhtStateLoggedOut\n\nimport { acceptHMRUpdate, defineStore } from 'pinia'\n\nconst delay = (t: number) => new Promise((r) => setTimeout(r, t))\n\nexport const useWholeStore = defineStore('whole', {\n  state: (): AuthState => ({ type: 'loggedIn', user: { id: '1' } }),\n\n  getters: {\n    errorMsg: (state) => (state.type === 'error' ? state.errorMsg : ''),\n  },\n\n  actions: {\n    async login() {\n      try {\n        await delay(1000)\n        this.$patch({ type: 'loggedIn', user: { id: '1' } })\n      } catch (e) {\n        const error = e as Error\n        this.$patch({ type: 'error', errorMsg: error.message })\n      }\n    },\n\n    logout() {\n      this.$patch({ type: 'loggedOut' })\n    },\n  },\n})\n\nif (import.meta.hot) {\n  import.meta.hot.accept(acceptHMRUpdate(useWholeStore, import.meta.hot))\n}\n"
  },
  {
    "path": "packages/playground/src/test.ts",
    "content": "export const ha = 3\n"
  },
  {
    "path": "packages/playground/src/views/404.vue",
    "content": "<template>\n  <p>Select one of the pages ⤵️</p>\n\n  <ul>\n    <li v-for=\"page in pages\">\n      <router-link :to=\"page\">{{ page.name }}</router-link>\n    </li>\n  </ul>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useRouter } from 'vue-router'\n\nconst router = useRouter()\n\nconst pages = router\n  .getRoutes()\n  .filter((route) => !route.meta.hide)\n  .map((route) => ({ name: route.name }))\n</script>\n"
  },
  {
    "path": "packages/playground/src/views/About.vue",
    "content": "<template>\n  <h2>About</h2>\n\n  <p>A simple about page that does not use any store.</p>\n</template>\n"
  },
  {
    "path": "packages/playground/src/views/AllStores.vue",
    "content": "<template>\n  <p>\n    I have a store \"{{ userStore.name }}\". I have\n    {{ cartStore.items.length }} items in the cart.\n  </p>\n  <div>\n    <p>Counter: {{ counterStore.double }} = 2 x {{ counterStore.n }}</p>\n    <button @click=\"counterStore.increment(10)\">Increment</button>\n    <button @click=\"counterStore.fail()\">Fail</button>\n    <button @click=\"counterStore.decrementToZero(300)\">Countdown!</button>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useUserStore } from '../stores/user'\nimport { useCartStore } from '../stores/cart'\nimport { useCounter } from '../stores/counter'\n\nconst userStore = useUserStore()\nconst cartStore = useCartStore()\nconst counterStore = useCounter()\n</script>\n"
  },
  {
    "path": "packages/playground/src/views/AllStoresDispose.vue",
    "content": "<template>\n  <p>\n    I have a store \"{{ userStore.name }}\". I have\n    {{ cartStore.items.length }} items in the cart.\n  </p>\n  <div>\n    <p>Counter: {{ counterStore.double }} = 2 x {{ counterStore.n }}</p>\n    <button @click=\"counterStore.increment(10)\">Increment</button>\n    <button @click=\"counterStore.fail()\">Fail</button>\n    <button @click=\"counterStore.decrementToZero(300)\">Countdown!</button>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useUserStore } from '../stores/user'\nimport { useCartStore } from '../stores/cart'\nimport { useCounter } from '../stores/counter'\nimport { onUnmounted } from 'vue'\n\nconst userStore = useUserStore()\nconst cartStore = useCartStore()\nconst counterStore = useCounter()\n\nonUnmounted(() => {\n  userStore.$dispose()\n  cartStore.$dispose()\n  counterStore.$dispose()\n})\n</script>\n"
  },
  {
    "path": "packages/playground/src/views/CounterSetupStore.vue",
    "content": "<script lang=\"ts\" setup>\nimport { ref, inject } from 'vue'\nimport { useCounter } from '../stores/counterSetup'\n\nconsole.log(\n  '(1) injected (within component should be from component)',\n  inject('hello')\n)\n\nconst counter = useCounter()\n\nconsole.log(\n  '(2) injected (within component should be from component)',\n  inject('hello')\n)\nconst n = ref(0)\n</script>\n\n<template>\n  <h2>Local variables</h2>\n\n  <button @click=\"n++\">Increment local: {{ n }}</button>\n\n  <h2>Counter Store</h2>\n\n  <p>Counter :{{ counter.n }}. Double: {{ counter.double }}</p>\n\n  <p>\n    Increment the Store <br />\n\n    <button @click=\"counter.increment()\">+1</button>\n    <button @click=\"counter.increment(10)\">+10</button>\n    <button @click=\"counter.increment(100)\">+100</button>\n    <button @click=\"counter.n++\">Direct Increment</button>\n    <button\n      @click=\"\n        counter.$patch((state) => {\n          state.n++\n          state.incrementedTimes++\n        })\n      \"\n    >\n      Direct patch\n    </button>\n  </p>\n\n  <p>\n    Other actions <br />\n\n    <button @click=\"counter.fail\">Fail</button>\n    <button @click=\"counter.decrementToZero(300)\">Decrement to zero</button>\n    <button @click=\"counter.changeMe()\"><code>counter.changeMe()</code></button>\n  </p>\n\n  <hr />\n\n  <p><code>counter.$state</code>:</p>\n\n  <pre>{{ counter.$state }}</pre>\n</template>\n"
  },
  {
    "path": "packages/playground/src/views/CounterStore.vue",
    "content": "<template>\n  <h2>Local variables</h2>\n\n  <button @click=\"n++\">Increment local: {{ n }}</button>\n\n  <h2>Counter Store</h2>\n\n  <p>Counter :{{ counter.n }}. Double: {{ counter.double }}</p>\n\n  <p>\n    Increment the Store <br />\n\n    <button @click=\"counter.increment()\">+1</button>\n    <button @click=\"counter.increment(10)\">+10</button>\n    <button @click=\"counter.increment(100)\">+100</button>\n    <button @click=\"counter.n++\">Direct Increment</button>\n    <button\n      @click=\"\n        counter.$patch((state) => {\n          state.n++\n          state.incrementedTimes++\n        })\n      \"\n    >\n      Direct patch\n    </button>\n  </p>\n\n  <p>\n    Other actions <br />\n\n    <label>\n      <input type=\"checkbox\" v-model=\"usePatch\" /> Use Patch when decrementing\n    </label>\n    <br />\n\n    <button @click=\"counter.fail\">Fail</button>\n    <button @click=\"counter.decrementToZero(300, usePatch)\">\n      Decrement to zero\n    </button>\n    <button @click=\"counter.changeMe()\"><code>counter.changeMe()</code></button>\n  </p>\n\n  <hr />\n\n  <p><code>counter.$state</code>:</p>\n\n  <pre>{{ counter.$state }}</pre>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue'\nimport { useCounter } from '../stores/counter'\n\nconst counter = useCounter()\nconst usePatch = ref(true)\nconst n = ref(0)\n</script>\n"
  },
  {
    "path": "packages/playground/src/views/DemoCounter.vue",
    "content": "<template>\n  <h2>Counter Store</h2>\n\n  <p>Counter :{{ counter.n }}. Double: {{ counter.double }}</p>\n\n  <button @click=\"counter.n++\">Increment</button>\n\n  <hr />\n\n  <p><code>counter.$state</code>:</p>\n\n  <pre>{{ counter.$state }}</pre>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useCounter } from '../stores/demo-counter'\n\nconst counter = useCounter()\n</script>\n"
  },
  {
    "path": "packages/playground/src/views/Jokes.vue",
    "content": "<template>\n  <h3>\n    Pinia + <a href=\"https://github.com/posva/vue-promised\">Vue Promised</a>\n  </h3>\n\n  <main>\n    <section>\n      <button\n        :disabled=\"state !== 'ready'\"\n        @click=\"fetchRandomJoke\"\n        style=\"margin-bottom: 4px\"\n      >\n        {{ buttonText }}\n      </button>\n\n      <div style=\"min-height: 9rem\" v-if=\"jokes.current\">\n        <blockquote :key=\"jokes.current.id\">\n          <i>{{ jokes.current.setup }}</i>\n          <br />\n          <br />\n          <p class=\"appear\" @animationend=\"state = 'ready'\">\n            {{ jokes.current.punchline }}\n          </p>\n        </blockquote>\n      </div>\n    </section>\n  </main>\n\n  <pre>{{ jokes.$state }}</pre>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, onMounted, ref } from 'vue'\nimport { useJokes } from '../stores/jokes'\n\nconst jokes = useJokes()\n// const jokes = useJokesSetup()\n\nconst texts = {\n  loading: 'Fetching the joke...',\n  waiting: 'Wait for it...',\n  ready: 'Another one?',\n}\n\nconst state = ref<'waiting' | 'loading' | 'ready'>('waiting')\n\nconst buttonText = computed(() => texts[state.value])\n\nfunction fetchRandomJoke() {\n  state.value = 'loading'\n\n  jokes.fetchJoke().finally(() => {\n    state.value = 'waiting'\n    console.log('done fetching', jokes.current)\n  })\n}\n\nonMounted(() => {\n  console.log('mounted')\n  // @ts-expect-error\n  window.jo = jokes\n  console.log('new pending')\n  fetchRandomJoke()\n})\n</script>\n\n<style>\n@keyframes appear {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n.appear {\n  opacity: 0;\n  animation: appear 1s ease-in-out 3s;\n  animation-fill-mode: forwards;\n}\n</style>\n"
  },
  {
    "path": "packages/playground/src/views/JokesPromised.vue",
    "content": "<template>\n  <h3>\n    Pinia + <a href=\"https://github.com/posva/vue-promised\">Vue Promised</a>\n  </h3>\n\n  <main>\n    <section>\n      <button\n        :disabled=\"state !== 'ready'\"\n        @click=\"fetchRandomJoke\"\n        style=\"margin-bottom: 4px\"\n      >\n        {{ buttonText }}\n      </button>\n\n      <div style=\"min-height: 9rem\">\n        <template v-if=\"jokes.isPending && jokes.isDelayElapsed\">\n          <div class=\"spinner\"></div>\n        </template>\n\n        <template v-else-if=\"jokes.data\">\n          <blockquote :key=\"jokes.data.id\">\n            <i>{{ jokes.data.setup }}</i>\n            <br />\n            <br />\n            <p class=\"appear\" @animationend=\"state = 'ready'\">\n              {{ jokes.data.punchline }}\n            </p>\n          </blockquote>\n        </template>\n\n        <template v-else-if=\"jokes.error\">\n          <p>Error: {{ jokes.error.message }}</p>\n        </template>\n      </div>\n    </section>\n  </main>\n\n  <pre>{{ jokes.$state }}</pre>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, onMounted, ref } from 'vue'\nimport { useJokes, useSetupJokes } from '../stores/jokesUsePromised'\n\n// const jokes = useJokes()\nconst jokes = useSetupJokes()\n\nconst texts = {\n  loading: 'Fetching the joke...',\n  waiting: 'Wait for it...',\n  ready: 'Another one?',\n}\n\nconst state = ref<'waiting' | 'loading' | 'ready'>('waiting')\n\nconst buttonText = computed(() => texts[state.value])\n\nfunction fetchRandomJoke() {\n  state.value = 'loading'\n\n  jokes.fetchJoke().finally(() => {\n    state.value = 'waiting'\n    console.log('done fetching', jokes.data)\n  })\n}\n\nonMounted(() => {\n  console.log('mounted')\n  // @ts-expect-error\n  window.jo = jokes\n  if (!jokes.isPending) {\n    console.log('new pending')\n    fetchRandomJoke()\n  }\n})\n</script>\n\n<style>\n@keyframes appear {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n.appear {\n  opacity: 0;\n  animation: appear 1s ease-in-out 3s;\n  animation-fill-mode: forwards;\n}\n</style>\n"
  },
  {
    "path": "packages/playground/src/views/NasaPOD.vue",
    "content": "<template>\n  <section>\n    <h2>Nasa Picture of the day</h2>\n\n    <section class=\"py-4 text-center date-selector\">\n      <button @click=\"decrementDay(currentDate)\">Previous Day</button>\n      <p class=\"inline-block mx-2\">\n        <input type=\"date\" v-model=\"currentDate\" />\n      </p>\n      <button\n        @click=\"incrementDay(currentDate)\"\n        :disabled=\"currentDate >= today\"\n      >\n        Next Day\n      </button>\n    </section>\n\n    <template v-if=\"!isReady && isLoading\">\n      <div class=\"spinner\"></div>\n    </template>\n\n    <template v-else-if=\"image\">\n      <h3 class=\"mb-4 text-center\">{{ image.title }}</h3>\n\n      <figure class=\"mb-0\">\n        <iframe\n          v-if=\"image.url.includes('youtube.com')\"\n          width=\"560\"\n          height=\"315\"\n          :key=\"image.url\"\n          :src=\"image.url\"\n          frameborder=\"0\"\n          allow=\"\n            accelerometer;\n            autoplay;\n            clipboard-write;\n            encrypted-media;\n            gyroscope;\n            picture-in-picture;\n          \"\n          allowfullscreen\n        ></iframe>\n        <img\n          v-else\n          class=\"max-w-full m-auto max-h-[75vh]\"\n          :src=\"image.url\"\n          :alt=\"image.title\"\n        />\n        <figcaption class=\"mt-2\">{{ image.explanation }}</figcaption>\n      </figure>\n    </template>\n  </section>\n</template>\n\n<script setup lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport { useNasaPOD } from '../stores/nasa-pod'\n\nconst nasa = useNasaPOD()\n\nconst today = new Date().toISOString().slice(0, 10)\n\nconst { image, currentDate, isLoading, isReady } = storeToRefs(nasa)\nconst { incrementDay, decrementDay } = nasa\n</script>\n\n<style scoped>\n.date-selector {\n  padding: 1rem 0;\n  text-align: center;\n}\n\n.date-selector > p {\n  display: inline-block;\n  margin: 0 0.4rem;\n}\n\nbutton {\n  margin: 0;\n}\n</style>\n"
  },
  {
    "path": "packages/playground/src/views/NasaPODSwrv.vue",
    "content": "<template>\n  <section>\n    <h2>Nasa Picture of the day</h2>\n\n    <section class=\"py-4 text-center date-selector\">\n      <button @click=\"decrementDay(currentDate)\">Previous Day</button>\n      <p class=\"inline-block mx-2\">\n        <input type=\"date\" v-model=\"currentDate\" />\n      </p>\n      <button\n        @click=\"incrementDay(currentDate)\"\n        :disabled=\"currentDate >= today\"\n      >\n        Next Day\n      </button>\n    </section>\n\n    <template v-if=\"error\">\n      <p>\n        ❌ Error:\n        <br />\n        {{ error }}\n      </p>\n    </template>\n\n    <template v-else-if=\"isValidating\">\n      <div class=\"spinner\"></div>\n    </template>\n\n    <template v-else-if=\"image\">\n      <h3 class=\"mb-4 text-center\">{{ image.title }}</h3>\n\n      <figure class=\"mb-0\">\n        <img\n          class=\"max-w-full m-auto max-h-[75vh]\"\n          :src=\"image.url\"\n          :key=\"image.url\"\n          :alt=\"image.title\"\n        />\n        <figcaption class=\"mt-2\">{{ image.explanation }}</figcaption>\n      </figure>\n    </template>\n\n    <template v-else-if=\"isValidating\">\n      <div class=\"spinner\"></div>\n    </template>\n  </section>\n</template>\n\n<script setup lang=\"ts\">\nimport { toRefs } from 'vue'\nimport { useNasaStore } from '../stores/nasa'\n\nconst nasa = useNasaStore()\n\nconst today = new Date().toISOString().slice(0, 10)\n\nconst { image, error, currentDate, isValidating } = toRefs(nasa.$state)\nconst { incrementDay, decrementDay } = nasa\n</script>\n\n<style scoped>\n.date-selector {\n  padding: 1rem 0;\n  text-align: center;\n}\n\n.date-selector > p {\n  display: inline-block;\n  margin: 0 0.4rem;\n}\n\nbutton {\n  margin: 0;\n}\n</style>\n"
  },
  {
    "path": "packages/playground/src/views/swrv.vue",
    "content": "<template>\n  <h3>Pinia + <a href=\"https://github.com/posva/vue-promised\">SWRV</a></h3>\n\n  <main>\n    <section>\n      <button\n        :disabled=\"state !== 'ready'\"\n        @click=\"fetchRandomJoke\"\n        style=\"margin-bottom: 4px\"\n      >\n        {{ buttonText }}\n      </button>\n\n      <div style=\"min-height: 9rem\" v-if=\"jokes.current\">\n        <blockquote :key=\"jokes.current.id\">\n          <i>{{ jokes.current.setup }}</i>\n          <br />\n          <br />\n          <p class=\"appear\" @animationend=\"state = 'ready'\">\n            {{ jokes.current.punchline }}\n          </p>\n        </blockquote>\n      </div>\n    </section>\n  </main>\n\n  <pre>{{ jokes.$state }}</pre>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, onMounted, ref, watch } from 'vue'\nimport { useJokesSetup } from '../stores/jokes-swrv'\n\n// const jokes = useJokes()\nconst jokes = useJokesSetup()\n\nconst texts = {\n  loading: 'Fetching the joke...',\n  waiting: 'Wait for it...',\n  ready: 'Another one?',\n}\n\nconst state = ref<'waiting' | 'loading' | 'ready'>('waiting')\n\nconst buttonText = computed(() => texts[state.value])\n\nfunction fetchRandomJoke() {\n  state.value = 'loading'\n\n  jokes.fetchJoke().finally(() => {\n    state.value = 'waiting'\n    console.log('done fetching', jokes.current)\n  })\n}\n\nwatch(\n  () => jokes.current,\n  (joke) => {\n    console.log('Got joke', joke)\n  }\n)\n\nonMounted(() => {\n  console.log('mounted')\n  // @ts-expect-error\n  window.jo = jokes\n  console.log('new pending')\n  fetchRandomJoke()\n})\n</script>\n\n<style>\n@keyframes appear {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n.appear {\n  opacity: 0;\n  animation: appear 1s ease-in-out 3s;\n  animation-fill-mode: forwards;\n}\n</style>\n"
  },
  {
    "path": "packages/playground/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n\n// Global compile-time constants\ndeclare var __DEV__: boolean\ndeclare var __TEST__: boolean\ndeclare var __FEATURE_PROD_DEVTOOLS__: boolean\ndeclare var __BROWSER__: boolean\ndeclare var __USE_DEVTOOLS__: boolean\ndeclare var __CI__: boolean\ndeclare var __VUE_DEVTOOLS_TOAST__: (\n  message: string,\n  type?: 'normal' | 'error' | 'warn'\n) => void\n"
  },
  {
    "path": "packages/playground/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"Bundler\",\n    \"strict\": true,\n    \"jsx\": \"preserve\",\n    \"sourceMap\": true,\n    \"esModuleInterop\": true,\n    \"lib\": [\"esnext\", \"dom\"],\n    \"types\": [\"vite/client\"],\n    \"paths\": {\n      \"pinia\": [\"../pinia/src/index.ts\"]\n    }\n  }\n  // \"include\": [\"src/**/*.ts\", \"src/**/*.d.ts\", \"src/**/*.tsx\", \"src/**/*.vue\"]\n}\n"
  },
  {
    "path": "packages/playground/vite.config.ts",
    "content": "import { defineConfig, Plugin } from 'vite'\nimport Vue from '@vitejs/plugin-vue'\nimport fs from 'node:fs/promises'\nimport { fileURLToPath, URL } from 'node:url'\nimport VueDevtools from 'vite-plugin-vue-devtools'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [Vue(), copyPiniaPlugin(), VueDevtools()],\n  define: {\n    __DEV__: 'true',\n    // __BROWSER__: 'true',\n    __TEST__: 'false',\n  },\n  resolve: {\n    dedupe: ['vue', 'pinia'],\n    alias: {\n      pinia: fileURLToPath(new URL('../pinia/src/index.ts', import.meta.url)),\n    },\n  },\n  optimizeDeps: {\n    exclude: ['@vueuse/shared', '@vueuse/core', 'pinia'],\n  },\n})\n\nfunction copyPiniaPlugin(): Plugin {\n  return {\n    name: 'copy-pinia',\n    async generateBundle() {\n      const filePath = fileURLToPath(\n        new URL('../pinia/dist/pinia.mjs', import.meta.url)\n      )\n\n      // throws if file doesn't exist\n      await fs.access(filePath)\n\n      this.emitFile({\n        type: 'asset',\n        fileName: 'pinia.mjs',\n        source: await fs.readFile(filePath, 'utf-8'),\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/size-check/package.json",
    "content": "{\n  \"name\": \"@pinia/size-check\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"description\": \"size checks\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build:size\": \"rollup -c rollup.config.mjs\",\n    \"size\": \"pnpm run build:size && pnpm run size:check\",\n    \"size:check\": \"node scripts/check-size.mjs\"\n  },\n  \"dependencies\": {\n    \"pinia\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"brotli-wasm\": \"~1.2.0\",\n    \"zlib\": \"^1.0.5\"\n  }\n}\n"
  },
  {
    "path": "packages/size-check/rollup.config.mjs",
    "content": "import { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport ts from 'rollup-plugin-typescript2'\nimport replace from '@rollup/plugin-replace'\nimport nodeResolve from '@rollup/plugin-node-resolve'\nimport commonjs from '@rollup/plugin-commonjs'\nimport terser from '@rollup/plugin-terser'\nimport alias from '@rollup/plugin-alias'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/** @type {import('rollup').RollupOptions[]} */\nconst config = ['pinia'].map(createConfig)\n\nexport default config\n\n/**\n *\n * @param {string} file\n * @returns {import('rollup').RollupOptions}\n */\nfunction createConfig(file) {\n  return {\n    external: [\n      'vue',\n      // this is left to test things\n      // '@vue/devtools-api'\n    ],\n    output: {\n      file: resolve(__dirname, `./dist/${file}.js`),\n      format: 'esm',\n    },\n    input: resolve(__dirname, `./src/${file}.js`),\n    plugins: [\n      alias({\n        entries: {\n          pinia: resolve(__dirname, '../pinia/dist/pinia.mjs'),\n        },\n      }),\n      replace({\n        preventAssignment: true,\n        values: {\n          __DEV__: 'false',\n          'process.env.NODE_ENV': JSON.stringify('production'),\n          // this is only used during tests\n          __TEST__: 'false',\n          // If the build is expected to run directly in the browser (global / esm builds)\n          __BROWSER__: 'true',\n          // is targeting bundlers?\n          __BUNDLER__: 'false',\n          __GLOBAL__: 'false',\n          // is targeting Node (SSR)?\n          __NODE_JS__: 'false',\n          __VUE_PROD_DEVTOOLS__: 'false',\n        },\n      }),\n      ts({\n        check: false,\n        tsconfig: resolve(__dirname, '../../tsconfig.json'),\n        cacheRoot: resolve(__dirname, '../../node_modules/.rts2_cache'),\n        tsconfigOverride: {\n          compilerOptions: {\n            sourceMap: false,\n            declaration: false,\n            declarationMap: false,\n          },\n          exclude: ['__tests__', 'test-dts'],\n        },\n      }),\n      nodeResolve(),\n      commonjs(),\n      terser({\n        format: {\n          comments: false,\n        },\n        module: true,\n        compress: {\n          ecma: 2019,\n          pure_getters: true,\n        },\n      }),\n    ],\n  }\n}\n"
  },
  {
    "path": "packages/size-check/scripts/check-size.mjs",
    "content": "// @ts-check\nimport fs from 'node:fs'\nimport { globby } from 'globby'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport { gzipSync } from 'node:zlib'\nimport { fileURLToPath } from 'node:url'\nimport { compress } from 'brotli-wasm'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\nasync function checkFileSize(filePath) {\n  if (!fs.existsSync(filePath)) {\n    return\n  }\n  const file = fs.readFileSync(filePath)\n  const minSize = (file.length / 1024).toFixed(2) + 'kb'\n  const gzipped = gzipSync(file)\n  const gzippedSize = (gzipped.length / 1024).toFixed(2) + 'kb'\n  const compressed = await compress(file)\n  const compressedSize = (compressed.length / 1024).toFixed(2) + 'kb'\n  console.log(\n    `${chalk.gray(\n      chalk.bold(path.basename(filePath))\n    )} min:${minSize} / gzip:${gzippedSize} / brotli:${compressedSize}`\n  )\n}\n\nasync function main() {\n  const paths = await globby(path.resolve(__dirname, '../dist/*.js'))\n\n  for (const file of paths) {\n    checkFileSize(file)\n  }\n}\n\nmain()\n"
  },
  {
    "path": "packages/size-check/src/pinia.js",
    "content": "import { createPinia, defineStore } from 'pinia'\n\nexport const pinia = createPinia()\n// @ts-ignore\nexport const useStore = defineStore()\n"
  },
  {
    "path": "packages/testing/CHANGELOG.md",
    "content": "### [1.0.3](https://github.com/vuejs/pinia/compare/@pinia/testing@1.0.2...@pinia/testing@1.0.3) (2025-11-05)\n\n### Features\n\n- add selective action stubbing support ([#3040](https://github.com/vuejs/pinia/issues/3040)) ([cff409e](https://github.com/vuejs/pinia/commit/cff409edf1862d54ad89528c0770c9c219453eed))\n\n### Bug Fixes\n\n- store type ([9b92217](https://github.com/vuejs/pinia/commit/9b92217788886fe757d7afd3aee11dff2ddf349c))\n\n### [1.0.2](https://github.com/vuejs/pinia/compare/@pinia/testing@1.0.1...@pinia/testing@1.0.2) (2025-06-04)\n\nNo code changes in this release.\n\n### [1.0.1](https://github.com/vuejs/pinia/compare/@pinia/testing@1.0.0...@pinia/testing@1.0.1) (2025-04-09)\n\n### Bug Fixes\n\n- consistent computation of computed in tests with storeToRefs ([417db7a](https://github.com/vuejs/pinia/commit/417db7aacb35b98ebe4274fd43bba593eaa583df)), closes [#2913](https://github.com/vuejs/pinia/issues/2913)\n\n## [1.0.0](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.7...@pinia/testing@1.0.0) (2025-02-11)\n\nThe testing package has been stable for a long time so it was time to have a v1!\n\n### Features\n\n- **testing:** warn about incorrect createSpy ([394f655](https://github.com/vuejs/pinia/commit/394f6553d13f2b46c6e52a68145c24699b98e7fa)), closes [#2896](https://github.com/vuejs/pinia/issues/2896)\n\n## [0.1.7](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.6...@pinia/testing@0.1.7) (2024-11-03)\n\nNo code changes in this release.\n\n## [0.1.6](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.5...@pinia/testing@0.1.6) (2024-09-30)\n\nNo code changes in this release.\n\n## [0.1.5](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.4...@pinia/testing@0.1.5) (2024-08-06)\n\nNo code changes in this release.\n\n## [0.1.4](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.4-beta.0...@pinia/testing@0.1.4) (2024-07-26)\n\nNo code changes in this release.\n\n## [0.1.4-beta.0](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.3...@pinia/testing@0.1.4-beta.0) (2024-04-17)\n\n### Bug Fixes\n\n- **types:** use declare module vue ([8a6ce86](https://github.com/vuejs/pinia/commit/8a6ce86db83b6315c067c8a98c898b3c74efe62e))\n\n## [0.1.3](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.3...@pinia/testing@0.1.3) (2024-04-04)\n\n### Bug Fixes\n\n- **types:** use declare module vue ([8a6ce86](https://github.com/vuejs/pinia/commit/8a6ce86db83b6315c067c8a98c898b3c74efe62e))\n\n## [0.1.3](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.2...@pinia/testing@0.1.3) (2023-07-26)\n\nNo code changes in this release.\n\n## [0.1.2](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.1...@pinia/testing@0.1.2) (2023-05-18)\n\n- Force vue-demi version\n\n## [0.1.1](https://github.com/vuejs/pinia/compare/@pinia/testing@0.1.0...@pinia/testing@0.1.1) (2023-05-17)\n\nNo code changes in this release.\n\n# [0.1.0](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.16...@pinia/testing@0.1.0) (2023-05-08)\n\n### Features\n\n- **testing:** allow mocking $reset ([5f526a3](https://github.com/vuejs/pinia/commit/5f526a33ab0ac441fe865344977a11e0e471ce17)), closes [#2188](https://github.com/vuejs/pinia/issues/2188)\n\n## [0.0.16](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.15...@pinia/testing@0.0.16) (2023-04-07)\n\n### Bug Fixes\n\n- support \"types\" condition in \"exports\" field ([#2078](https://github.com/vuejs/pinia/issues/2078)) ([66d3a5e](https://github.com/vuejs/pinia/commit/66d3a5edd03f28f52daf35449db8c5f660c70b01))\n- **testing:** override computed in setup stores ([f9534c9](https://github.com/vuejs/pinia/commit/f9534c926469027f8ccc75c43ce1ea329b58aa0d)), closes [#2109](https://github.com/vuejs/pinia/issues/2109)\n\n## [0.0.15](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.14...@pinia/testing@0.0.15) (2023-02-20)\n\nNo changes in this release\n\n## [0.0.14](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.13...@pinia/testing@0.0.14) (2022-08-18)\n\n- refactor changes\n\n## [0.0.13](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.12...@pinia/testing@0.0.13) (2022-07-25)\n\n- doc generation changes\n\n## [0.0.12](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.11...@pinia/testing@0.0.12) (2022-05-05)\n\n### Bug Fixes\n\n- **testing:** allow overriding plugin computed properties ([ad90fd2](https://github.com/vuejs/pinia/commit/ad90fd24eecca8bd7bff238bcfa039e1a0a7f3d5))\n- **testing:** correct order of plugin installation ([0f789fe](https://github.com/vuejs/pinia/commit/0f789fe1591ef8d2d10a8616c7abac8ad09cdf98))\n- **testing:** stub actions without app ([2e4f6ca](https://github.com/vuejs/pinia/commit/2e4f6ca2e5ba92bc5ba835ebad4ab325a6428a5f))\n\n## [0.0.11](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.10...@pinia/testing@0.0.11) (2022-03-31)\n\n### Bug Fixes\n\n- avoid prototype pollution ([e4858f9](https://github.com/vuejs/pinia/commit/e4858f9d5f447ba6162ca9f2472608a8bac3eca7))\n- **testing:** enable initialState witohut app ([0a99a75](https://github.com/vuejs/pinia/commit/0a99a7589bed28104e26fccfa4fad007d73f4ca1))\n- **testing:** Vue 2 initial state reactive ([#1165](https://github.com/vuejs/pinia/issues/1165)) ([f23af8e](https://github.com/vuejs/pinia/commit/f23af8eac97b055e58908eb76aae684fd68685b5))\n\n### Features\n\n- **testing:** allow overriding computed in tests ([f4db826](https://github.com/vuejs/pinia/commit/f4db8264bd61467fa85f2407aedf23756af4b67c)), closes [#945](https://github.com/vuejs/pinia/issues/945)\n\n## [0.0.10](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.9...@pinia/testing@0.0.10) (2022-03-14)\n\n### Features\n\n- Automatically vitest globals handling\n\n## [0.0.9](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.8...@pinia/testing@0.0.9) (2021-12-20)\n\nNo code updates in this release\n\n## [0.0.8](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.7...@pinia/testing@0.0.8) (2021-12-04)\n\n### Features\n\n- can set an initialState for tests ([028e0ca](https://github.com/vuejs/pinia/commit/028e0cae2f46744f90c98914cfca13daa7ce36c1))\n\n## [0.0.7](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.6...@pinia/testing@0.0.7) (2021-12-01)\n\n### Bug Fixes\n\n- **testing:** preserve non-enumerable properties of pinia instance in createTestingPinia ([#841](https://github.com/vuejs/pinia/issues/841)) ([b130d6f](https://github.com/vuejs/pinia/commit/b130d6f648239293457f347b42a7f1b668748d30))\n\n## [0.0.6](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.5...@pinia/testing@0.0.6) (2021-11-19)\n\n### Bug Fixes\n\n- **testing:** typo when detecting jest existence ([#811](https://github.com/vuejs/pinia/issues/811)) ([c1fd013](https://github.com/vuejs/pinia/commit/c1fd01350b12b09ce49f923ebc9fee992c2408fd))\n\n## [0.0.5](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.4...@pinia/testing@0.0.5) (2021-11-03)\n\nNothing new.\n\n## [0.0.4](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.3...@pinia/testing@0.0.4) (2021-10-21)\n\n### Bug Fixes\n\n- correct peer deps and deps ([c83677a](https://github.com/vuejs/pinia/commit/c83677a9cf7a1cb20b2e6fed529f3c5500062648))\n\n## [0.0.3](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.2...@pinia/testing@0.0.3) (2021-09-03)\n\n### Features\n\n- add typedoc ([b98e23d](https://github.com/vuejs/pinia/commit/b98e23d5588925c6a0094a92067a3cc5784e965d))\n\n## [0.0.2](https://github.com/vuejs/pinia/compare/@pinia/testing@0.0.1...@pinia/testing@0.0.2) (2021-08-19)\n\nSmall refactor with no effective changes.\n\n## 0.0.1 (2021-08-19)\n\n### Features\n\n- **testing:** add testing package ([fc05376](https://github.com/vuejs/pinia/commit/fc053763752c2b11d7b851f95334034a1f9b8347))\n"
  },
  {
    "path": "packages/testing/README.md",
    "content": "# Pinia testing module\n"
  },
  {
    "path": "packages/testing/package.json",
    "content": "{\n  \"name\": \"@pinia/testing\",\n  \"version\": \"1.0.3\",\n  \"description\": \"Testing module for Pinia\",\n  \"keywords\": [\n    \"mock\",\n    \"pinia\",\n    \"store\",\n    \"testing\",\n    \"tests\",\n    \"vue\",\n    \"vuex\"\n  ],\n  \"homepage\": \"https://pinia.vuejs.org/cookbook/testing.html\",\n  \"bugs\": {\n    \"url\": \"https://github.com/vuejs/pinia/issues\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Eduardo San Martin Morote\",\n    \"email\": \"posva13@gmail.com\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/vuejs/pinia.git\"\n  },\n  \"funding\": \"https://github.com/sponsors/posva\",\n  \"files\": [\n    \"dist/*.d.mts\",\n    \"dist/*.mjs\"\n  ],\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"types\": \"./dist/index.d.mts\",\n  \"exports\": {\n    \".\": \"./dist/index.mjs\",\n    \"./package.json\": \"./package.json\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"build\": \"tsdown\",\n    \"changelog\": \"conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l @pinia/testing -r 1\"\n  },\n  \"devDependencies\": {\n    \"pinia\": \"workspace:*\",\n    \"tsdown\": \"^0.20.1\"\n  },\n  \"peerDependencies\": {\n    \"pinia\": \">=3.0.4\"\n  }\n}\n"
  },
  {
    "path": "packages/testing/src/index.ts",
    "content": "/**\n * @module @pinia/testing\n */\nexport { createTestingPinia } from './testing'\nexport type { TestingPinia, TestingOptions } from './testing'\n"
  },
  {
    "path": "packages/testing/src/initialState.spec.ts",
    "content": "import { describe, it, expect } from 'vitest'\nimport { createTestingPinia, TestingOptions } from './testing'\nimport { defineStore } from 'pinia'\nimport { mount } from '@vue/test-utils'\nimport { defineComponent } from 'vue'\n\ndescribe('Testing: initial state', () => {\n  const useCounter = defineStore('counter', {\n    state: () => ({ n: 0, nested: { n: 0, other: false } }),\n    actions: {\n      increment(amount = 1) {\n        this.n += amount\n      },\n    },\n  })\n\n  const Counter = defineComponent({\n    setup() {\n      const counter = useCounter()\n      return { counter }\n    },\n    template: `\n    <button @click=\"counter.increment()\">+1</button>\n    <span>{{ counter.n }}</span>\n    <button @click=\"counter.increment(10)\">+10</button>\n    `,\n  })\n\n  function factory(options?: TestingOptions) {\n    const wrapper = mount(Counter, {\n      global: {\n        plugins: [createTestingPinia(options)],\n      },\n    })\n\n    const counter = useCounter()\n\n    return { wrapper, counter }\n  }\n\n  it('can set an initial state', () => {\n    const { counter } = factory({\n      initialState: { counter: { n: 10 } },\n    })\n    expect(counter.nested).toEqual({ n: 0, other: false })\n    expect(counter.n).toBe(10)\n    counter.n++\n    expect(counter.n).toBe(11)\n  })\n\n  it('can provide objects', () => {\n    const { counter } = factory({\n      initialState: { counter: { nested: { n: 10 } } },\n    })\n    expect(counter.n).toBe(0)\n    expect(counter.nested.other).toBe(false)\n    expect(counter.nested.n).toBe(10)\n    counter.nested.n++\n    expect(counter.nested.n).toBe(11)\n  })\n\n  it('can set an initial state with no app', () => {\n    const pinia = createTestingPinia({\n      initialState: {\n        counter: { n: 20 },\n      },\n    })\n    const counter = useCounter(pinia)\n    expect(counter.nested).toEqual({ n: 0, other: false })\n    expect(counter.n).toBe(20)\n    counter.n++\n    expect(counter.n).toBe(21)\n  })\n})\n"
  },
  {
    "path": "packages/testing/src/mocked-store.spec.ts",
    "content": "import { describe, expect, it, vi, type Mock } from 'vitest'\nimport { computed, defineComponent, ref, type UnwrapRef } from 'vue'\nimport { defineStore, type Store, type StoreDefinition } from 'pinia'\nimport { TestingOptions, createTestingPinia } from './testing'\nimport { mount } from '@vue/test-utils'\n\nfunction mockedStore<TStoreDef extends () => unknown>(\n  useStore: TStoreDef\n): TStoreDef extends StoreDefinition<\n  infer Id,\n  infer State,\n  infer Getters,\n  infer Actions\n>\n  ? Store<\n      Id,\n      State,\n      Record<string, never>,\n      {\n        [K in keyof Actions]: Actions[K] extends (...args: any[]) => any\n          ? // 👇 depends on your testing framework\n            Mock<Actions[K]>\n          : Actions[K]\n      }\n    > & {\n      [K in keyof Getters]: UnwrapRef<Getters[K]>\n    }\n  : ReturnType<TStoreDef> {\n  return useStore() as any\n}\n\ndescribe('mockedStore', () => {\n  const useCounter = defineStore('counter-setup', () => {\n    const n = ref(0)\n    const doubleComputedCallCount = ref(0)\n    const double = computed(() => {\n      doubleComputedCallCount.value++\n      return n.value * 2\n    })\n    const doublePlusOne = computed(() => double.value + 1)\n    function increment(amount = 1) {\n      n.value += amount\n    }\n    function decrement() {\n      n.value--\n    }\n    function setValue(newValue: number) {\n      n.value = newValue\n    }\n    function $reset() {\n      n.value = 0\n    }\n\n    return {\n      n,\n      doubleComputedCallCount,\n      double,\n      doublePlusOne,\n      increment,\n      decrement,\n      setValue,\n      $reset,\n    }\n  })\n\n  const Counter = defineComponent({\n    setup() {\n      const counter = useCounter()\n      return { counter }\n    },\n    template: `\n    <button @click=\"counter.increment()\">+1</button>\n    <span>{{ counter.n }}</span>\n    <button @click=\"counter.increment(10)\">+10</button>\n    `,\n  })\n\n  function factory(options?: TestingOptions) {\n    const wrapper = mount(Counter, {\n      global: {\n        plugins: [createTestingPinia(options)],\n      },\n    })\n\n    const counter = mockedStore(useCounter)\n\n    return { wrapper, counter }\n  }\n\n  it('can unstub actions', () => {\n    const { counter } = factory({\n      stubActions: false,\n      createSpy: (fn) => vi.fn(fn).mockImplementation(() => {}),\n    })\n    counter.increment()\n    expect(counter.increment).toHaveBeenCalledTimes(1)\n    expect(counter.n).toBe(0)\n\n    counter.increment.mockRestore()\n    counter.increment()\n    expect(counter.increment).toHaveBeenCalledTimes(1)\n    expect(counter.n).toBe(1)\n  })\n})\n"
  },
  {
    "path": "packages/testing/src/restoreGetters.spec.ts",
    "content": "import { defineStore } from 'pinia'\nimport { computed, ref } from 'vue'\nimport { describe, expect, it } from 'vitest'\nimport { createTestingPinia } from './testing'\nimport { restoreGetter } from './restoreGetters'\n\ndescribe('restoreGetters', () => {\n  it('allows overriding getters', () => {\n    const useStore = defineStore('lol', {\n      state: () => ({ n: 0 }),\n      getters: {\n        double: (state) => state.n * 2,\n      },\n    })\n    const pinia = createTestingPinia()\n    const store = useStore(pinia)\n\n    store.double = 3\n    expect(store.double).toBe(3)\n    restoreGetter(store, 'double')\n    expect(store.double).toBe(0)\n  })\n\n  tds(() => {\n    const s1 = defineStore('s1', () => {\n      const n = ref(0)\n      const double = computed(() => n.value * 2)\n      return { n, double }\n    })()\n\n    const s2 = defineStore('s2', {\n      state: () => ({ n: 0 }),\n      getters: {\n        double: (state) => state.n * 2,\n      },\n    })()\n\n    restoreGetter(s1, 'double')\n    restoreGetter(s2, 'double')\n\n    // @ts-expect-error: not a getter\n    restoreGetter(s1, 'n')\n    // @ts-expect-error: not a getter\n    restoreGetter(s2, 'n')\n  })\n})\n\nfunction tds(_fn: Function) {}\n"
  },
  {
    "path": "packages/testing/src/restoreGetters.ts",
    "content": "import { Store, StateTree, _ExtractGettersFromSetupStore_Keys } from 'pinia'\n\n// TODO: more testing, document and release\n\nexport function restoreGetter<G>(\n  store: Store<string, StateTree, G, any>,\n  getter: keyof G\n): void\nexport function restoreGetter<SS>(\n  store: SS,\n  getter: _ExtractGettersFromSetupStore_Keys<SS>\n): void\nexport function restoreGetter<G>(store: Store, getter: any): void {\n  // @ts-expect-error: private api\n  store[getter] = undefined\n}\n"
  },
  {
    "path": "packages/testing/src/testing.spec.ts",
    "content": "import { describe, expect, it, vi } from 'vitest'\nimport { createTestingPinia, TestingOptions } from './testing'\nimport { createPinia, defineStore, setActivePinia, storeToRefs } from 'pinia'\nimport { mount } from '@vue/test-utils'\nimport { defineComponent, ref, computed } from 'vue'\n\ndescribe('Testing', () => {\n  const useCounter = defineStore('counter', {\n    state: () => ({ n: 0, doubleComputedCallCount: 0 }),\n    getters: {\n      double: (state) => {\n        // NOTE: not supposed to be done in a getter...\n        state.doubleComputedCallCount++\n        return state.n * 2\n      },\n      doublePlusOne(): number {\n        return this.double + 1\n      },\n    },\n    actions: {\n      increment(amount = 1) {\n        this.n += amount\n      },\n      decrement() {\n        this.n--\n      },\n      setValue(newValue: number) {\n        this.n = newValue\n      },\n    },\n  })\n\n  const useCounterSetup = defineStore('counter-setup', () => {\n    const n = ref(0)\n    const doubleComputedCallCount = ref(0)\n    const double = computed(() => {\n      doubleComputedCallCount.value++\n      return n.value * 2\n    })\n    const doublePlusOne = computed(() => double.value + 1)\n    function increment(amount = 1) {\n      n.value += amount\n    }\n    function decrement() {\n      n.value--\n    }\n    function setValue(newValue: number) {\n      n.value = newValue\n    }\n    function $reset() {\n      n.value = 0\n    }\n\n    return {\n      n,\n      doubleComputedCallCount,\n      double,\n      doublePlusOne,\n      increment,\n      decrement,\n      setValue,\n      $reset,\n    }\n  })\n\n  type CounterStore =\n    | ReturnType<typeof useCounter>\n    | ReturnType<typeof useCounterSetup>\n\n  const STORES_TO_TEST = {\n    'Options Store': useCounter,\n    'Setup Store': useCounterSetup,\n  }\n\n  const Counter = (useStore: () => CounterStore) =>\n    defineComponent({\n      setup() {\n        const counter = useStore()\n        return { counter }\n      },\n      template: `\n    <button @click=\"counter.increment()\">+1</button>\n    <span>{{ counter.n }}</span>\n    <button @click=\"counter.increment(10)\">+10</button>\n    `,\n    })\n\n  function factory(\n    options?: TestingOptions,\n    useStore: () => CounterStore = useCounter\n  ) {\n    const wrapper = mount(Counter(useStore), {\n      global: {\n        plugins: [createTestingPinia(options)],\n      },\n    })\n\n    const counter = useStore()\n\n    return { wrapper, counter }\n  }\n\n  for (const name in STORES_TO_TEST) {\n    const useStore = STORES_TO_TEST[name as keyof typeof STORES_TO_TEST]\n\n    describe(name, () => {\n      describe('actions', () => {\n        it(`spies with no config with ${name}`, () => {\n          const { counter, wrapper } = factory(undefined, useStore)\n\n          counter.increment()\n          expect(counter.n).toBe(0)\n          expect(counter.increment).toHaveBeenCalledTimes(1)\n          expect(counter.increment).toHaveBeenLastCalledWith()\n\n          counter.increment(5)\n          expect(counter.n).toBe(0)\n          expect(counter.increment).toHaveBeenCalledTimes(2)\n          expect(counter.increment).toHaveBeenLastCalledWith(5)\n\n          wrapper.findAll('button')[0].trigger('click')\n          expect(counter.n).toBe(0)\n          expect(counter.increment).toHaveBeenCalledTimes(3)\n          expect(counter.increment).toHaveBeenLastCalledWith()\n\n          wrapper.findAll('button')[1].trigger('click')\n          expect(counter.n).toBe(0)\n          expect(counter.increment).toHaveBeenCalledTimes(4)\n          expect(counter.increment).toHaveBeenLastCalledWith(10)\n        })\n\n        it(`can execute actions with ${name}`, () => {\n          const { counter, wrapper } = factory({ stubActions: false }, useStore)\n\n          counter.increment()\n          expect(counter.n).toBe(1)\n          expect(counter.increment).toHaveBeenCalledTimes(1)\n          expect(counter.increment).toHaveBeenLastCalledWith()\n\n          counter.increment(5)\n          expect(counter.n).toBe(6)\n          expect(counter.increment).toHaveBeenCalledTimes(2)\n          expect(counter.increment).toHaveBeenLastCalledWith(5)\n\n          wrapper.findAll('button')[0].trigger('click')\n          expect(counter.n).toBe(7)\n          expect(counter.increment).toHaveBeenCalledTimes(3)\n          expect(counter.increment).toHaveBeenLastCalledWith()\n\n          wrapper.findAll('button')[1].trigger('click')\n          expect(counter.n).toBe(17)\n          expect(counter.increment).toHaveBeenCalledTimes(4)\n          expect(counter.increment).toHaveBeenLastCalledWith(10)\n        })\n      })\n    })\n\n    describe('builtins', () => {\n      it(`spies $patch calls in ${name}`, () => {\n        const { counter } = factory(undefined, useStore)\n\n        expect(counter.n).toBe(0)\n        expect(counter.$patch).toHaveBeenCalledTimes(0)\n        counter.$patch({ n: 1 })\n        expect(counter.$patch).toHaveBeenCalledTimes(1)\n        expect(counter.$patch).toHaveBeenLastCalledWith({ n: 1 })\n        expect(counter.n).toBe(1)\n      })\n\n      it(`can stub $patch calls ${name}`, () => {\n        const { counter } = factory({ stubPatch: true }, useStore)\n\n        expect(counter.n).toBe(0)\n        expect(counter.$patch).toHaveBeenCalledTimes(0)\n        counter.$patch({ n: 1 })\n        expect(counter.$patch).toHaveBeenCalledTimes(1)\n        expect(counter.$patch).toHaveBeenLastCalledWith({ n: 1 })\n        expect(counter.n).toBe(0)\n      })\n\n      it(`ignores $reset ${name}`, () => {\n        const { counter } = factory(undefined, useStore)\n\n        counter.n = 5\n        counter.$reset()\n        expect(counter.n).toBe(0)\n      })\n\n      it(`can stub $reset calls ${name}`, () => {\n        const { counter } = factory({ stubReset: true }, useStore)\n\n        counter.n = 5\n        counter.$reset()\n        expect(counter.n).toBe(5)\n      })\n    })\n\n    describe('plugins', () => {\n      it('executes plugins', () => {\n        const { counter, wrapper } = factory(\n          {\n            plugins: [() => ({ pluginN: 0 })],\n          },\n          useStore\n        )\n\n        expect(counter.pluginN).toBe(0)\n        expect(wrapper.vm.counter.pluginN).toBe(0)\n      })\n\n      it('executes plugins with fakeApp', () => {\n        const pinia = createTestingPinia({\n          plugins: [() => ({ pluginN: 0 })],\n          fakeApp: true,\n        })\n\n        const counter = useStore(pinia)\n\n        expect(counter.pluginN).toBe(0)\n        expect(pinia.app).toHaveProperty('mount', expect.any(Function))\n      })\n\n      it('actions are stubbed even when replaced by other plugins', () => {\n        const spy = vi.fn()\n        const { counter } = factory(\n          {\n            plugins: [\n              ({ store }) => {\n                const { increment } = store.increment\n                store.increment = spy\n                spy.mockImplementation(increment)\n              },\n            ],\n          },\n          useStore\n        )\n\n        counter.increment()\n        counter.increment(5)\n        expect(counter.n).toBe(0)\n        expect(counter.increment).toHaveBeenCalledTimes(2)\n        expect(counter.increment).toHaveBeenLastCalledWith(5)\n        // the actual spy is never called because we stub the action\n        expect(spy).toHaveBeenCalledTimes(0)\n      })\n\n      it('pass through replaced actions in plugins', () => {\n        const spy = vi.fn()\n        const { counter } = factory(\n          {\n            stubActions: false,\n            plugins: [\n              ({ store }) => {\n                const { increment } = store.increment\n                store.increment = spy\n                spy.mockImplementation(increment)\n              },\n            ],\n          },\n          useStore\n        )\n\n        counter.increment()\n        counter.increment(5)\n        expect(counter.n).toBe(0)\n        expect(counter.increment).toHaveBeenCalledTimes(2)\n        expect(counter.increment).toHaveBeenLastCalledWith(5)\n        expect(spy).toHaveBeenCalledTimes(2)\n        expect(spy).toHaveBeenLastCalledWith(5)\n      })\n\n      it('can override getters added in plugins', () => {\n        const pinia = createTestingPinia({\n          plugins: [\n            ({ store }) => {\n              store.triple = computed(() => store.n * 3)\n            },\n          ],\n        })\n\n        const store = useStore(pinia)\n        store.n++\n        // @ts-expect-error: non declared\n        expect(store.triple).toBe(3)\n        // once the getter is overridden, it stays\n        // @ts-expect-error: non declared\n        store.triple = 10\n        // @ts-expect-error: non declared\n        expect(store.triple).toBe(10)\n        store.n++\n        // @ts-expect-error: non declared\n        expect(store.triple).toBe(10)\n        // it can be set to undefined again to reset\n        // @ts-expect-error\n        store.triple = undefined\n        // @ts-expect-error: non declared\n        expect(store.triple).toBe(6)\n        store.n++\n        // @ts-expect-error: non declared\n        expect(store.triple).toBe(9)\n      })\n    })\n\n    describe('getters', () => {\n      it('allows overriding getters', () => {\n        const pinia = createTestingPinia()\n        const store = useStore(pinia)\n\n        // console.log('is same', d === toRaw(store).double._computed)\n\n        store.n++\n        expect(store.double).toBe(2)\n        // once the getter is overridden, it stays\n        store.double = 3\n        expect(store.double).toBe(3)\n        expect(store.doublePlusOne).toBe(4)\n        store.n++\n        expect(store.double).toBe(3)\n        expect(store.doublePlusOne).toBe(4)\n        // it can be set to undefined again to reset\n        // @ts-expect-error\n        store.double = undefined\n        expect(store.n).toBe(2)\n        expect(store.double).toBe(4)\n        expect(store.doublePlusOne).toBe(5)\n        // it works again\n        store.n++\n        expect(store.n).toBe(3)\n        expect(store.double).toBe(6)\n        expect(store.doublePlusOne).toBe(7)\n      })\n    })\n\n    // https://github.com/vuejs/pinia/issues/2913\n    it('does not compute getters immediately with storeToRefs', () => {\n      const pinia = createTestingPinia()\n      const store = useStore(pinia)\n\n      expect(store.doubleComputedCallCount).toBe(0)\n      storeToRefs(store)\n      expect(store.doubleComputedCallCount).toBe(0)\n    })\n\n    describe('selective action stubbing', () => {\n      it('stubs only actions in array', () => {\n        setActivePinia(\n          createTestingPinia({\n            stubActions: ['increment', 'setValue'],\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        // Actions in array should be stubbed (not execute)\n        store.increment()\n        expect(store.n).toBe(0) // Should not change\n        expect(store.increment).toHaveBeenCalledTimes(1)\n\n        store.setValue(42)\n        expect(store.n).toBe(0) // Should not change\n        expect(store.setValue).toHaveBeenCalledTimes(1)\n        expect(store.setValue).toHaveBeenLastCalledWith(42)\n\n        // Actions not in array should execute normally but still be spied\n        store.decrement()\n        expect(store.n).toBe(-1) // Should change\n        expect(store.decrement).toHaveBeenCalledTimes(1)\n      })\n\n      it('handles empty array (same as false)', () => {\n        setActivePinia(\n          createTestingPinia({\n            stubActions: [],\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        // All actions should execute normally\n        store.increment()\n        expect(store.n).toBe(1) // Should change\n        expect(store.increment).toHaveBeenCalledTimes(1)\n\n        store.setValue(42)\n        expect(store.n).toBe(42) // Should change\n        expect(store.setValue).toHaveBeenCalledTimes(1)\n      })\n\n      it('handles non-existent action names gracefully', () => {\n        setActivePinia(\n          createTestingPinia({\n            stubActions: ['increment', 'nonExistentAction'],\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        // Should work normally despite non-existent action in array\n        store.increment()\n        expect(store.n).toBe(0) // Should not change\n        expect(store.increment).toHaveBeenCalledTimes(1)\n\n        store.setValue(42)\n        expect(store.n).toBe(42) // Should change (not in array)\n        expect(store.setValue).toHaveBeenCalledTimes(1)\n      })\n\n      it('stubs actions based on function predicate', () => {\n        setActivePinia(\n          createTestingPinia({\n            stubActions: (actionName) =>\n              actionName.startsWith('set') || actionName === 'decrement',\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        // setValue should be stubbed (starts with 'set')\n        store.setValue(42)\n        expect(store.n).toBe(0) // Should not change\n        expect(store.setValue).toHaveBeenCalledTimes(1)\n\n        // increment should execute (doesn't match predicate)\n        store.increment()\n        expect(store.n).toBe(1) // Should change\n        expect(store.increment).toHaveBeenCalledTimes(1)\n\n        // decrement should be stubbed (matches predicate)\n        store.decrement()\n        expect(store.n).toBe(1) // Should not change (stubbed)\n        expect(store.decrement).toHaveBeenCalledTimes(1)\n      })\n\n      it('function predicate receives correct store instance', () => {\n        const predicateSpy = vi.fn(() => false)\n\n        setActivePinia(\n          createTestingPinia({\n            stubActions: predicateSpy,\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        expect(predicateSpy).toHaveBeenCalledWith('increment', store)\n      })\n\n      it('can stub all actions (default)', () => {\n        setActivePinia(\n          createTestingPinia({\n            stubActions: true,\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        store.increment()\n        expect(store.n).toBe(0) // Should not change\n        expect(store.increment).toHaveBeenCalledTimes(1)\n\n        store.setValue(42)\n        expect(store.n).toBe(0) // Should not change\n        expect(store.setValue).toHaveBeenCalledTimes(1)\n      })\n\n      it('can not stub any action', () => {\n        setActivePinia(\n          createTestingPinia({\n            stubActions: false,\n            createSpy: vi.fn,\n          })\n        )\n\n        const store = useStore()\n\n        store.increment()\n        expect(store.n).toBe(1) // Should change\n        expect(store.increment).toHaveBeenCalledTimes(1)\n\n        store.setValue(42)\n        expect(store.n).toBe(42) // Should change\n        expect(store.setValue).toHaveBeenCalledTimes(1)\n      })\n    })\n  }\n\n  it('works with no actions', () => {\n    const useEmpty = defineStore('empty', {})\n\n    const Empty = defineComponent({\n      setup() {\n        const empty = useEmpty()\n        return { empty }\n      },\n      template: `{{ empty.$id }}`,\n    })\n\n    const wrapper = mount(Empty, {\n      global: {\n        plugins: [createTestingPinia()],\n      },\n    })\n\n    expect(wrapper.text()).toBe('empty')\n  })\n\n  it('bypass useStore(pinia)', () => {\n    const realPinia = createPinia()\n    const { counter } = factory()\n\n    // it will actually use the testing pinia instead of the real one\n    const counterWithRealPinia = useCounter(realPinia)\n\n    expect(counter.n).toBe(0)\n    expect(counterWithRealPinia.n).toBe(0)\n    counter.n++\n    expect(counter.n).toBe(1)\n    expect(counterWithRealPinia.n).toBe(1)\n  })\n\n  it('works with nested stores', () => {\n    const useA = defineStore('a', () => {\n      const n = ref(0)\n      return { n }\n    })\n\n    const useB = defineStore('b', () => {\n      const a = useA()\n      const n = ref(0)\n      const doubleA = computed(() => a.n * 2)\n      return { n, doubleA }\n    })\n\n    const pinia = createTestingPinia()\n    setActivePinia(pinia)\n\n    const b = useB()\n    const a = useA()\n\n    expect(a.n).toBe(0)\n    a.n++\n    expect(b.doubleA).toBe(2)\n    expect(pinia.state.value).toEqual({\n      a: { n: 1 },\n      b: { n: 0 },\n    })\n  })\n})\n"
  },
  {
    "path": "packages/testing/src/testing.ts",
    "content": "import { computed, createApp, isReactive, isRef, toRaw, triggerRef } from 'vue'\nimport type { App, ComputedRef, WritableComputedRef } from 'vue'\nimport {\n  type Pinia,\n  type PiniaPlugin,\n  setActivePinia,\n  createPinia,\n  type StateTree,\n  type _DeepPartial,\n  type PiniaPluginContext,\n  type StoreGeneric,\n} from 'pinia'\n// NOTE: the implementation type is correct and contains up to date types\n// while the other types hide internal properties\nimport type { ComputedRefImpl } from '@vue/reactivity'\n\nexport interface TestingOptions {\n  /**\n   * Allows defining a partial initial state of all your stores. This state gets applied after a store is created,\n   * allowing you to only set a few properties that are required in your test.\n   */\n  initialState?: StateTree\n\n  /**\n   * Plugins to be installed before the testing plugin. Add any plugins used in\n   * your application that will be used while testing.\n   */\n  plugins?: PiniaPlugin[]\n\n  /**\n   * When set to false, actions are only spied, but they will still get executed. When\n   * set to true, **all** actions will be replaced with spies, resulting in their code\n   * not being executed. When set to an array of action names, only those actions\n   * will be stubbed. When set to a function, it will be called for each action with\n   * the action name and store instance, and should return true to stub the action.\n   *\n   * NOTE: when providing `createSpy()`,\n   * it will **only** make the `fn` argument `undefined`. You still have to\n   * handle this in `createSpy()`.\n   *\n   * @default `true`\n   */\n  stubActions?:\n    | boolean\n    | string[]\n    | ((actionName: string, store: StoreGeneric) => boolean)\n\n  /**\n   * When set to true, calls to `$patch()` won't change the state. Defaults to\n   * false. NOTE: when providing `createSpy()`, it will **only** make the `fn`\n   * argument `undefined`. You still have to handle this in `createSpy()`.\n   */\n  stubPatch?: boolean\n\n  /**\n   * When set to true, calls to `$reset()` won't change the state. Defaults to\n   * false.\n   */\n  stubReset?: boolean\n\n  /**\n   * Creates an empty App and calls `app.use(pinia)` with the created testing\n   * pinia. This allows you to use plugins while unit testing stores as\n   * plugins **will wait for pinia to be installed in order to be executed**.\n   * Defaults to false.\n   */\n  fakeApp?: boolean\n\n  /**\n   * Function used to create a spy for actions and `$patch()`. Pre-configured\n   * with `jest.fn` in Jest projects or `vi.fn` in Vitest projects if\n   * `globals: true` is set.\n   */\n  createSpy?: (fn?: (...args: any[]) => any) => (...args: any[]) => any\n}\n\n/**\n * Pinia instance specifically designed for testing. Extends a regular\n * `Pinia` instance with test specific properties.\n */\nexport interface TestingPinia extends Pinia {\n  /** App used by Pinia */\n  app: App\n}\n\ndeclare var vi:\n  | undefined\n  | {\n      fn: (fn?: (...args: any[]) => any) => (...args: any[]) => any\n    }\n\n/**\n * Creates a pinia instance designed for unit tests that **requires mocking**\n * the stores. By default, **all actions are mocked** and therefore not\n * executed. This allows you to unit test your store and components separately.\n * You can change this with the `stubActions` option. If you are using jest,\n * they are replaced with `jest.fn()`, otherwise, you must provide your own\n * `createSpy` option.\n *\n * @param options - options to configure the testing pinia\n * @returns a augmented pinia instance\n */\nexport function createTestingPinia({\n  initialState = {},\n  plugins = [],\n  stubActions = true,\n  stubPatch = false,\n  stubReset = false,\n  fakeApp = false,\n  createSpy: _createSpy,\n}: TestingOptions = {}): TestingPinia {\n  const pinia = createPinia()\n\n  // allow adding initial state\n  pinia._p.push(({ store }) => {\n    if (initialState[store.$id]) {\n      mergeReactiveObjects(store.$state, initialState[store.$id])\n    }\n  })\n\n  // bypass waiting for the app to be installed to ensure the action stubbing happens last\n  plugins.forEach((plugin) => pinia._p.push(plugin))\n\n  // allow computed to be manually overridden\n  pinia._p.push(WritableComputed)\n\n  const createSpy =\n    _createSpy ||\n    // @ts-ignore\n    (typeof jest !== 'undefined' && (jest.fn as typeof _createSpy)) ||\n    (typeof vi !== 'undefined' && vi.fn)\n  /* istanbul ignore if */\n  if (!createSpy) {\n    throw new Error(\n      '[@pinia/testing]: You must configure the `createSpy` option. See https://pinia.vuejs.org/cookbook/testing.html#Specifying-the-createSpy-function'\n    )\n  } else if (\n    typeof createSpy !== 'function' ||\n    // When users pass vi.fn() instead of vi.fn\n    // https://github.com/vuejs/pinia/issues/2896\n    'mockReturnValue' in createSpy\n  ) {\n    throw new Error(\n      '[@pinia/testing]: Invalid `createSpy` option. See https://pinia.vuejs.org/cookbook/testing.html#Specifying-the-createSpy-function'\n    )\n  }\n\n  // stub actions\n  pinia._p.push(({ store, options }) => {\n    Object.keys(options.actions).forEach((action) => {\n      if (action === '$reset') return\n\n      store[action] = shouldStubAction(stubActions, action, store)\n        ? createSpy()\n        : createSpy(store[action])\n    })\n\n    store.$patch = stubPatch ? createSpy() : createSpy(store.$patch)\n    store.$reset = stubReset ? createSpy() : createSpy(store.$reset)\n  })\n\n  if (fakeApp) {\n    const app = createApp({})\n    app.use(pinia)\n  }\n\n  pinia._testing = true\n\n  setActivePinia(pinia)\n\n  Object.defineProperty(pinia, 'app', {\n    configurable: true,\n    enumerable: true,\n    get(): App {\n      return this._a\n    },\n  })\n\n  return pinia as TestingPinia\n}\n\nfunction mergeReactiveObjects<T extends StateTree>(\n  target: T,\n  patchToApply: _DeepPartial<T>\n): T {\n  // no need to go through symbols because they cannot be serialized anyway\n  for (const key in patchToApply) {\n    if (!Object.hasOwn(patchToApply, key)) continue\n    const subPatch = patchToApply[key]\n    const targetValue = target[key]\n    if (\n      isPlainObject(targetValue) &&\n      isPlainObject(subPatch) &&\n      Object.hasOwn(target, key) &&\n      !isRef(subPatch) &&\n      !isReactive(subPatch)\n    ) {\n      target[key] = mergeReactiveObjects(targetValue, subPatch)\n    } else {\n      // @ts-expect-error: subPatch is a valid value\n      target[key] =\n        //\n        subPatch\n    }\n  }\n\n  return target\n}\n\nfunction isPlainObject<S extends StateTree>(value: S | unknown): value is S\nfunction isPlainObject(\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  o: any\n): o is StateTree {\n  return (\n    o &&\n    typeof o === 'object' &&\n    Object.prototype.toString.call(o) === '[object Object]' &&\n    typeof o.toJSON !== 'function'\n  )\n}\n\nfunction isComputed<T>(\n  v: ComputedRef<T> | WritableComputedRef<T> | unknown\n): v is (ComputedRef<T> | WritableComputedRef<T>) & ComputedRefImpl<T> {\n  return !!v && isRef(v) && 'effect' in v\n}\n\nfunction WritableComputed({ store }: PiniaPluginContext) {\n  const rawStore = toRaw(store)\n  for (const key in rawStore) {\n    const originalComputed = rawStore[key]\n    if (isComputed(originalComputed)) {\n      const originalFn = originalComputed.fn\n      // override the computed with a new one\n      const overriddenFn = () =>\n        // @ts-expect-error: internal cached value\n        originalComputed._value\n      // originalComputed.fn = overriddenFn\n\n      rawStore[key] = computed<unknown>({\n        get() {\n          return originalComputed.value\n        },\n        set(newValue) {\n          // reset the computed to its original value by setting it to its initial state\n          if (newValue === undefined) {\n            originalComputed.fn = originalFn\n            // @ts-expect-error: private api to remove the current cached value\n            delete originalComputed._value\n            // @ts-expect-error: private api to force the recomputation\n            originalComputed._dirty = true\n          } else {\n            originalComputed.fn = overriddenFn\n            // @ts-expect-error: private api\n            originalComputed._value = newValue\n          }\n          // this allows to trigger the original computed in setup stores\n          triggerRef(originalComputed)\n        },\n      })\n    }\n  }\n}\n\n/**\n * Should the given action be stubbed?\n *\n * @param stubActions - config option\n * @param action - action name\n * @param store - Store instance\n */\nfunction shouldStubAction(\n  stubActions: TestingOptions['stubActions'],\n  action: string,\n  store: StoreGeneric\n): boolean {\n  if (typeof stubActions === 'boolean') {\n    return stubActions\n  } else if (Array.isArray(stubActions)) {\n    return stubActions.includes(action)\n  } else if (typeof stubActions === 'function') {\n    return stubActions(action, store)\n  }\n  return false\n}\n"
  },
  {
    "path": "packages/testing/tsconfig.build.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"files\": [\"./src/**/*.ts\"],\n  \"exclude\": [\"./src/**/*.spec.ts\"],\n  \"compilerOptions\": {\n    \"rootDir\": \"../..\",\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"strict\": true\n  }\n}\n"
  },
  {
    "path": "packages/testing/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown'\n\nexport default defineConfig({\n  entry: ['src/index.ts'],\n  format: ['esm'],\n  skipNodeModulesBundle: true,\n  dts: true,\n  exports: true,\n})\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - 'packages/*'\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"extends\": [\"github>posva/renovate-config\"]\n}\n"
  },
  {
    "path": "scripts/docs-check.sh",
    "content": "#!/bin/bash\n\n# check if doc files changes for netlify\n# needed because we cannot use && in netlify.toml\n\n# exit 0 will skip the build while exit 1 will build\n\n# - check any change in docs\n# - check for new version of vite related deps\n# - changes in netlify conf\n# - a commit message that starts with docs like docs: ... or docs(nuxt): ...\n\ngit diff --quiet 'HEAD^' HEAD ./packages/docs/ && ! git diff 'HEAD^' HEAD ./pnpm-lock.yaml | grep --quiet vitepress && git diff --quiet 'HEAD^' HEAD netlify.toml && ! git log -1 --pretty=%B | grep '^docs'\n"
  },
  {
    "path": "scripts/release.mjs",
    "content": "import fs from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { dirname, join, relative } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport minimist from 'minimist'\nimport chalk from 'chalk'\nimport semver from 'semver'\nimport prompts from '@posva/prompts'\nimport { execa } from 'execa'\nimport pSeries from 'p-series'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nconst args = minimist(process.argv.slice(2))\nconst {\n  skipBuild,\n  tag: optionTag,\n  dry: isDryRun,\n  skipCleanCheck: skipCleanGitCheck,\n  noDepsUpdate,\n  noPublish,\n  noLockUpdate,\n  all: skipChangeCheck,\n} = args\n\nif (args.h || args.help) {\n  console.log(\n    `\nUsage: node release.mjs [flags]\n       node release.mjs [ -h | --help ]\n\nFlags:\n  --skipBuild         Skip building packages\n  --tag               Publish under a given npm dist tag\n  --dry               Dry run\n  --skipCleanCheck    Skip checking if the git repo is clean\n  --noDepsUpdate      Skip updating dependencies in package.json files\n  --noPublish         Skip publishing packages\n  --noLockUpdate      Skips updating the lock with \"pnpm install\"\n  --all               Skip checking if the packages have changed since last release\n`.trim()\n  )\n  process.exit(0)\n}\n\n// const preId =\n//   args.preId ||\n//   (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])\nconst EXPECTED_BRANCH = 'v3'\n// this package will use tags like v1.0.0 while the rest will use the full package name like @pinia/testing@1.0.0\nconst MAIN_PKG_NAME = 'pinia'\n// whether the main package is at the root of the mono repo or this is not a mono repo\nconst IS_MAIN_PKG_ROOT = false\n// array of folders of packages to release\nconst PKG_FOLDERS = [\n  // comment for multiline format\n  join(__dirname, '../packages/pinia'),\n  join(__dirname, '../packages/testing'),\n  join(__dirname, '../packages/nuxt'),\n]\n\n// files to add and commit after building a new version\nconst FILES_TO_COMMIT = [\n  // comment for multiline format\n  'packages/*/package.json',\n  'packages/*/CHANGELOG.md',\n]\n\n/**\n * @type {typeof execa}\n */\nconst run = (bin, args, opts = {}) =>\n  execa(bin, args, { stdio: 'inherit', ...opts })\n/**\n * @param bin {string}\n * @param args {string[]}\n * @param opts {import('execa').Options}\n */\nconst dryRun = async (bin, args, opts = {}) =>\n  console.log(chalk.blue(`[dry-run] ${bin} ${args.join(' ')}`), opts)\nconst runIfNotDry = isDryRun ? dryRun : run\n\n/**\n * @param msg {string[]}\n */\nconst step = (...msg) => console.log(chalk.cyan(...msg))\n\nasync function main() {\n  if (!skipCleanGitCheck) {\n    const isDirtyGit = !!(\n      await run('git', ['status', '--porcelain'], { stdio: 'pipe' })\n    ).stdout\n\n    if (isDirtyGit) {\n      console.log(chalk.red(`Git repo isn't clean.`))\n      return\n    }\n\n    const currentBranch = (\n      await run('git', ['branch', '--show-current'], { stdio: 'pipe' })\n    ).stdout\n\n    if (currentBranch !== EXPECTED_BRANCH) {\n      console.log(\n        chalk.red(\n          `You should be on branch \"${EXPECTED_BRANCH}\" but are on \"${currentBranch}\"`\n        )\n      )\n      return\n    }\n  } else {\n    console.log(chalk.bold.white(`Skipping git checks...`))\n  }\n\n  if (!skipCleanGitCheck) {\n    const isOutdatedRE = new RegExp(\n      `\\\\W${EXPECTED_BRANCH}\\\\W.*(?:fast-forwardable|local out of date)`,\n      'i'\n    )\n\n    const isOutdatedGit = isOutdatedRE.test(\n      (await run('git', ['remote', 'show', 'origin'], { stdio: 'pipe' })).stdout\n    )\n\n    if (isOutdatedGit) {\n      console.log(chalk.red(`Git branch is not in sync with remote`))\n      return\n    }\n  }\n\n  const changedPackages = await getChangedPackages(...PKG_FOLDERS)\n\n  if (!changedPackages.length) {\n    console.log(chalk.red(`No packages have changed since last release`))\n    return\n  }\n\n  if (isDryRun) {\n    console.log(`\\n${chalk.bold.blue('This is a dry run')}\\n`)\n  }\n\n  let packagesToRelease = changedPackages\n\n  // if there are more than one package, ask which ones to release\n  if (packagesToRelease.length > 1) {\n    // allow to select which packages\n    const { pickedPackages } = await prompts({\n      type: 'multiselect',\n      name: 'pickedPackages',\n      message: 'What packages do you want to release?',\n      instructions: false,\n      min: 1,\n      choices: changedPackages.map((pkg) => ({\n        title: pkg.name,\n        value: pkg.name,\n        selected: true,\n      })),\n    })\n\n    // const packagesToRelease = changedPackages\n    packagesToRelease = changedPackages.filter((pkg) =>\n      pickedPackages.includes(pkg.name)\n    )\n  }\n\n  step(\n    `Ready to release ${packagesToRelease.map(({ name }) => chalk.bold.white(name)).join(', ')}`\n  )\n\n  const pkgWithVersions = await pSeries(\n    packagesToRelease.map(({ name, path, pkg, relativePath }) => async () => {\n      let { version } = pkg\n\n      const prerelease = semver.prerelease(version)\n      const preId = prerelease && prerelease[0]\n\n      const versionIncrements = [\n        'patch',\n        'minor',\n        'major',\n        ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : []),\n      ]\n\n      const betaVersion = semver.inc(version, 'prerelease', 'beta')\n\n      const { release } = await prompts({\n        type: 'select',\n        name: 'release',\n        message: `Select release type for ${chalk.bold.white(name)}`,\n        choices: versionIncrements\n          .map((release) => {\n            const newVersion = semver.inc(version, release, preId)\n            return {\n              value: newVersion,\n              title: `${release}: ${name} (${newVersion})`,\n            }\n          })\n          .concat(\n            optionTag === 'beta'\n              ? [\n                  {\n                    title: `beta: ${name} (${betaVersion})`,\n                    value: betaVersion,\n                  },\n                ]\n              : []\n          )\n          .concat([{ value: 'custom', title: 'custom' }]),\n      })\n\n      console.log(release)\n\n      if (release === 'custom') {\n        version = (\n          await prompts({\n            type: 'text',\n            name: 'version',\n            message: `Input custom version (${chalk.bold.white(name)})`,\n            initial: version,\n          })\n        ).version\n      } else {\n        version = release\n      }\n\n      if (!semver.valid(version)) {\n        throw new Error(`invalid target version: ${version}`)\n      }\n\n      return { name, path, relativePath, version, pkg }\n    })\n  )\n\n  // put the main package first as others might depend on it\n  const mainPkgIndex = packagesToRelease.find(\n    ({ name }) => name === MAIN_PKG_NAME\n  )\n  if (mainPkgIndex > 0) {\n    packagesToRelease.unshift(packagesToRelease.splice(mainPkgIndex, 1)[0])\n  }\n\n  const { yes: isReleaseConfirmed } = await prompts({\n    type: 'confirm',\n    name: 'yes',\n    message: `Releasing \\n${pkgWithVersions\n      .map(\n        ({ name, version }) =>\n          `  · ${chalk.white(name)}: ${chalk.yellow.bold(`v${version}`)}`\n      )\n      .join('\\n')}\\nConfirm?`,\n  })\n\n  if (!isReleaseConfirmed) {\n    return\n  }\n\n  step('\\nUpdating versions in package.json files...')\n  await updateVersions(pkgWithVersions)\n\n  if (!noLockUpdate) {\n    step('\\nUpdating lock...')\n    await runIfNotDry(`pnpm`, ['install'])\n  }\n\n  step('\\nGenerating changelogs...')\n  await Promise.all(\n    pkgWithVersions.map(async (pkg) => {\n      step(` -> ${pkg.name} (${pkg.path})`)\n      const changelogExists = existsSync(join(pkg.path, 'CHANGELOG.md'))\n\n      if (!changelogExists) {\n        console.log(chalk.yellow(`No CHANGELOG.md found in ${pkg.name}`))\n      }\n\n      await runIfNotDry(\n        `pnpm`,\n        [\n          'exec',\n          'conventional-changelog',\n          '-i',\n          'CHANGELOG.md',\n          '--same-file',\n          '-p',\n          'conventionalcommits',\n          '-r',\n          changelogExists ? '1' : '0',\n          '--commit-path',\n          // in the case of a mono repo with the main package at the root\n          // using `.` would add all the changes of all packages\n          ...(pkg.name === MAIN_PKG_NAME && IS_MAIN_PKG_ROOT\n            ? [join(pkg.path, 'src'), join(pkg.path, 'package.json')]\n            : ['.']),\n          ...(pkg.name === MAIN_PKG_NAME ? [] : ['--lerna-package', pkg.name]),\n          ...(pkg.name === MAIN_PKG_NAME\n            ? []\n            : ['--tag-prefix', `${pkg.name}@`]),\n        ],\n        { cwd: pkg.path }\n      )\n      await runIfNotDry(`pnpm`, ['exec', 'oxfmt', 'CHANGELOG.md'], {\n        cwd: pkg.path,\n      })\n      // NOTE: pnpm publish automatically copies the LICENSE file\n    })\n  )\n\n  const { yes: isChangelogCorrect } = await prompts({\n    type: 'confirm',\n    name: 'yes',\n    message: 'Are the changelogs correct?',\n    initial: true,\n  })\n\n  if (!isChangelogCorrect) {\n    return\n  }\n\n  step('\\nBuilding all packages...')\n  if (!skipBuild) {\n    await runIfNotDry('pnpm', ['run', 'build'])\n  } else {\n    console.log(`(skipped)`)\n  }\n\n  const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })\n  if (stdout) {\n    step('\\nCommitting changes...')\n    await runIfNotDry('git', ['add', ...FILES_TO_COMMIT])\n    await runIfNotDry('git', [\n      'commit',\n      '-m',\n      `release: ${pkgWithVersions.map(({ name, version }) => `${name}@${version}`).join(' ')}`,\n    ])\n  } else {\n    console.log('No changes to commit.')\n  }\n\n  step('\\nCreating tags...')\n  const versionsToPush = []\n  for (const pkg of pkgWithVersions) {\n    const tagName =\n      pkg.name === MAIN_PKG_NAME\n        ? `v${pkg.version}`\n        : `${pkg.name}@${pkg.version}`\n\n    versionsToPush.push(`refs/tags/${tagName}`)\n    await runIfNotDry('git', [\n      'tag',\n      '-a',\n      `${tagName}`,\n      '-m',\n      `Release ${tagName}`,\n    ])\n  }\n\n  if (!noPublish) {\n    step('\\nPublishing packages...')\n    for (const pkg of pkgWithVersions) {\n      await publishPackage(pkg)\n    }\n\n    step('\\nPushing to Github...')\n    await runIfNotDry('git', ['push', 'origin', ...versionsToPush])\n    await runIfNotDry('git', ['push'])\n  } else {\n    console.log(chalk.bold.white(`Skipping publishing...`))\n  }\n}\n\n/**\n *\n * @param packageList {{ name: string; path: string; version: string, pkg: any }[]}\n */\nasync function updateVersions(packageList) {\n  return Promise.all(\n    packageList.map(({ pkg, version, path, name }) => {\n      pkg.version = version\n      if (!noDepsUpdate) {\n        updateDeps(pkg, 'dependencies', packageList)\n        updateDeps(pkg, 'peerDependencies', packageList)\n      }\n      const content = `${JSON.stringify(pkg, null, 2)}\\n`\n      return isDryRun\n        ? dryRun('write', [name], {\n            version: pkg.version,\n            dependencies: pkg.dependencies,\n            peerDependencies: pkg.peerDependencies,\n          })\n        : fs.writeFile(join(path, 'package.json'), content)\n    })\n  )\n}\n\nfunction updateDeps(pkg, depType, updatedPackages) {\n  const deps = pkg[depType]\n  if (!deps) return\n  step(`Updating ${chalk.bold(depType)} for ${chalk.bold.white(pkg.name)}...`)\n  Object.keys(deps).forEach((dep) => {\n    const updatedDep = updatedPackages.find((pkg) => pkg.name === dep)\n    // avoid updated peer deps that are external like @vue/devtools-api\n    if (dep && updatedDep) {\n      // skip any workspace reference, pnpm will handle it\n      if (deps[dep].startsWith('workspace:')) {\n        console.log(\n          chalk.yellow.dim(\n            `${pkg.name} -> ${depType} -> ${dep}@${deps[dep]} (skipped)`\n          )\n        )\n      } else {\n        console.log(\n          chalk.yellow(\n            `${pkg.name} -> ${depType} -> ${dep}@>=${updatedDep.version}`\n          )\n        )\n        deps[dep] = `>=${updatedDep.version}`\n      }\n    }\n  })\n}\n\nasync function publishPackage(pkg) {\n  step(`Publishing ${pkg.name}...`)\n\n  try {\n    await runIfNotDry(\n      'pnpm',\n      [\n        'publish',\n        ...(optionTag ? ['--tag', optionTag] : []),\n        ...(skipCleanGitCheck ? ['--no-git-checks'] : []),\n        '--access',\n        'public',\n        // only needed for branches other than main\n        '--publish-branch',\n        EXPECTED_BRANCH,\n      ],\n      {\n        cwd: pkg.path,\n        stdio: 'pipe',\n      }\n    )\n    console.log(\n      chalk.green(`Successfully published ${pkg.name}@${pkg.version}`)\n    )\n  } catch (e) {\n    if (e.stderr.match(/previously published/)) {\n      console.log(chalk.red(`Skipping already published: ${pkg.name}`))\n    } else {\n      throw e\n    }\n  }\n}\n\n/**\n * Get the last tag published for a package or null if there are no tags\n *\n * @param {string} pkgName - package name\n */\nasync function getLastTag(pkgName) {\n  try {\n    const { stdout } = await run(\n      'git',\n      [\n        'describe',\n        '--tags',\n        '--abbrev=0',\n        '--match',\n        pkgName === MAIN_PKG_NAME ? 'v*' : `${pkgName}@*`,\n      ],\n      {\n        stdio: 'pipe',\n      }\n    )\n\n    return stdout\n  } catch (error) {\n    console.log(\n      chalk.dim(\n        `Couldn't get \"${chalk.bold(pkgName)}\" last tag, using first commit...`\n      )\n    )\n\n    // 128 is the git exit code when there is nothing to describe\n    if (error.exitCode !== 128) {\n      console.error(error)\n    }\n    const { stdout } = await run(\n      'git',\n      ['rev-list', '--max-parents=0', 'HEAD'],\n      { stdio: 'pipe' }\n    )\n    return stdout\n  }\n}\n\n/**\n * Get the packages that have changed. Based on `lerna changed` but without lerna.\n *\n * @param {string[]} folders\n * @returns {Promise<{ name: string; path: string; relativePath: string; pkg: any; version: string; start: string }[]} a promise of changed packages\n */\nasync function getChangedPackages(...folders) {\n  const pkgs = await Promise.all(\n    folders.map(async (folder) => {\n      if (!(await fs.lstat(folder)).isDirectory()) {\n        console.warn(chalk.dim(`Skipping \"${folder}\" as it is not a directory`))\n        return null\n      }\n\n      const pkg = JSON.parse(\n        await fs.readFile(join(folder, 'package.json'), 'utf-8')\n      )\n      if (pkg.private) {\n        console.info(chalk.dim(`Skipping \"${pkg.name}\" it's private`))\n        return null\n      }\n\n      const lastTag = await getLastTag(pkg.name)\n\n      const { stdout: hasChanges } = await run(\n        'git',\n        [\n          'diff',\n          '--name-only',\n          lastTag,\n          '--',\n          // apparently {src,package.json} doesn't work\n          join(folder, 'src'),\n          // TODO: should not check dev deps and should compare to last tag changes\n          join(folder, 'package.json'),\n        ],\n        { stdio: 'pipe' }\n      )\n      const relativePath = relative(join(__dirname, '..'), folder)\n\n      if (hasChanges || skipChangeCheck) {\n        const changedFiles = hasChanges.split('\\n').filter(Boolean)\n        console.log(\n          chalk.dim.blueBright(\n            `Found ${changedFiles.length} changed files in \"${pkg.name}\" since last release (${lastTag})`\n          )\n        )\n        console.log(chalk.dim(`\"${changedFiles.join('\", \"')}\"`))\n\n        return {\n          path: folder,\n          relativePath,\n          name: pkg.name,\n          version: pkg.version,\n          pkg,\n          start: lastTag,\n        }\n      } else {\n        console.warn(\n          chalk.dim(\n            `Skipping \"${pkg.name}\" as it has no changes since last release (${lastTag})`\n          )\n        )\n        return null\n      }\n    })\n  )\n\n  return pkgs.filter((p) => p)\n}\n\nmain().catch((error) => {\n  console.error(error)\n  process.exit(1)\n})\n"
  },
  {
    "path": "scripts/verifyCommit.mjs",
    "content": "// @ts-check\nimport { readFileSync } from 'node:fs'\nimport path from 'node:path'\n\n// Define raw escape codes for colors\nconst reset = '\\x1b[0m'\nconst red = '\\x1b[31m'\nconst green = '\\x1b[32m'\nconst bgRedWhite = '\\x1b[41m\\x1b[37m'\n\nconst msgPath = path.resolve('.git/COMMIT_EDITMSG')\nconst msg = readFileSync(msgPath, 'utf-8').trim()\n\nconst commitRE =\n  /^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\\(.+\\))?: .{1,50}/\n\nif (!commitRE.test(msg)) {\n  console.log()\n  console.error(\n    `  ${bgRedWhite} ERROR ${reset} ${red}invalid commit message format.${reset}\\n\\n` +\n      `${red}  Proper commit message format is required for automated changelog generation. Examples:\\n\\n` +\n      `    ${green}feat: add disableRoot option${reset}\\n` +\n      `    ${green}fix(view): handle keep-alive with aborted navigations (close #28)${reset}\\n\\n` +\n      `${red}  See .github/commit-convention.md for more details.${reset}\\n`\n  )\n  process.exit(1)\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"include\": [\n    \"packages/pinia/src/global.d.ts\",\n    \"packages/*/src/**/*.ts\",\n    \"packages/*/__tests__/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"packages/test-vue-2\",\n    \"packages/pinia/__tests__/test-utils.ts\",\n    \"packages/pinia/test-dts\",\n    \"packages/playground\",\n    \"packages/online-playground\",\n    \"packages/nuxt\"\n  ],\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"rootDir\": \".\",\n    \"outDir\": \"dist\",\n    \"sourceMap\": false,\n    \"noEmit\": true,\n    \"paths\": {\n      \"@pinia/*\": [\"packages/*/src\"],\n      \"pinia\": [\"packages/pinia/src\"]\n    },\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"noUnusedLocals\": true,\n    \"strictNullChecks\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitThis\": true,\n    \"noImplicitReturns\": false,\n    \"strict\": true,\n    \"isolatedModules\": true,\n    \"experimentalDecorators\": true,\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"removeComments\": false,\n    \"jsx\": \"preserve\",\n    \"lib\": [\"esnext\", \"dom\"],\n    \"types\": [\"vitest\", \"node\", \"vite/client\"]\n  }\n}\n"
  },
  {
    "path": "vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config'\nimport { fileURLToPath, URL } from 'node:url'\nimport { resolve } from 'node:path'\n\nexport default defineConfig({\n  define: {\n    __DEV__: 'true',\n    __TEST__: 'true',\n    __BROWSER__: 'true',\n    __USE_DEVTOOLS__: 'false',\n  },\n  resolve: {\n    alias: [\n      {\n        find: /^@pinia\\/(.*?)$/,\n        replacement: fileURLToPath(\n          new URL('./packages/packages/$1/src', import.meta.url)\n        ),\n      },\n      {\n        find: /^pinia$/,\n        replacement: fileURLToPath(\n          new URL('./packages/pinia/src', import.meta.url)\n        ),\n      },\n    ],\n  },\n\n  test: {\n    include: ['src/**/*.{test,spec}.ts', '__tests__/**/*.{test,spec}.ts'],\n    setupFiles: [\n      fileURLToPath(\n        new URL('./packages/pinia/__tests__/vitest-setup.ts', import.meta.url)\n      ),\n    ],\n    environment: 'happy-dom',\n    fakeTimers: {\n      // easier to read, some date in 2001\n      now: 1_000_000_000_000,\n    },\n    typecheck: {\n      enabled: true,\n    },\n    coverage: {\n      enabled: true,\n      provider: 'v8',\n      reporter: ['text', 'lcovonly', 'html'],\n      include: [\n        'packages/pinia/src',\n        // FIXME: enable when nuxt tests are fixed\n        // 'packages/nuxt/src',\n        'packages/testing/src',\n      ],\n      exclude: [\n        '**/src/index.ts',\n        '**/*.test-d.ts',\n        'packages/pinia/src/devtools',\n        'packages/pinia/src/hmr.ts',\n      ],\n    },\n    projects: [\n      {\n        extends: true,\n        test: {\n          name: 'pinia',\n          root: './packages/pinia',\n        },\n      },\n      // FIXME: doesn't work since last nuxt test-utils update\n      // {\n      //   extends: true,\n      //   test: {\n      //     name: '@pinia/nuxt',\n      //     root: './packages/nuxt',\n      //     environment: 'node',\n      //     include: ['test/**/*.{spec,test}.ts'],\n      //   },\n      // },\n      {\n        extends: true,\n        test: {\n          name: '@pinia/testing',\n          root: './packages/testing',\n          globals: true,\n        },\n      },\n    ],\n  },\n})\n"
  }
]