[
  {
    "path": ".babelrc",
    "content": "{\n  \"plugins\": [ \"add-module-exports\" ],\n  \"presets\": [\n    [ \"env\", { \"modules\": false } ],\n    \"es2015\",\n    \"stage-2\"\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: marshallswain\n"
  },
  {
    "path": ".github/contributing.md",
    "content": "# Contributing to Feathers\n\nThank you for contributing to Feathers! :heart: :tada:\n\nThis repo is the main core and where most issues are reported. Feathers embraces modularity and is broken up across many repos. To make this easier to manage we currently use [Zenhub](https://www.zenhub.com/) for issue triage and visibility. They have a free browser plugin you can install so that you can see what is in flight at any time, but of course you also always see current issues in Github.\n\n## Report a bug\n\nBefore creating an issue please make sure you have checked out the docs, specifically the [FAQ](https://docs.feathersjs.com/help/faq.html) section. You might want to also try searching Github. It's pretty likely someone has already asked a similar question.\n\nIf you haven't found your answer please feel free to join our [slack channel](http://slack.feathersjs.com), create an issue on Github, or post on [Stackoverflow](http://stackoverflow.com) using the `feathers` or `feathersjs` tag. We try our best to monitor Stackoverflow but you're likely to get more immediate responses in Slack and Github.\n\nIssues can be reported in the [issue tracker](https://github.com/feathersjs/feathers/issues).  Since feathers combines many modules it can be hard for us to assess the root cause without knowing which modules are being used and what your configuration looks like, so **it helps us immensely if you can link to a simple example that reproduces your issue**.\n\n## Report a Security Concern\n\nWe take security very seriously at Feathers. We welcome any peer review of our 100% open source code to ensure nobody's Feathers app is ever compromised or hacked. As a web application developer you are responsible for any security breaches. We do our very best to make sure Feathers is as secure as possible by default.\n\nIn order to give the community time to respond and upgrade we strongly urge you report all security issues to us. Send one of the core team members a PM in [Slack](http://slack.feathersjs.com) or email us at hello@feathersjs.com with details and we will respond ASAP.\n\nFor full details refer to our [Security docs](https://docs.feathersjs.com/SECURITY.html).\n\n## Pull Requests\n\nWe :heart: pull requests and we're continually working to make it as easy as possible for people to contribute, including a [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) and a [common test suite](https://github.com/feathersjs/feathers-service-tests) for database adapters.\n\nWe prefer small pull requests with minimal code changes. The smaller they are the easier they are to review and merge. A core team member will pick up your PR and review it as soon as they can. They may ask for changes or reject your pull request. This is not a reflection of you as an engineer or a person. Please accept feedback graciously as we will also try to be sensitive when providing it.\n\nAlthough we generally accept many PRs they can be rejected for many reasons. We will be as transparent as possible but it may simply be that you do not have the same context or information regarding the roadmap that the core team members have. We value the time you take to put together any contributions so we pledge to always be respectful of that time and will try to be as open as possible so that you don't waste it. :smile:\n\n**All PRs (except documentation) should be accompanied with tests and pass the linting rules.**\n\n### Code style\n\nBefore running the tests from the `test/` folder `npm test` will run ESlint. You can check your code changes individually by running `npm run lint`.\n\n### ES6 compilation\n\nFeathers uses [Babel](https://babeljs.io/) to leverage the latest developments of the JavaScript language. All code and samples are currently written in ES2015. To transpile the code in this repository run\n\n> npm run compile\n\n__Note:__ `npm test` will run the compilation automatically before the tests.\n\n### Tests\n\n[Mocha](http://mochajs.org/) tests are located in the `test/` folder and can be run using the `npm run mocha` or `npm test` (with ESLint and code coverage) command.\n\n### Documentation\n\nFeathers documentation is contained in Markdown files in the [feathers-docs](https://github.com/feathersjs/feathers-docs) repository. To change the documentation submit a pull request to that repo, referencing any other PR if applicable, and the docs will be updated with the next release.\n\n## External Modules\n\nIf you're written something awesome for Feathers, the Feathers ecosystem, or using Feathers please add it to the [showcase](https://docs.feathersjs.com/why/showcase.html). You also might want to check out the [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) that can be used to scaffold plugins to be Feathers compliant from the start.\n\nIf you think it would be a good core module then please contact one of the Feathers core team members in [Slack](http://slack.feathersjs.com) and we can discuss whether it belongs in core and how to get it there. :beers:\n\n## 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, ethnicity, 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/issue_template.md",
    "content": "### Steps to reproduce\n\n(First please check that this issue is not already solved as [described\nhere](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#report-a-bug))\n\n- [ ] Tell us what broke. The more detailed the better.\n- [ ] If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc.\n\n### Expected behavior\nTell us what should happen\n\n### Actual behavior\nTell us what happens instead\n\n### System configuration\n\nTell us about the applicable parts of your setup.\n\n**Module versions** (especially the part that's not working):\n\n**NodeJS version**:\n\n**Operating System**:\n\n**Browser Version**:\n\n**React Native Version**:\n\n**Module Loader**:"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "### Summary\n\n(If you have not already please refer to the contributing guideline as [described\nhere](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#pull-requests))\n\n- [ ] Tell us about the problem your pull request is solving.\n- [ ] Are there any open issues that are related to this?\n- [ ] Is this PR dependent on PRs in other repos?\n\nIf so, please mention them to keep the conversations linked together.\n\n### Other Information\n\nIf there's anything else that's important and relevant to your pull\nrequest, mention that information here. This could include\nbenchmarks, or other information.\n\nYour PR will be reviewed by a core team member and they will work with you to get your changes merged in a timely manner. If merged your PR will automatically be added to the changelog in the next release.\n\nIf your changes involve documentation updates please mention that and link the appropriate PR in [feathers-docs](https://github.com/feathersjs/feathers-docs).\n\nThanks for contributing to Feathers! :heart:"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\n# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\n# Commenting this out is preferred by some people, see\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-\nnode_modules\n\n# Users Environment Variables\n.lock-wscript\n\n# The compiled/babelified modules\nlib/\n\n# Editor directories and files\n.idea\n.vscode/settings.json\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n/dist\n"
  },
  {
    "path": ".istanbul.yml",
    "content": "verbose: false\ninstrumentation:\n  root: ./src/\n  excludes:\n    - lib/\n  include-all-sources: true\nreporting:\n  print: summary\n  reports:\n    - html\n    - text\n    - lcov\n  watermarks:\n    statements: [50, 80]\n    lines: [50, 80]\n    functions: [50, 80]\n    branches: [50, 80]"
  },
  {
    "path": ".npmignore",
    "content": ".editorconfig\n.jshintrc\n.travis.yml\n.istanbul.yml\n.babelrc\n.idea/\ntest/\n!lib/\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js: node\ncache: yarn\naddons:\n  code_climate:\n    repo_token: 'your repo token'\n  firefox: \"51.0\"\nservices:\n  - xvfb\nnotifications:\n  email: false"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible Node.js debug attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"TS - Mocha Tests\",\n      \"program\": \"${workspaceFolder}/node_modules/mocha/bin/_mocha\",\n      \"args\": [\n        \"--require\",\n        \"ts-node/register\",\n        \"-u\",\n        \"bdd\",\n        \"--timeout\",\n        \"999999\",\n        \"--colors\",\n        \"--recursive\",\n        \"${workspaceFolder}/test/**/*.ts\"\n      ],\n      \"env\": {\n        \"TS_NODE_PROJECT\": \"tsconfig.test.json\"\n      },\n      \"internalConsoleOptions\": \"openOnSessionStart\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Launch Program\",\n      \"program\": \"${workspaceRoot}/lib/\",\n      \"cwd\": \"${workspaceRoot}\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"attach\",\n      \"name\": \"Attach to Process\",\n      \"port\": 5858\n    }\n  ]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n## [v1.0.0-pre.3](https://github.com/feathersjs/feathers-vuex/tree/v1.0.0-pre.3) (2017-08-26)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v1.0.0-pre.2...v1.0.0-pre.3)\n\n**Closed issues:**\n\n- \\[Proposal\\] Allow existing store modules to provide additional properties like the customActions, etc. [\\#36](https://github.com/feathersjs/feathers-vuex/issues/36)\n\n## [v1.0.0-pre.2](https://github.com/feathersjs/feathers-vuex/tree/v1.0.0-pre.2) (2017-08-24)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v1.0.0-pre.1...v1.0.0-pre.2)\n\n## [v1.0.0-pre.1](https://github.com/feathersjs/feathers-vuex/tree/v1.0.0-pre.1) (2017-08-22)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.8.0...v1.0.0-pre.1)\n\n**Closed issues:**\n\n- Update mutation not changing store or backend [\\#38](https://github.com/feathersjs/feathers-vuex/issues/38)\n- namespaces cannot be arrays for nested modules \\(supported by vuex\\) [\\#34](https://github.com/feathersjs/feathers-vuex/issues/34)\n- using deep-assign package [\\#15](https://github.com/feathersjs/feathers-vuex/issues/15)\n\n## [v0.8.0](https://github.com/feathersjs/feathers-vuex/tree/v0.8.0) (2017-08-18)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.7.2...v0.8.0)\n\n**Closed issues:**\n\n- How to setup vuex and vue-router to redirect when a store value is not set? [\\#37](https://github.com/feathersjs/feathers-vuex/issues/37)\n- OAuth with Google and feathers-vuex [\\#33](https://github.com/feathersjs/feathers-vuex/issues/33)\n\n**Merged pull requests:**\n\n- Support array namespaces for module nesting [\\#35](https://github.com/feathersjs/feathers-vuex/pull/35) ([jskrzypek](https://github.com/jskrzypek))\n\n## [v0.7.2](https://github.com/feathersjs/feathers-vuex/tree/v0.7.2) (2017-08-04)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.7.1...v0.7.2)\n\n**Closed issues:**\n\n- Mapped Action is not a function [\\#31](https://github.com/feathersjs/feathers-vuex/issues/31)\n- Real time updates stops working sometimes depending on the query [\\#28](https://github.com/feathersjs/feathers-vuex/issues/28)\n\n**Merged pull requests:**\n\n- Return Promise.reject for action path of services when catching an er… [\\#32](https://github.com/feathersjs/feathers-vuex/pull/32) ([phenrigomes](https://github.com/phenrigomes))\n\n## [v0.7.1](https://github.com/feathersjs/feathers-vuex/tree/v0.7.1) (2017-07-08)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.7.0...v0.7.1)\n\n**Closed issues:**\n\n- errorOnAuthenticate is not reset [\\#29](https://github.com/feathersjs/feathers-vuex/issues/29)\n- Hoping to support server paging parameters [\\#23](https://github.com/feathersjs/feathers-vuex/issues/23)\n\n**Merged pull requests:**\n\n- Fix 29 [\\#30](https://github.com/feathersjs/feathers-vuex/pull/30) ([mgesmundo](https://github.com/mgesmundo))\n\n## [v0.7.0](https://github.com/feathersjs/feathers-vuex/tree/v0.7.0) (2017-06-27)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.6.0...v0.7.0)\n\n**Merged pull requests:**\n\n- Remove “feathers” module completely [\\#27](https://github.com/feathersjs/feathers-vuex/pull/27) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.6.0](https://github.com/feathersjs/feathers-vuex/tree/v0.6.0) (2017-06-27)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.5.0...v0.6.0)\n\n**Merged pull requests:**\n\n- Allow customizing a service’s default store [\\#26](https://github.com/feathersjs/feathers-vuex/pull/26) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.5.0](https://github.com/feathersjs/feathers-vuex/tree/v0.5.0) (2017-06-27)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.4.2...v0.5.0)\n\n**Closed issues:**\n\n- Are feathers-reactive and RxJS dependencies necessary? [\\#22](https://github.com/feathersjs/feathers-vuex/issues/22)\n- Weird / destructive behaviour using mapActions service find between components [\\#21](https://github.com/feathersjs/feathers-vuex/issues/21)\n- removeItems action added after create when upgrading version from version 0.4.0 to 0.4.1 [\\#19](https://github.com/feathersjs/feathers-vuex/issues/19)\n\n**Merged pull requests:**\n\n- Remove all non-serializable data from the state [\\#25](https://github.com/feathersjs/feathers-vuex/pull/25) ([marshallswain](https://github.com/marshallswain))\n- Opt in to `autoRemove` [\\#24](https://github.com/feathersjs/feathers-vuex/pull/24) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.4.2](https://github.com/feathersjs/feathers-vuex/tree/v0.4.2) (2017-06-07)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.4.1...v0.4.2)\n\n**Closed issues:**\n\n- What is the benefit to convert data in feathers to vuex instead of accessing feathers services directly? [\\#18](https://github.com/feathersjs/feathers-vuex/issues/18)\n- items not being removed from the list - fix proposal [\\#12](https://github.com/feathersjs/feathers-vuex/issues/12)\n\n**Merged pull requests:**\n\n- QuickFix: Use idField for removal [\\#20](https://github.com/feathersjs/feathers-vuex/pull/20) ([cmeissl](https://github.com/cmeissl))\n\n## [v0.4.1](https://github.com/feathersjs/feathers-vuex/tree/v0.4.1) (2017-05-26)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.4.0...v0.4.1)\n\n**Closed issues:**\n\n- Is possible to upload a file using feathers-vuex? [\\#11](https://github.com/feathersjs/feathers-vuex/issues/11)\n- Integration with Nuxt [\\#8](https://github.com/feathersjs/feathers-vuex/issues/8)\n\n**Merged pull requests:**\n\n- Bugfix - add params to patch action service call [\\#14](https://github.com/feathersjs/feathers-vuex/pull/14) ([ndamjan](https://github.com/ndamjan))\n- fix item removal in addOrUpdateList \\(\\#12\\) [\\#13](https://github.com/feathersjs/feathers-vuex/pull/13) ([ndamjan](https://github.com/ndamjan))\n\n## [v0.4.0](https://github.com/feathersjs/feathers-vuex/tree/v0.4.0) (2017-05-01)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.3.1...v0.4.0)\n\n## [v0.3.1](https://github.com/feathersjs/feathers-vuex/tree/v0.3.1) (2017-05-01)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.3.0...v0.3.1)\n\n## [v0.3.0](https://github.com/feathersjs/feathers-vuex/tree/v0.3.0) (2017-05-01)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.2.2...v0.3.0)\n\n**Merged pull requests:**\n\n- Node: Keep functions out of Vuex state [\\#9](https://github.com/feathersjs/feathers-vuex/pull/9) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.2.2](https://github.com/feathersjs/feathers-vuex/tree/v0.2.2) (2017-04-28)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.2.1...v0.2.2)\n\n## [v0.2.1](https://github.com/feathersjs/feathers-vuex/tree/v0.2.1) (2017-04-18)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.2.0...v0.2.1)\n\n**Closed issues:**\n\n- `clearList` mutation behaves unexpectedly if `current` isn't defined [\\#5](https://github.com/feathersjs/feathers-vuex/issues/5)\n\n**Merged pull requests:**\n\n- Fix clearList unexpected behavior. Closes \\#5 [\\#6](https://github.com/feathersjs/feathers-vuex/pull/6) ([silvestreh](https://github.com/silvestreh))\n\n## [v0.2.0](https://github.com/feathersjs/feathers-vuex/tree/v0.2.0) (2017-04-18)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.1.1...v0.2.0)\n\n**Merged pull requests:**\n\n- Actions [\\#4](https://github.com/feathersjs/feathers-vuex/pull/4) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.1.1](https://github.com/feathersjs/feathers-vuex/tree/v0.1.1) (2017-04-18)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.1.0...v0.1.1)\n\n## [v0.1.0](https://github.com/feathersjs/feathers-vuex/tree/v0.1.0) (2017-04-18)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.5...v0.1.0)\n\n**Merged pull requests:**\n\n- Service tests clear list [\\#3](https://github.com/feathersjs/feathers-vuex/pull/3) ([marshallswain](https://github.com/marshallswain))\n- add before-install script [\\#2](https://github.com/feathersjs/feathers-vuex/pull/2) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.0.5](https://github.com/feathersjs/feathers-vuex/tree/v0.0.5) (2017-04-15)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.4...v0.0.5)\n\n**Merged pull requests:**\n\n- Update readme.md [\\#1](https://github.com/feathersjs/feathers-vuex/pull/1) ([marshallswain](https://github.com/marshallswain))\n\n## [v0.0.4](https://github.com/feathersjs/feathers-vuex/tree/v0.0.4) (2017-04-12)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.3...v0.0.4)\n\n## [v0.0.3](https://github.com/feathersjs/feathers-vuex/tree/v0.0.3) (2017-03-15)\n[Full Changelog](https://github.com/feathersjs/feathers-vuex/compare/v0.0.2...v0.0.3)\n\n## [v0.0.2](https://github.com/feathersjs/feathers-vuex/tree/v0.0.2) (2017-03-15)\n\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Feathers\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\n"
  },
  {
    "path": "README.md",
    "content": "# Feathers-Vuex\n\n[![Build Status](https://travis-ci.org/feathersjs-ecosystem/feathers-vuex.png?branch=master)](https://travis-ci.org/feathersjs-ecosystem/feathers-vuex)\n[![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-vuex.svg?style=flat-square)](https://david-dm.org/feathersjs-ecosystem/feathers-vuex)\n[![Download Status](https://img.shields.io/npm/dm/feathers-vuex.svg?style=flat-square)](https://www.npmjs.com/package/feathers-vuex)\n[![Greenkeeper badge](https://badges.greenkeeper.io/feathersjs-ecosystem/feathers-vuex.svg)](https://greenkeeper.io/)\n\n`Feathers-Vuex` is a first class integration of FeathersJS and Vuex. It implements many Redux best practices under the hood, eliminates _a lot_ of boilerplate code with flexible data modeling, and still allows you to easily customize the Vuex store.\n\n![feathers-vuex service logo](./service-logo.png)\n\n## Demo & Documentation\n\n[Demo](https://codesandbox.io/s/xk52mqm7o)\n\nSee [https://vuex.feathersjs.com](https://vuex.feathersjs.com) for full documentation.\n\n## Installation\n\n```bash\nnpm install feathers-vuex --save\n```\n\n```bash\nyarn add feathers-vuex\n```\n\nIMPORTANT: Feathers-Vuex is (and requires to be) published in ES6 format for full compatibility with JS classes. If your project uses Babel, it must be configured properly. See the [Project Configuration](https://vuex.feathersjs.com/getting-started.html#project-configuration) section for more information.\n\n## Contributing\n\nThis repo is pre-configured to work with the Visual Studio Code debugger. After running `yarn install`, use the \"Mocha Tests\" debug script for a smooth debugging experience.\n\n## License\n\nCopyright (c) Forever and Ever, or at least the current year.\n\nLicensed under the [MIT license](https://github.com/feathersjs-ecosystem/feathers-vuex/blob/master/LICENSE).\n"
  },
  {
    "path": "docs/.vuepress/config.js",
    "content": "module.exports = {\n  title: 'FeathersVuex',\n  description: 'Integration of FeathersJS, Vue, and Nuxt for the artisan developer',\n  head: [['link', { rel: 'icon', href: '/favicon.ico' }]],\n  theme: 'default-prefers-color-scheme',\n  themeConfig: {\n    repo: 'feathersjs-ecosystem/feathers-vuex',\n    docsDir: 'docs',\n    editLinks: true,\n    sidebar: [\n      '/api-overview.md',\n      '/3.0-major-release.md',\n      '/getting-started.md',\n      '/example-applications.md',\n      '/vue-plugin.md',\n      '/service-plugin.md',\n      '/auth-plugin.md',\n      '/model-classes.md',\n      '/common-patterns.md',\n      '/composition-api.md',\n      '/mixins.md',\n      '/data-components.md',\n      '/feathers-vuex-forms.md',\n      '/nuxt.md',\n      '/2.0-major-release.md',\n    ],\n    serviceWorker: {\n      updatePopup: true\n    }\n  }\n}\n"
  },
  {
    "path": "docs/2.0-major-release.md",
    "content": "---\ntitle: 2.0 Breaking Changes\nsidebarDepth: 3\n---\n\n# 2.0 Breaking Changes\n\nThe biggest change in Feathers-Vuex 2.0 is that it has been refactored with TypeScript!  (It's mostly ES6, still)\n\nYour project does NOT require to be written in TypeScript.  The `dist` is compiled to ES6.\n\n## Fixing the build for older Vue-CLI Apps\n\nAs of October 2019, up-to-date Vue-CLI apps are able to properly build and use `feathers-vuex` after adding `transpileDependencies`.  For apps based on older modules, I've found the simplest solution to be to copy the `feathers-vuex` module into `src/libs` inside your project.  Create a `copy-deps.sh` file in the root of your project and setup the following:\n\n```bash\nrm -rf src/libs\nmkdir src/libs\n\n# feathers-vuex\ncp -r node_modules/feathers-vuex/dist src/libs/feathers-vuex\n```\n\nThen in `package.json`, create a script that uses the `copy-deps.sh` file:\n\n```json\n{\n  \"copy\": \". ./copy-deps.sh\",\n  \"serve\": \"npm run copy && vue-cli-service serve\",\n  \"build\": \"npm run copy && vue-cli-service build\",\n  \"postinstall\": \"npm run copy\"\n}\n```\n\nIt's not the prettiest solution, but it works well and is the simplest for older apps.\n\n## Here's what's new in `feathers-vuex`\n\nCheck out the tests for the best documentation.  They've been reorganized.  This is still a Work in Progress.\n\n## Changes to Initialization\n\n1. To assist in connecting with multiple FeathersJS API servers, a new `serverAlias` option is now required.  This requires a simple addition to the initial options.\n2. The exports have changed.\n  - (a) A new `BaseModel` is available.  This is the base `FeathersVuexModel` which contains the model methods. Feel free to extend it and make it fit your awesome services!\n  - (b) The `service` method has been renamed to `makeServicePlugin`.\n  - (c) The `auth` method is now called `makeAuthPlugin`\n  - (d) The `models` object is now exported, so you can access them from anywhere.  They are keyed by `serverAlias`.\n  - (e) A new `clients` object is available. The intention is to allow working with multiple FeathersJS API servers.\n3. You no longer pass a `servicePath` to create a service-plugin. Instead, pass the actual Feathers service.\n4. Since you can customize the Model, you also pass the extended Model into the `makeServicePlugin` method.\n\nBelow is an all-in-one example of a the basic configuration steps.  See the next section for how to setup a project.\n\n```js\n// ./src/store/store.js\nimport feathers from './feathers-client'\nimport Vuex from 'vuex'\nimport feathersVuex from 'feathers-vuex'\n\nconst {\n  BaseModel,         // (2a)\n  makeServicePlugin, // (2b)\n  makeAuthPlugin,    // (2c)\n  models,            // (2d)\n  clients            // (2e)\n} = feathersVuex(feathers, {\n  idField: '_id',\n  serverAlias: 'myApi' // (1)\n})\n\nclass Todo extends BaseModel {\n  // required\n  constructor (data, options) {\n    super(data, options)\n  }\n  // required\n  static modelName = 'Todo'\n\n  // optional, but useful\n  static instanceDefaults(data) {\n    return {\n      name: '',\n      isComplete: false,\n      userId: null,\n      user: null // populated on the server\n    }\n  }\n\n  // optional, but useful\n  static setupInstance(data) {\n    if (data.user) {\n      data.user = new models.myApi.User(data.user)\n    }\n    return data\n  }\n  // customize the model as you see fit!\n}\n\nconst todosPlugin = makeServicePlugin({\n  Model: Todo, // (3)\n  service: feathers.service('todos') // (4)\n})\n\nconst store = new Vuex.Store({\n  plugins: [\n    todosPlugin\n  ]\n})\n```\n\n## Feathers-Vuex Vue plugin changes\n\nThe Vue plugin is registered in exactly the same way.  The difference comes when you try to find the Model classes in the `$FeathersVuex` object.  Instead of finding models directly on the `$FeathersVuex` object, they are namespaced by the `serverAlias` you provided.  This allows cleaner support for multiple APIs.  Supposing you had this code in a component, previously...\n\n```js\ncreated () {\n  // The old way\n  const { Todo } = this.$FeathersVuex\n}\n```\n\nModify it to include the new `serverAlias`.  Suppose you set a `serverAlias` of `myApi`, you'd put this in the new version:\n\n```js\ncreated () {\n  // The new way includes the `serverAlias` of '.myApi'\n  const { Todo } = this.$FeathersVuex.myApi\n}\n```\n\n## Better default `idField` support\n\nSince records are keyed by id, `feathers-vuex` needs to know what the `idField` is for each service.  In the last version, the default was `id`, and you had to specify something different.  This version supports `id` and `_id` with zero configuration.  You only need to set `idField` when you're using something other than `id` or `_id`.\n\nThere's still a warning message when records don't have a property matching the `idField`.  Just like in the last version, it only appears when you turn on `debug: true` in the options.\n\n## Support for Temporary Records\n\nFeathers-Vuex 2.0 supports tracking temporary items and automatically assigns a temporary id to new records. It also adds the records to `state.tempsById`.  This is customizable using the `tempIdField` option.\n\nBecause of the new ability to handle temporary records, a message is only logged when assigning a temporary id to a record.  The `checkId` utility function has been removed, since this was its main purpose.\n\n## Getters Work with Temporary Records\n\nThe `find` getter has been updated to include records from `state.tempsById` when you pass `temps: true` in the params.\n\nThe `get` getter has also been updated to work with temp ids.  Pass the tempId the way you normally would pass the id:  `get(tempId)`\n\n## The \"currentItem\" workflow is no longer supported\n\nThe `setCurrent` mutation and `currentId` state encouraged use of a very limiting API.  It's much more common for apps to require more than one current record.  The `createCopy`, `resetCopy` (formerly called `rejectCopy`), `commitCopy`, and `clearCopy` mutations (since v1.x) provide a more flexible solution for implementing the same functionality.  As a result of this, following have been removed from the modules:\n\n- state: `currentID`, `copy`\n- getters: `current`, `getCopy`\n- mutations: `setCurrent`, `clearCurrent`, `clearList`, `commitCopy`, `clearCopy`, `resetCopy`\n\n## The `diffOnPatch` option has been removed\n\n(See the next section for its replacement.)\n\nI have not been able to find a diffing algorithm that works equally well acroos all schemas.  It's especially difficult for nested schemas.  Because of this, `diffOnPatch` is no longer a global option.  It is being replaced by the `diffOnPatch` static Model method. See the next section.\n\n## Model Classes: BYOD (Bring Your Own Diffing)\n\n> Note: As of `feathers-vuex@3.9.0`, you can also pass `params.data` to the patch object to implement partial patching on objects.  You might choose to use `params.data` instead of `diffOnPatch`.\n\nFirst, why do any diffing?  On the API server, an `update` request replaces an entire object, but a `patch` request only overwrites the attributes that are provided in the data.  For services with simple schemas, it doesn't really matter.  But if your schema grows really large, it can be supportive to only send the updates instead of the entire object.\n\nA new `diffOnPatch` method is available to override in your extended models.  `diffOnPatch` gets called just before sending the data to the API server.  It gets called with the data and must return the diffed data.  By default, it is set to `diffOnPatch: data => data`.\n\nBelow is an example of how you might implement `diffOnPatch`.  You would only ever use this with a cloned instance, otherwise there's nothing to diff.\n\n```js\nimport { diff } from 'deep-object-diff'\nconst { makeServicePlugin, BaseModel } = feathersVuex(feathers, { serverAlias: 'myApi' })\n\nclass Todo extends BaseModel {\n  public constructor (data, options?) {\n    super(data, options)\n  }\n  public static modelName = 'Todo'\n  public static diffOnPatch (data) {\n    const originalObject = Todo.store.state.keyedById[data._id]\n    return diff(originalObject, data)\n  }\n}\n\nconst store = new Vuex.Store({\n  plugins: [\n    makeServicePlugin({\n      Model: Todo,\n      service: feathers.service(servicePath)\n    })\n  ]\n})\n```\n\n## The `modelName` option has moved\n\nWhile the original intent was to completely remove the `modelName` option, it's still required after transpiling to ES5.  This is because during transpilation, the class name gets stripped and can't be put back into place.  Since ES5 is the default target for most build environments, the `modelName` is still required to be specified, but it has been moved.  Instead of being an option, it's required as a static property of each class.\n\nNote: Once ES6 is the default target for most build systems, modelName will become optional.  For future upgradability, it's recommended that you give your `modelName` the exact same name as your model class.\n\n```js\nconst { makeServicePlugin, BaseModel } = feathersVuex(feathers, { serverAlias: 'myApi' })\n\nclass Todo extends BaseModel {\n  public constructor (data, options?) {\n    super(data, options)\n  }\n  public static modelName = 'Todo' // modelName is required on all Model classes.\n  public static exampleProp: string = 'Hello, World! (notice the comma, folks!)'\n}\n\nconst store = new Vuex.Store({\n  plugins: [\n    makeServicePlugin({\n      Model: Todo,\n      service: feathers.service(servicePath)\n    })\n  ]\n})\n```\n\n## Options are no longer kept on the Model\n\nThe Model class no longer has an `options` property.  You can access the same information through the `Model.store.state[Model.namespace]`.\n\n## The 'apiPrefix' option has been removed\n\nFeathers-Vuex now includes full support for communicating with multiple FeathersJS APIs.  The `apiPrefix` option was a poorly-implemented, hacky, first attempt at this same feature.  Since it didn't work as intended, it has been removed.  See this example test for working with multiple APIs:\n\n```js\nimport { assert } from 'chai'\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport {\n  feathersRestClient as feathers,\n  makeFeathersRestClient\n} from '../../test/fixtures/feathers-client'\nimport feathersVuex from './index'\n\nit('works with multiple, independent Feathers servers', function() {\n  // Connect to myApi, create a Todo Model & Plugin\n  const feathersMyApi = makeFeathersRestClient('https://api.my-api.com')\n  const myApi = feathersVuex(feathersMyApi, {\n    idField: '_id',\n    serverAlias: 'myApi'\n  })\n  class Todo extends myApi.BaseModel {\n    public test: boolean = true\n  }\n  const todosPlugin = myApi.makeServicePlugin({\n    Model: Todo,\n    service: feathersMyApi.service('todos')\n  })\n\n  // Create a Task Model & Plugin on theirApi\n  const feathersTheirApi = makeFeathersRestClient('https://api.their-api.com')\n  const theirApi = feathersVuex(feathersTheirApi, {\n    serverAlias: 'theirApi'\n  })\n  class Task extends theirApi.BaseModel {\n    public test: boolean = true\n  }\n  const tasksPlugin = theirApi.makeServicePlugin({\n    Model: Task,\n    service: feathersTheirApi.service('tasks')\n  })\n\n  // Register the plugins\n  new Vuex.Store({\n    plugins: [todosPlugin, tasksPlugin]\n  })\n  const { models } = myApi\n\n  assert(models.myApi.Todo === Todo)\n  assert(!models.theirApi.Todo, `Todo stayed out of the 'theirApi' namespace`)\n  assert(models.theirApi.Task === Task)\n  assert(!models.myApi.Task, `Task stayed out of the 'myApi' namespace`)\n\n  assert.equal(\n    models.myApi.byServicePath[Todo.servicePath],\n    Todo,\n    'also registered in models.byServicePath'\n  )\n  assert.equal(\n    models.theirApi.byServicePath[Task.servicePath],\n    Task,\n    'also registered in models.byServicePath'\n  )\n```\n\n## Services are no longer set up, internally\n\nYou no longer just pass a servicePath.  Instead, create the service, then pass the returned service object.\n\n## Simplified Pending Mutations\n\nPreviously, there was a mutation for every single variety of method and set/unset pending. (`setFindPending`, `unsetFindPending`, etc.). There were a total of twelve methods for this simple operation. They are now combined into two methods: `setPending(method)` and `unsetPending(method)`.  Here's the difference.\n\n```js\n// The old way\ncommit('setFindPending')\ncommit('unsetFindPending')\n\n// The new way\ncommit('setPending', 'find')\ncommit('unsetPending', 'find')\n```\n\n## Simplified Error Mutations\n\nThe \"error\" mutations have been simplified similar to the \"pending\" mutations:\n\n```js\n// The old way\ncommit('setFindError', error)\ncommit('clearFindError')\n\n// The new way\ncommit('setError', { method: 'find', error })\ncommit('clearError', 'find')\n```\n\n## `instanceDefaults` must be a function\n\nIn the previous version, you could specify instanceDefaults as an object.  It was buggy and limiting.  In this new version, `instanceDefaults` must always be a function.  See the next section for an example.\n\n## Getter and Setter props go on the Model classes\n\nOne of the great features about using Model classes is data-level computed properties.  You get to specify computed properties directly on your data structures instead of inside components, which keeps a better separation of concerns.  In `feathers-vuex@2.x`, since we have direct access to the Model classes, it's the perfect place to define the computed properties:\n\n```js\nimport feathersClient, {\n  makeServicePlugin,\n  BaseModel\n} from '../../feathers-client'\n\nclass User extends BaseModel {\n  constructor(data, options) {\n    super(data, options)\n  }\n  static modelName = 'User' // required\n  // Computed properties don't go on in the instanceDefaults, anymore.\n  static instanceDefaults() {\n    return {\n      firstName: '',\n      lastName: '',\n      email: '',\n      password: '',\n      isAdmin: false,\n    }\n  }\n  // Here's a computed getter\n  get fullName() {\n    return `${this.firstName} ${this.lastName}`\n  }\n  // Probably not something you'd do in real life, but it's an example of a setter.\n  set fullName(fullName) {\n    const [ firstName, lastName ] = fullName.split(' ')\n    Object.assign(this, { firstName, lastName })\n  }\n}\nconst servicePath = 'users'\nconst servicePlugin = makeServicePlugin({\n  Model: User,\n  service: feathersClient.service(servicePath),\n  servicePath\n})\n```\n\n## Relationships have been separated from `instanceDefaults`\n\nFeathers-Vuex 2.0 has a new API for establishing relationships between data.  Before we cover how it works, let's review the old API.\n\nFeathers-Vuex 1.x allowed using the `instanceDefaults` API to both setup default values for Vue reactivity AND establishing relationships between services.  It supported passing a string name that matched a model name to setup a relationship, as shown in this next example.  This was a simple, but very limited API:\n\n```js\n// The old way\ninstanceDefaults: {\n  _id: '',\n  description: '',\n  isCompleted: false,\n  user: 'User'\n}\n```\n\nAny instance data with a matching key would overwrite the same property in the instanceDefaults, which resulted in an inconsistent API.\n\nIn Feathers-Vuex 2.0, the `instanceDefaults` work the same for setting defaults with only one exception:  They no longer setup the relationships.  The new `setupInstance` function provides an API that is much more powerful.\n\nAs mentioned earlier, it MUST be provided as a function:\n\n```js\n// See the `model-instance-defaults.test.ts` file for example usage.\n// This is a brief example.\ninstanceDefaults(data, { models, store}) {\n  return {\n    _id: '',\n    description: '',\n    isCompleted: false\n    // No user props, here.\n  }\n}\n```\n\nNotice in the above example that we did not return `user`.  Relationships are now handled in the `setupInstance` method.\n\nWhere `instanceDefaults` props get overwritten with instance data, the props returned from `setupInstance` overwrite the instance data.  If it were using `Object.assign`, internally (it's not, but IF it were), it would look like the below example, where `data` is the original instance data passed to the constructor.\n\n```js\nObject.assign({}, instanceDefaults(data), data, setupInstance(data))\n```\n\n## Define Relationships and Modify Data with `setupInstance`\n\nThe new `setupInstance` method allows a lot of flexibility in creating new instances.  It has the exact same API as the `instanceDefaults` method.  The only difference is the order in which they are applied to the instance data.\n\nAlthough it looks similar to `instanceDefaults`, it can't be used for default values.  This is because it overwrites instance data. Having separate methods allows a clean separation between setting up defaults and establishing relationships with other constructors.\n\n```js\n// See the `model-relationships.test.ts` file for example usage.\n// This is a brief example.\nfunction setupInstance(data, { models, store }) {\n  const { User, Tag } = models.myServerAlias // Based on the serverAlias you provide, initially\n\n  // A single User instance\n  if (data.user) {\n    data.user = new User(data.user)\n  }\n  // An array of Tag instances\n  if (data.tags) {\n    data.tags = data.tags.map(t => new Tag(t))\n  }\n  // A JavaScript Date Object\n  if (data.createdAt) {\n    data.createdAt = new Date(data.createdAt)\n  }\n  return data\n}\n```\n\nOr below is an example that does the exact same thing with one line per attribute:\n\n```js\nfunction setupInstance(data, { models, store }) {\n  const { User } = models.myServerAlias\n\n  Object.assign(data, {\n    ...(data.user && { user: new User(data.user) }), // A single User instance\n    ...(data.tags && { tags: data.tags.map(t => new Tag(t)) }), // An array of Tag instances\n    ...(data.createdAt && { createdAt: new Date(data.createdAt) }) // A JavaScript Date Object\n  })\n  return data\n}\n```\n\nWhere `instanceDefaults` props get replaced by instance data, the props returned from `setupInstance` overwrite the instance data.  If it were using `Object.assign`, internally (it's not, but IF it were), it would look like the below example, where `data` is the original instance data passed to the constructor.\n\n```js\nObject.assign({}, instanceDefaults(data), data, setupInstance(data))\n```\n\n## Preventing duplicate merge when extending BaseModel with a custom constructor\n\nThe BaseModel constructor calls `mergeWithAccessors(this, newData)`.  This utility function correctly copies data between both regular objects and Vue.observable instances.  If you create a class where you need to do your own merging, you probably don't want `mergeWithAccessors` to run twice.  In this case, you can use the `merge: false` BaseModel ___instance option___ to prevent the internal merge.  You can then access the `mergeWithAccessors` method by calling `MyModel.merge(this, newData)`.  Here's an example:\n\n```ts\nconst { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n  serverAlias: 'myApiServer'\n})\n\nclass Todo extends BaseModel {\n  public constructor(data, options?) {\n    options.merge = false // Prevent the internal merge from occurring.\n    super(data, options)\n\n    // ... your custom constructor logic happens here.\n\n    // Call the static merge method to do your own merging.\n    Todo.merge(this, data)\n  }\n}\n```\n\nIt's important to note that setting `merge: false` in the options will disable the `setupinstance` function.  You need to manually call it, like this:\n\n```ts\nclass Todo extends BaseModel {\n  public constructor(data, options?) {\n    options = options || {}\n    options.merge = false // Prevent the internal merge from occurring.\n    super(data, options)\n\n    // ... your custom construcor logic happens here.\n\n    // Call setupInstance manually\n    const { models, store } = Todo\n    // JavaScript fundamentals: if you're using `this` in `setupInstance`, use .call(this, ...)\n    const instanceData = Todo.setupInstance.call(this, data, { models, store })\n    // If you're not using `this, just call it like normal\n    const instanceData = Todo.setupInstance(data, { models, store })\n\n    // Call the static merge method to do your own merging.\n    Todo.merge(this, instanceData)\n  }\n}\n```\n\n## Customizing the BaseModel\n\nBecause we have access to the BaseModel, we can extend it to do whatever custom stuff we need in our application.  The `feathers-client.js` file is a great, centralized location for accomplishing this:\n\n```js\n// src/feathers-client.js\nimport feathers from '@feathersjs/feathers'\nimport socketio from '@feathersjs/socketio-client'\nimport authClient from '@feathersjs/authentication-client'\nimport io from 'socket.io-client'\nimport feathersVuex from 'feathers-vuex' // or '@/libs/feathers-vuex' if you're copying feathers-vuex as mentioned earlier.\n\n// Setup the Feathers client\nconst host = process.env.VUE_APP_API_URL // or set a string here, directly\nconst socket = io(host, { transports: ['websocket'] })\nconst feathersClient = feathers()\n  .configure(socketio(socket))\n  .configure(authClient({ storage: window.localStorage }))\n\nexport default feathersClient\n\n// Setup feathers-vuex\nconst {\n  makeServicePlugin,\n  makeAuthPlugin,\n  BaseModel,\n  models,\n  clients,\n  FeathersVuex\n} = feathersVuex(feathersClient, {\n  serverAlias: 'api', // or whatever that makes sense for your project\n  idField: '_id' // `id` and `_id` are both supported, so this is only necessary if you're using something else.\n})\n\n// Note that if you want to\n// extend the BaseClass for the rest of the app, this is a great place to do it.\n// After you've extended the BaseClass with your CustomClass, export it, here.\nclass CustomBaseModel extends BaseModel {\n  // Optionally add custom functionality for all services, here.\n}\n\n// Export all of the utilities for the rest of the app.\nexport {\n  makeAuthPlugin,\n  makeServicePlugin,\n  BaseModel,\n  models,\n  clients,\n  FeathersVuex,\n  CustomBaseModel // Don't forget to export it for use in all other services.\n}\n\n```\n\n\n## Auth plugin changes\n\nWith FeathersJS version 4, the user is returned in the authentication response, by default.  This means that it's no longer required to provide a `userService` option to populate the user. 👍\n\nIf you would like to enable backwards compatibility with the previous version of Feathers, pass the below code in the makeAuthPlugin.\n\n```js\nmakeAuthPlugin({\n  userService: 'users',\n  actions: {\n        responseHandler({ commit, state, dispatch }, response) {\n      if (response.accessToken) {\n        commit('setAccessToken', response.accessToken)\n        // Decode the token and set the payload, but return the response\n        return feathersClient.passport\n          .verifyJWT(response.accessToken)\n          .then(payload => {\n            commit('setPayload', payload)\n            let user = response[state.responseEntityField]\n            // If a user was returned in the authenticate response, use that user.\n            if (user) {\n              if (state.serverAlias && state.userService) {\n                const Model = Object.keys(models[state.serverAlias])\n                  .map(modelName => models[state.serverAlias][modelName])\n                  .find(model => model.servicePath === state.userService)\n                if (Model) {\n                  user = new Model(user)\n                }\n              }\n              commit('setUser', user)\n              // Populate the user if the userService was provided\n            } else if (\n              state.userService &&\n              payload.hasOwnProperty(state.entityIdField)\n            ) {\n              return dispatch(\n                'populateUser',\n                payload[state.entityIdField]\n              ).then(() => {\n                commit('unsetAuthenticatePending')\n                return response\n              })\n            } else {\n              commit('unsetAuthenticatePending')\n            }\n            return response\n          })\n        // If there was not an accessToken in the response, allow the response to pass through to handle two-factor-auth\n      } else {\n        return response\n      }\n    }\n  }\n```\n\nThe above code will override the `responseHandler` auth action to work with the Passport-based version of Feathers Authentication.\n\n\n## Gotchas\n\n\n### Don't Perform Queries (Side Effects) in Getters\n\nDon't try to perform a query from within a getter like the example, below.  It will result in an infinite loop:\n\n```js\nget user () {\n  if (this.userId) {\n    const user = Models.User.getFromStore(this.userId)\n\n    // Fetch the User record if we don't already have it\n    if (!user) {\n      Models.User.get(this.userId)\n    }\n\n    return user\n  } else {\n    return null\n  }\n}\n```\n\n### Using custom query parameters\n\nThere are two places where the query operators have to be allowed.\n\n- In the Feathers Client (for the actions): refer to the FeathersJS docs for `whitelist`ing operators.\n- Inside feathers-vuex (for the getters): Check out the `paramsForServer` and  `whitelist` options for `feathers-vuex`. Both accept an array of strings representing prop names, but now I can't remember why I determined that I needed both. :)\n\nFor the Feathers Client, follow the FeathersJS docs for your database adapter.\n"
  },
  {
    "path": "docs/3.0-major-release.md",
    "content": "---\ntitle: What's New in 3.0\nsidebarDepth: 3\n---\n\n# What's new in Feathers-Vuex 3.0\n\n## Vue Composition API Support\n\nVersion 3.0 of Feathers-Vuex is the Vue Composition API release!  There were quite a few disappointed (and misinformed:) developers in 2019 when the Vue.js team announced what is now called the Vue Composition API.  From my perspective:\n\n- It is the most powerful feature added to Vue since its first release.\n- It improves the ability to create dynamic functionality in components.\n- It greatly enhances organization of code in components.\n- It encourages code re-use. Check out the [vue-use-web](https://tarektouati.github.io/vue-use-web/) collection for some great examples.\n\nAnd now it has become the best way to perform queries with Feathers-Vuex.  To find out how to take advantage of the new functionality in your apps, read the [Feather-Vuex Composition API docs](./composition-api.md).\n\n## New `extend` option for `makeServicePlugin` <Badge text=\"3.9.0+\" />\n\nThe `makeServicePlugin` now supports an `extend` method that allows customizing the store and gives access to the actual Vuex `store` object, as shown in this example:\n\n```js\nimport { makeServicePlugin } from ‘feathers-vuex’\nimport { feathersClient } from ‘./feathers-client.js’\n\nclass Todo { /* truncated */ }\n\nexport default makeServicePlugin({\n  Model: Todo,\n  service: feathersClient.service(‘todos’),\n  extend({ store, module }) {\n    // Listen to other parts of the store\n    store.watch(/* truncated */)\n\n    return {\n      state: {},\n      getters: {},\n      mutations: {},\n      actions: {}\n    }\n  }\n})\n```\n\n## Partial data on patch <Badge text=\"3.9.0+\" />\nAs of version 3.9.0, you can provide an object as `params.data`, and Feathers-Vuex will use `params.data` as the patch data.  This change was made to the service-module, itself, so it will work for `patch` across all of feathers-vuex.  Here's an example of patching with partial data:\n\n```js\nimport { models } from 'feathers-vuex'\nconst { Todo } = models.api\n\nconst todo = new Todo({ description: 'Do Something', isComplete: false })\n\ntodo.patch({ data: { isComplete: true } })\n\n// also works for patching with instance.save\ntodo.save({ data: { isComplete: true } })\n```\n\n## FeathersVuexPagination Component <Badge text=\"3.8.0+\" />\n\nTo assist with Server Side Pagination support, Feathers-Vuex now includes the `<FeathersVuexPagination>` component.  It's a renderless component that removes the boilerplate behind handling pagination in the UI.  Read about it in the [Composition API Docs](/composition-api.html#feathersvuexpagination).\n\n## Custom Handling for Feathers Events <Badge text=\"3.1.0+\" />\n\nVersion 3.1 of Feathers-Vuex enables ability to add custom handling for each of the FeathersJS realtime events.  You can read more about it in the [Service Plugin: Events](./service-plugin.md#service-events) docs.\n\n## Breaking Changes\n\nFeathers-Vuex follows semantic versioning.  There are two breaking changes in this release:\n\n### Auth Plugin `user` Not Reactive <Badge text=\"New API in 3.2.0+\" />\n\nDue to changes in how reactivity is applied to service state (it's now using Vue.set under the hood), the `user` state of the `auth` module is no longer reactive.  To fix this issue, two getters have been added to the `auth` state.  They are available when a `userService` is provided to the `makeAuthPlugin` options.\n\n- `user`: returns the reactive, logged-in user from the `userService` specified in the options.\n- `isAuthenticated`: a easy to remember boolean attribute for if the user is logged in.\n\nIf you depend on a reactive, logged-in user in your apps, here is how to fix the reactivity:\n\n- Replace any reference to `store.state.auth.user` with `store.getters['auth/user']`.\n\nBecause the `user` state is no longer reactive, it is logical for it to be removed in the next version.  It will likely be replaced by a `userId` attribute in Feathers-Vuex 4.0.\n\n\n### Server-Side Pagination Support is Off by Default\n\nThe `makeFindMixin` (and the new `useFind` utility) now have server-side pagination support turned off, by default.  Real-time arrays of results are now the default setting.  This really improves the development experience, especially for new users.\n\nTo migrate your app to version 3.0, you need to update any `params` where you are using server-side pagination.  It will work as it has been in version 2.0 once you explicitly set `paginate: true` in the params, like this:\n\n```js\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'MyComponent',\n  mixins: [ makeFindMixin({ service: 'users', watch: true })],\n  computed: {\n    usersParams() {\n      return {\n        query: {},\n        paginate: true // explicitly enable pagination, now.\n      }\n    }\n  }\n}\n```\n\nThis behavior exactly matches the new `useFind` utility.\n\n## Deprecations\n\n### The `keepCopiesInStore` Option <Badge text=\"deprecated\" type=\"warning\"/>\n\nThe `keepCopiesInStore` option is now deprecated.  This was a part of the \"clone and commit\" API which basically disabled the reason for creating the \"clone and commit\" API in the first place.\n\nIf you're not familiar with the Feathers-Vuex \"clone and commit\" API, you can learn more about the [built-in data modeling](./model-classes.md) API and the section about [Working with Forms](./feathers-vuex-forms.md#the-clone-and-commit-pattern).\n\nThe `keepCopiesInStore` feature is set to be removed in Feathers-Vuex 4.0.\n\n### Auth Plugin State: `user` <Badge text=\"deprecated\" type=\"warning\"/>\n\nAs described, earlier on this page, since the Auth Plugin's `user` state is no longer reactive and has been replaced by a `user` getter that IS reactive, the `user` state will be removed in the Feathers-Vuex 4.0.\n\n### Renderless Data Components: `query`, `fetchQuery` and `temps` <Badge text=\"deprecated type=\"warning\">\n\nTo keep consistency with mixins and the composition API it's preferred to use `params` and `fetchParams` instead of the old `query` and `fetchQuery` for renderless data components. Also the `:temps=\"true\"` is deprecated in favour of `:params=\"{ query: {}, temps: true }\"`. This way additional params can be passed to the server if you need some more magic like `$populateParams`."
  },
  {
    "path": "docs/api-overview.md",
    "content": "---\ntitle: API Overview\nsidebarDepth: 3\n---\n\n<!--- Usage ------------------------------------------------------------------------------------ -->\n[![Build Status](https://travis-ci.org/feathersjs-ecosystem/feathers-vuex.png?branch=master)](https://travis-ci.org/feathersjs-ecosystem/feathers-vuex)\n[![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-vuex.svg?style=flat-square)](https://david-dm.org/feathersjs-ecosystem/feathers-vuex)\n[![Download Status](https://img.shields.io/npm/dm/feathers-vuex.svg?style=flat-square)](https://www.npmjs.com/package/feathers-vuex)\n\n![feathers-vuex service logo](https://github.com/feathersjs-ecosystem/feathers-vuex/raw/master/service-logo.png)\n\n> Integrate the Feathers Client into Vuex\n\n`feathers-vuex` is a first class integration of the Feathers Client and Vuex.  It implements many Redux best practices under the hood, eliminates *a lot* of boilerplate code, and still allows you to easily customize the Vuex store.\n\nThese docs are for version 2.x.  For feathers-vuex@1.x, please go to [https://feathers-vuex-v1.netlify.com](https://feathers-vuex-v1.netlify.com).\n\n## Features\n\n- Fully powered by Vuex & Feathers\n- Realtime By Default\n- Actions With Reactive Data\n- Local Queries\n- Live Queries\n- Feathers Query Syntax\n- Vuex Strict Mode Support\n- [Client-Side Pagination Support](./service-plugin.md#pagination-and-the-find-getter)\n- Fall-Through Caching\n- [`$FeathersVuex` Plugin for Vue](./vue-plugin.md)\n- [Per-Service Data Modeling](./common-patterns.md#Basic-Data-Modeling-with-instanceDefaults)\n- [Clone & Commit](./feathers-vuex-forms.md#the-clone-and-commit-pattern)\n- Simplified Auth\n- [Per-Record Defaults](./model-classes.md#instancedefaults)\n- [Data Level Computed Properties](./2.0-major-release.md#getter-and-setter-props-go-on-the-model-classes)\n- [Improved Relation Support](./2.0-major-release.md#define-relationships-and-modify-data-with-setupinstance)\n- [Powerful Mixins](./mixins.md)\n- [Renderless Data Components](./data-components.md)\n- [Renderless Form Component](./feathers-vuex-forms.md#feathersvuexformwrapper) for Simplified Vuex Forms\n- [Temporary (Local-only) Record Support](./2.0-major-release.md#support-for-temporary-records) *\n- New `useFind` and `useGet` Vue Composition API super powers! <Badge text=\"3.0.0+\" />\n- [Server-Powered Pagination Support](./service-plugin.md#pagination-and-the-find-action) *\n- [VuePress Dark Mode Support](https://tolking.github.io/vuepress-theme-default-prefers-color-scheme/) for the Docs\n\n`** Improved in v3.0.0`\n\n## License\n\nLicensed under the [MIT license](LICENSE).\n\nFeathers-Vuex is developed and maintained by [Marshall Thompson](https://www.github.com/marshallswain).\n\n"
  },
  {
    "path": "docs/auth-plugin.md",
    "content": "---\ntitle: Auth Plugin\n---\n\nThe Auth module assists setting up user login and logout.\n\n## Setup\n\nSee the [Auth Setup](/getting-started.html#auth-plugin) section for an example of how to setup the Auth Plugin.\n\n## Breaking Changes in 2.0\n\nThe following breaking changes were made between 1.x and 2.0:\n\n- The `auth` method is now called `makeAuthPlugin`.\n\n## Configuration\n\nYou can provide a `userService` in the auth plugin's options to automatically populate the user upon successful login.\n\n## State\n\nIt includes the following state by default:\n\n```js\n{\n  accessToken: undefined, // The JWT\n  payload: undefined, // The JWT payload\n\n  userService: null, // Specify the userService to automatically populate the user upon login.\n  entityIdField: 'userId', // The property in the payload storing the user id\n  responseEntityField: 'user', // The property in the payload storing the user\n  user: null, // Deprecated: This is no longer reactive, so use the `user` getter. See below.\n\n  isAuthenticatePending: false,\n  isLogoutPending: false,\n\n  errorOnAuthenticate: undefined,\n  errorOnLogout: undefined\n}\n```\n\n## Getters\n\nTwo getters are available when a `userService` is provided to the `makeAuthPlugin` options.\n\n- `user`: returns the reactive, logged-in user from the `userService` specified in the options. Returns `null` if not logged in.\n- `isAuthenticated`: a easy to remember boolean attribute for if the user is logged in.\n\n## Actions\n\nThe following actions are included in the `auth` module.  Login is accomplished through the `authenticate` action.  For logout, use the `logout` action.  It's important to note that the records that were loaded for a user are NOT automatically cleared upon logout.  Because the business logic requirements for that feature would vary from app to app, it can't be baked into Feathers-Vuex.  It must be manually implemented.  The recommended solution is to simply refresh the browser, which clears the data from memory.\n\n- `authenticate`: use instead of `feathersClient.authenticate()`\n- `logout`: use instead of `feathersClient.logout()`\n\nIf you provided a `userService` and have correctly configured your `entityIdField` and `responseEntityField` (the defaults work with Feathers V4 out of the box), the `user` state will be updated with the logged-in user.  The record will also be reactive, which means when the user record updates (in the users service) the auth user will automatically update, as well.\n\n> Note: The Vuex auth store will not update if you use the feathers client version of the above methods.\n\n## Example\n\nHere's a short example that implements the `authenticate` and `logout` actions.\n\n```js\nexport default {\n  // ...\n  methods: {\n\n    login() {\n      this.$store.dispatch('auth/authenticate', {\n        email: '...',\n        password: '...'\n      })\n    }\n\n    // ...\n\n    logout() {\n      this.$store.dispatch('auth/logout')\n    }\n\n  }\n  // ...\n}\n```\n\nNote that if you customize the auth plugin's `namespace` then the `auth/` prefix in the above example would change to the provided namespace.\n"
  },
  {
    "path": "docs/common-patterns.md",
    "content": "---\ntitle: Common Patterns\n---\n\n## Set the `idField`\n\nIf you have a \"WTF this isn't working\" moment while setting up a new service, make sure you've set the `idField` property on your service.  In `feathers-vuex@1.x`, the `id` is the default `idField`.  You have to manually set `_id`.  Starting in `feathers-vuex@2.x`, both the `id` and `_id` fields are supported without any configuration, so you only set the `idField` when your service uses something else.\n\n## Enable debugging\n\nYou can set `debug: true` in the options to enable some logging to assist with debugging.\n\n## Use the `<FeathersVuexFind>` and `<FeathersVuexGet>` components\n\nUsing the new `<FeathersVuexFind>` and `<FeathersVuexGet>` components provides concise access to the best features of `feathers-vuex`, including live queries, reactive lists, custom pagination tracking per component, and fall-through cacheing of local data in the Vuex store.  Check out the [Renderless Data Components](./data-components.html) docs for more details.\n\n## Use the `makeFindMixin` and `makeGetMixin` utilities\n\nThe mixin utilities provide the same functionality as the components, but with more power and flexibility.  Check out the [Mixin docs](./mixins.html) for more details.\n\n## Working with TypeScript\n\nAs of version 2.0, Feathers-Vuex has been rewritten in TypeScript.\n\nSee [this issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/114) for suggestions for with TypeScript helpers.\n\nIn version 3.0, support for the [Vue Composition API](https://github.com/vuejs/composition-api) was added in the form of the `useFind` and `useGet` utilities.  These new utilities provide the best experience for working with TypeScript.  This is due to two things:\n\n1. Better general TypeScript support in the Vue Composition API.\n1. Consistent object-key naming and types in the new utilities.\n\nRead more about how to use them in the [Feathers-Vuex Composition API Docs](./composition-api.md)\n\n## Clearing data upon user logout\n\nThe best solution is to simply refresh to clear memory.  The alternative to refreshing would be to perform manual cleanup of the service stores.  Refreshing is much simpler, so it's the officially supported solution.  Feel free to read [this issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/10) for more suggestions.\n\n## Accessing the store from hooks\n\nBecause the service's Model [is available](./service-plugin.html#The-FeathersClient-Service) at `service.FeathersVuexModel`, you can access the store inside hooks.  This is especially handy if you have some custom attributes in a paginated server response.\n\nAs an example, this `speeding-tickets` service has a `summary` attribute that comes back in the response.  We can\n\n```js\nimport { makeServicePlugin, BaseModel } from '../feathers-client'\n\nclass SpeedingTicket extends BaseModel {\n  constructor(data, options) {\n    super(data, options)\n  }\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'SpeedingTicket'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      vin: '',\n      plateState: ''\n    }\n  }\n}\nconst servicePath = 'speeding-tickets'\nconst servicePlugin = makeServicePlugin({\n  Model: SpeedingTicket,\n  service: feathersClient.service(servicePath),\n  servicePath,\n  mutations: {\n    handleSummaryData (state, summaryData) {\n      state.mostRecentSummary = summaryData\n    }\n  }\n})\n\nfeathersClient.service(servicePath)\n  .hooks({\n    after: {\n      find: [\n        context => {\n          const { service, result } = context\n\n          if (result.summary) {\n            service.FeathersVuexModel.store.commit('handleSummaryData', result.summary)\n          }\n        }\n      ]\n    }\n  })\n```\n\n## Handling custom server responses\n\nSometimes your server response may contain more attributes than just database records and pagination data.  You could handle this directly in a component, if it's only needed in that one component,  But, if you need it in multiple components, there are better options.\n\nDepending on what you need to do, you may be able to solve this by [accessing the store from hooks](#Accessing-the-store-from-hooks).  But that solution won't handle a scenario where you need the response data to be already populated in the store.\n\nIf you need the response data to already be in the store, you can use the [`afterFind` action](./service-plugin.html#afterFind-response).  Here's what this looks like:\n\n```js\nimport { makeServicePlugin, BaseModel } from '../feathers-client'\n\nclass SpeedingTicket extends BaseModel {\n  constructor(data, options) {\n    super(data, options)\n  }\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'SpeedingTicket'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      vin: '',\n      plateState: ''\n    }\n  }\n}\nconst servicePath = 'speeding-tickets'\nconst servicePlugin = makeServicePlugin({\n  Model: SpeedingTicket,\n  service: feathersClient.service(servicePath),\n  servicePath,\n  actions: {\n    afterFind ({ commit, dispatch, getters, state }, response) {\n      if (response.summary) {\n        commit('handleSummaryData', response.summary)\n      }\n    }\n  },\n  mutations: {\n    handleSummaryData (state, summaryData) {\n      state.mostRecentSummary = summaryData\n    }\n  }\n})\n```\n\n## Reactive Lists with Live Queries\n\nUsing Live Queries will greatly simplify app development.  The `find` getter enables this feature.  Here is how you might setup a component to take advantage of them.  The next example shows how to setup two live-query lists using two getters.\n\n```js\nimport { mapState, mapGetters, mapActions } from 'vuex'\n\nexport default {\n  name: 'some-component',\n  computed: {\n    ...mapState('appointments', { areAppointmentsLoading: 'isFindPending' }),\n    ...mapGetters('appointments', { findAppointmentsInStore: 'find' } ),\n    // Query for future appointments\n    queryUpcoming () {\n      return { date: { $gt: new Date() }}\n    },\n    // Query for past appointments\n    queryPast () {\n      return { date: { $lt: new Date() }}\n    },\n    // The list of upcoming appointments.\n    upcomingAppointments () {\n      return this.findAppointmentsInStore({ query: this.queryUpcoming }).data\n    },\n    // The list of past appointments\n    pastAppointments () {\n      return this.findAppointmentsInStore({ query: this.queryPast }).data\n    }\n  },\n  methods: {\n    ...mapActions('appointments', { findAppointments: 'find' })\n  },\n  created () {\n    // Find all appointments. We'll use the getters to separate them.\n    this.findAppointments({ query: {} })\n  }\n}\n```\n\nin the above example of component code, the `upcomingAppointments` and `pastAppointments` will automatically update.  If a new item is sent from the server, it will get added to one of the lists, automatically.  `feathers-vuex` listens to socket events automatically, so you don't have to manually wire any of this up!\n\n## Organizing the services in your project\n\nYou can use the file system to organize each service into its own module. This is especially useful in organizing larger-sized projects.  Here's an example `store.js`.  It uses Webpack's require.context feature save repetitive imports.\n\nSee it [here](/getting-started.html#auth-plugin)\n\nWith the `store.js` file in place, we can start adding services to the `services` folder.\n\n- [Learn how to setup the feathers-client.js file](/getting-started.html#feathers-client-feathers-vuex)\n- [Learn how to setup a Vuex plugin for a Feathers service](/getting-started.html#service-plugins)\n- [Learn how to setup the auth plugin](/getting-started.html#auth-plugin)\n\n## Actions return reactive store records\n\nPreviously, when you directly used the response from an action, the individual records were not reactive.  This meant that these plain objects wouldn't update when you updated the matching record in the store.\n\n```js\nmethods: {\n  ...mapActions('todos', { findTodos: 'find' })\n},\ncreated () {\n  this.findTodos({ query: {} })\n    .then(response => {\n      const todos = response.data || response\n      // Suppose firstTodo has an id of 'todo-1'\n      const firstTodo = todos[0]\n\n      // Now, when you update the data in the store...\n      this.$store.state.todos.keyedById['todo-1'].description = 'Updated description'\n\n      // ... the instance in the `find` response also updates.  Yay!\n      console.log(firstTodo.description) // --> 'Updated description'\n    })\n}\n```\n\nThis is a super convenient feature, and it works with all actions (except remove, of course) But be aware that **only the individual records returned are reactive**.  The lists, themselves, are not reactive.  So if another record comes in from the server that matches the query, the list will not update.  For reactive lists, you must use the `find` getter, as shown in the following example.\n\n```js\ncomputed: {\n  ...mapGetters('todos', { findTodosInStore: 'find' })\n  todos () {\n    return this.findTodosInStore({ query: {} }).data\n  }\n},\nmethods: {\n  ...mapActions('todos', { findTodos: 'find' })\n},\ncreated () {\n  this.findTodos({ query: {} })\n    .then(response => {\n      // In the find action, the 'todos' array is not a reactive list, but the individual records are.\n      const todos = response.data || response\n    })\n}\n```\n\nIn the above example, the computed `todos` will be a reactive list.  This means that when new records are added to the store, the list of todos will automatically update in the UI to include the new data.\n\nIn summary, you can plan on individual records in the action response data to be reactive, but if you need the actual arrays to be reactive to live queries, use the 'find' getter.\n\n## Basic Data Modeling with `instanceDefaults`\n\nSee the [instanceDefaults API](./model-classes.html#instancedefaults)\n\n## Handling Non-Reactive Data\n\nIf you are encountering a scenario where certain properties in your records are not reactive, it's probably because they\n\n1. Are not defined in the `instanceDefaults`.\n2. Are getting added to the record after it gets added to the Vuex store.\n\nThere are two ways to solve this:\n\n1. Add the property to the `instanceDefaults` (see the previous section, above)  This tends to be the simplest solution.\n2. Make sure the property is added in the responses from the API server.\n\n## Model-Specific Computed Properties\n\nYou may find yourself in a position where model-specific computed properties would be very useful. [github issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/163).  In Feathers-Vuex 1.7, these could be specified in the `instanceDefaults`.  As of 2.0, they are specified directly on each Model class:\n\n```js\nclass Post extends BaseModel {\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'Post'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      description: '',\n      isComplete: false,\n      comments: [],\n    }\n  }\n\n  // Specify computed properties as regular class properties\n  get numberOfCommenters () {\n      // Put your logic here.\n  },\n  set someOtherProp () {\n      //  Setters also work\n  }\n}\n```\n\n## Relationships for Populated Data\n\nIf you're looking for a great solution for populating data to work with Feathers-Vuex, check out [feathers-graph-populate](https://feathers-graph-populate.netlify.app/).\n\nA common task with almost any API is properly handling relationships between endpoints.  Imagine an API where you have `/todos` and `/users` services.  Each todo record can belong to a single user, so a todo has a `userId`.\n\n```js\n// GET todos/1\n{\n  id: 1,\n  description: 'Learn about the health benefits of a low-carb diet.',\n  isComplete: false,\n  userId: 5\n}\n```\n\nAnd a user response looks like this:\n\n```js\n// GET users/5\n{\n  id: 5,\n  name: 'Marshall',\n  username: 'marshallswain'\n  email: 'marshall@ilovehealthy.com'\n}\n```\n\nSuppose a requirement is put on the `/todos` service to populate the `user` in the response.  (As a super handy side note, this task is pretty easy when using [Matt Chaffe's](https://github.com/mattchewone) magical, efficient [feathers-shallow-populate hook](https://www.npmjs.com/package/feathers-shallow-populate))  So now the todo response looks like this:\n\n```js\n{\n  id: 1,\n  description: 'Learn about the health benefits of a low-carb diet.',\n  isComplete: false,\n  userId: 5,\n  user: {\n    id: 5,\n    name: 'Marshall',\n    username: 'marshallswain'\n    email: 'marshall@ilovehealthy.com'\n  }\n}\n```\n\nCan you see the problem that will occur with this response?  When this record is put into the `/todos` store, it will contain a copy of the user record.  But we already have the user record in the `/users` store.  And what happens when the user data changes?  Now it's out of sync.  To keep it in sync, you might have to manually listen for `users updated` & `users patched` events.  Then you might have to write a custom mutation to update the user record attached to every applicable `todo` record.  This gets messy, fast!\n\nThere's an easier way to solve this problem. Use the new [`setupInstance` method on Model classes](/model-classes.html#setupinstance).\n\n```js\nimport feathersClient, { makeServicePlugin, BaseModel } from '../feathers-client'\n\nclass Todo extends BaseModel {\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'Todo'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      email: '',\n      password: ''\n    }\n  }\n  // Updates `data.user` to be an instance of the `User` class.\n  static setupInstance(data, { models }) {\n    if (data.user) {\n      data.user = new models.api.User(data.user)\n    }\n    return data\n  }\n}\n\nconst servicePath = 'todos'\nconst servicePlugin = makeServicePlugin({\n  Model: Todo,\n  service: feathersClient.service(servicePath),\n  servicePath\n})\n```\n\nWhen this record is instantiated, the `user` attribute will first be turned into a User [model instance](./model-classes.html), stored properly in the `/users` store. The `todo.user` attribute will be a reference to that user.  No more duplicate data!  Here's an example of how to set this up.\n\nThere's another amazing benefit from these relationships.  Because `feathers-vuex` listens to real-time events and keeps data up to date, when the user record changes, the `todo.user` automatically updates!\n\n### Handling Sequelize Joins\n\nSee [this issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/404) for a discussion on how to handle joins with Sequelize.  It's important to specify `raw: false`, as shown in [this comment](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/404#issuecomment-571774598).\n\n### Workflow for Saving Model Associations\n\nA great issue was opened on GitHub about the [Workflow for clone and save Model with associations](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/278).  That's a great issue to read to get familiar with the workflow.\n\n## Form Binding\n\nUse the Model classes to reduce the boilerplate required to work with forms and Vuex, even in strict mode!  Every model instance has a `.clone()` method which can be used to get a fully-reactive copy of the record in the store.  Here is a very simple version of how you could bind to a form and submit new data to the server.\n\n```vue\n<template>\n  <div class=\"bg-white h-full p-6\">\n    <h1>Create Todo</h1>\n\n    <form @submit.prevent=\"createTodo\">\n      <input v-model=\"clone.name\" type=\"text\" class=\"form-input\" />\n      <button\n        type=\"submit\"\n        class=\"bg-blue-500 px-4 py-2 rounded text-white ml-2\"\n      >\n        Create Todo\n      </button>\n    </form>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Todos',\n  mixins: [makeFindMixin({ service: 'todos', watch: true })],\n  data: () => ({\n    todo: null,\n    clone: null\n  }),\n  computed: {\n    todosParams() {\n      return {\n        query: {},\n        paginate: false\n      }\n    }\n  },\n  created() {\n    const { Todo } = this.$FeathersVuex.myApi\n    this.todo = new Todo({})\n    this.clone = this.todo.clone()\n  },\n  methods: {\n    async createTodo() {\n      try {\n        const todo = await this.clone.save()\n        console.log(todo)\n      } catch (error) {\n        console.log(error)\n      }\n    },\n    updateTodo(ev, todo) {\n      todo.isComplete = ev.target.checked\n      todo.save()\n    }\n  }\n}\n</script>\n\n<style lang=\"postcss\"></style>\n```\n\n## Multiple Copies\n\nThe previous version of `feathers-vuex` was hard-coded to allow for a single `current` record and one copy.  It was pretty easy to hit that limit.  This new release allows for keeping many more copies, one copy per stored record.  To make it easier to comply with Vuex's `strict` mode, copies are not kept in the store by default, but are instead kept on `Model.copiesById`.  You can make changes to the copies without having to make custom mutations, then you can commit them back into the store:\n\n```js\nconst { Todo } = this.$FeathersVuex\n\n// Create two records in the store (since they have ids, they get stored)\nconst todo = new Todo({ id: 1, description: 'Become more aware of others.'})\nconst todo2 = new Todo({ id: 2, description: 'Heal one ailments through healthy eating.'})\n\n// Create a deep-cloned copies in Todo.copiesById\nconst todoCopy = todo.clone()\nconst todoCopy2 = todo2.clone()\n\n// Try to clone a copy, and fail.\ntodoCopy.clone() // --> Error: You cannot clone a copy.\ntodoCopy2.clone() // --> Error: You cannot clone a copy.\n\n// Modify the copies.\ntodoCopy.description.replace('others', 'self')\ntodoCopy2.description.replace('one', 'all')\n\n// and update the original records\ntodoCopy.commit()\ntodoCopy2.commit()\n```\n\nYou can use the `keepCopiesInStore`<Badge text=\"deprecated\" type=\"warning\"/> option to make this service keep all of its copies in `state.copiesById`.  Remember that to comply with Vuex `strict` mode (if that's a concern for you), you'll have to write custom mutations.  If it's not a concern (maybe you're the sole developer or whatever reason), you could technically turn off `strict` mode, enable `keepCopiesInStore`, and modify them however you desire, ignoring custom mutations.\n\n```js\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport feathersVuex from 'feathers-vuex'\nimport feathersClient from './feathers-client'\n\nconst { service, auth, FeathersVuex } = feathersVuex(feathersClient, { idField: '_id' })\n\nVue.use(FeathersVuex)\nVue.use(Vuex)\n\nexport default new Vuex.Store({\n  plugins: [\n    service('todos', {\n      keepCopiesInStore: true,\n      instanceDefaults: {\n        description: '',\n        complete: false\n      }\n    })\n  ]\n})\n```\n\n## Enable Debug Logging\n\nIf items aren't not getting added to the store properly, try setting the `debug` option on the `makeServicePlugin` to `true`.  It enables some additional logging that may be useful for troubleshooting.\n\n## Full nuxt example\n\nIn this example we will create a nuxt configuration with all the features discribed in the [Nuxt Section](./nuxt.md).\n\nOur application is hosted in 2 different endpoints, one for the feathers api and another with our nuxt SSR, and we will also implement a permission system that will prevent users without an `admin` permission to get into some pages. For that you will need to [customize your payload](https://docs.feathersjs.com/cookbook/authentication/stateless.html#customizing-the-payload) in your feathers api.\n\n```js\n// nuxt.config.js\nexport default {\n  env: {\n    API_URL: process.env.API_URL || 'http://localhost:3030'\n  },\n  router: {\n    middleware: [\n      'feathers'\n    ]\n  },\n  plugins: [\n    { src: '~/plugins/feathers-vuex.js' }\n  ],\n  modules: [\n    'nuxt-client-init-module'\n  ],\n  build: {\n    transpile: [\n      'feathers-vuex'\n    ]\n  }\n}\n```\n\nThe `feathers` middleware will redirect any user in a non public page to the login, but will also redirect a loged user away from any public pages.\n\n```js\n// ~/middleware/feathers.js\nexport default function ({ store, redirect, route }) {\n  const { auth } = store.state\n  if (auth.publicPages.length > 0 && !auth.publicPages.includes(route.name) && !auth.payload) {\n    return redirect('/login')\n  } else if (auth.publicPages.length > 0 && auth.publicPages.includes(route.name) && auth.payload) {\n    return redirect('/feed')\n  }\n}\n```\n\nThe `admin` middleware will redirect any user that is not loged in or do not have the `admin` permission to a `not-permited` page.\n\n```js\n// ~/middleware/admin.js\nexport default function ({ store, redirect }) {\n  const { auth } = store.state\n  const permissions = auth.payload.permissions\n  if (\n    !auth.payload ||\n    !permissions.includes('admin')\n  ) {\n    return redirect('/not-permited')\n  }\n}\n```\nAdd the `admin` middleware to the administrative pages\n\n```js\n// ~/pages/**\nexport default {\n\n  ...\n\n  middleware: ['admin']\n\n  ...\n\n}\n```\n\nIn the feathers client configuration we will use a different transport for the nuxt-server > api comunication and the nuxt-client > api. When we are making request on the server we do not need the socket io realtime messaging system so we can use an rest configuration for better performance.\n\n```js\n// ~/plugins/feathers.js\nimport feathers from '@feathersjs/feathers'\nimport rest from '@feathersjs/rest-client'\nimport axios from 'axios'\nimport socketio from '@feathersjs/socketio-client'\nimport auth from '@feathersjs/authentication-client'\nimport io from 'socket.io-client'\nimport { CookieStorage } from 'cookie-storage'\nimport { iff, discard } from 'feathers-hooks-common'\nimport feathersVuex, { initAuth, hydrateApi } from 'feathers-vuex'\n// Get the api url from the environment variable\nconst apiUrl = process.env.API_URL\nlet socket\nlet restClient\n// We won't use socket to comunicate from server to server\nif (process.client) {\n  socket = io(apiUrl, { transports: ['websocket'] })\n} else {\n  restClient = rest(apiUrl)\n}\nconst transport = process.client ? socketio(socket) : restClient.axios(axios)\nconst storage = new CookieStorage()\n\nconst feathersClient = feathers()\n  .configure(transport)\n  .configure(auth({ storage }))\n  .hooks({\n    before: {\n      all: [\n        iff(\n          context => ['create', 'update', 'patch'].includes(context.method),\n          discard('__id', '__isTemp')\n        )\n      ]\n    }\n  })\n\nexport default feathersClient\n\n// Setting up feathers-vuex\nconst { makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex } = feathersVuex(\n  feathersClient,\n  {\n    serverAlias: 'api', // optional for working with multiple APIs (this is the default value)\n    idField: '_id', // Must match the id field in your database table/collection\n    whitelist: ['$regex', '$options'],\n    enableEvents: process.client // Prevent memory leak\n  }\n)\n\nexport { makeAuthPlugin, makeServicePlugin, BaseModel, models, FeathersVuex, initAuth, hydrateApi }\n```\n\nI prefere install the `FeathersVuex` plugin in a separate file, it's more consistent with nuxt patterns.\n\n```js\n// ~/plugins/feathers-vuex.js\nimport Vue from 'vue'\nimport { FeathersVuex } from './feathers'\n\nVue.use(FeathersVuex)\n```\n\nConfigure any service you want on `~/store/services/*.js`.\n\n```js\n// ~/store/services/users.js\nimport feathersClient, { makeServicePlugin, BaseModel } from '~/plugins/feathers'\n\nclass User extends BaseModel {\n  constructor(data, options) {\n    super(data, options)\n  }\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'User'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      email: '',\n      password: '',\n      permissions: []\n    }\n  }\n}\nconst servicePath = 'users'\nconst servicePlugin = makeServicePlugin({\n  Model: User,\n  service: feathersClient.service(servicePath),\n  servicePath\n})\n\n// Setup the client-side Feathers hooks.\nfeathersClient.service(servicePath).hooks({\n  before: {\n    all: [],\n    find: [],\n    get: [],\n    create: [],\n    update: [],\n    patch: [],\n    remove: []\n  },\n  after: {\n    all: [],\n    find: [],\n    get: [],\n    create: [],\n    update: [],\n    patch: [],\n    remove: []\n  },\n  error: {\n    all: [],\n    find: [],\n    get: [],\n    create: [],\n    update: [],\n    patch: [],\n    remove: []\n  }\n})\n\nexport default servicePlugin\n```\n\nCreate your nuxt store with the right nuxt pattern, exporting an Vuex store will be deprecated on nuxt 3.\n\n```js\n// ~/store/index.js\nimport { makeAuthPlugin, initAuth, hydrateApi, models } from '~/plugins/feathers'\nconst auth = makeAuthPlugin({\n  userService: 'users',\n  state: {\n    publicPages: [\n      'login',\n      'signup'\n    ]\n  },\n  actions: {\n    onInitAuth ({ state, dispatch }, payload) {\n      if (payload) {\n        dispatch('authenticate', { strategy: 'jwt', accessToken: state.accessToken })\n          .then((result) => {\n            // handle success like a boss\n            console.log('loged in')\n          })\n          .catch((error) => {\n            // handle error like a boss\n            console.log(error)\n          })\n      }\n    }\n  }\n})\n\nconst requireModule = require.context(\n  // The path where the service modules live\n  './services',\n  // Whether to look in subfolders\n  false,\n  // Only include .js files (prevents duplicate imports`)\n  /.js$/\n)\nconst servicePlugins = requireModule\n  .keys()\n  .map(modulePath => requireModule(modulePath).default)\n\nexport const modules = {\n  // Custom modules\n}\n\nexport const state = () => ({\n  // Custom state\n})\n\nexport const mutations = {\n  // Custom mutations\n}\n\nexport const actions = {\n  // Custom actions\n  nuxtServerInit ({ commit, dispatch }, { req }) {\n    return initAuth({\n      commit,\n      dispatch,\n      req,\n      moduleName: 'auth',\n      cookieName: 'feathers-jwt'\n    })\n  },\n  nuxtClientInit ({ state, dispatch, commit }, context) {\n\n    hydrateApi({ api: models.api })\n\n    if (state.auth.accessToken) {\n      return dispatch('auth/onInitAuth', state.auth.payload)\n    }\n  }\n}\n\nexport const getters = {\n  // Custom getters\n}\n\nexport const plugins = [ ...servicePlugins, auth ]\n```\n"
  },
  {
    "path": "docs/composition-api.md",
    "content": "---\ntitle: Composition API\nsidebarDepth: 3\n---\n\n# Feathers-Vuex Composition API <Badge text=\"3.0.0+\" />\n\nIn addition to the Renderless Components and the Mixins, Feathers-Vuex includes utilities that let you take advantage of the [Vue Composition API](https://github.com/vuejs/composition-api).\n\nIt's important to note that the `@vue/composition-api` plugin must be registered BEFORE you import `App.vue`.  This means that you need to modify your `main.js` file to look like this:\n\n```js\nimport Vue from 'vue'\nimport VueCompositionApi from '@vue/composition-api'\n\n// Register the Composition API plugin BEFORE you import App.vue\nVue.use(VueCompositionApi)\n\nimport App from './App.vue'\nimport router from './router'\nimport store from './store/store'\nimport './plugins/plugins'\nVue.config.productionTip = true\nnew Vue({\n  router,\n  store,\n  render: h => h(App)\n}).$mount('#app')\n```\n\nIf you forget to register the plugin, first, you will see a console warning from Vue stating `[Vue warn]: Error in data(): \"Error: [vue-composition-api] must call Vue.use(plugin) before using any function.\".  To fix this, make sure to register the plugin BEFORE you call any Composition utility functions, as shown above.\n\n## Setup\n\nBefore you can use the `useFind` and `useGet` composition functions, you'll need to [install the Vue Composition API](https://github.com/vuejs/composition-api#Installation) plugin.\n\n## Detour: Reading a TypeScript interface\n\nThe next few sections show various TypeScript interfaces, which are basically shorthand descriptions of the types of data that make up a variable.  In this case, they're used to show the `options` object which can be passed to each of the composition api utilities.  If this is your first time with interfaces, here's a quick primer as an alternative to reading the [TypeScript interface docs](https://www.typescriptlang.org/docs/handbook/interfaces.html):\n\n- In the [first interface example](#options), below, `UseFindOptions` is the name of the interface, similar to naming any other variable.  When using TypeScript, you can import and pass interfaces around like variables.\n- Each line of the interface describes a property.\n- The part before the `:` is the name of the property.\n- The part after the `:` describes what type of variable it can be.\n- You can look at any `|` after the `:` as a conditional \"or\"\n- Any property followed by a `?` is optional.\n- Any property not followed by a `?` is required.\n\n## useFind <Badge text=\"3.0.0+\" />\n\nThe `useFind` utility reduces boilerplate for querying with fall-through cache and realtime updates.  To get started with it you provide a `model` class and a computed `params` object.\n\nLet's use the example of creating a User Guide, where we need to pull in the various `Tutorial` records from our `tutorials` service.  We'll keep it simple in the template and just show a list of the names.\n\n```html\n<template>\n  <div>\n    <li v-for=\"tutorial in tutorials\" :key=\"tutorial._id\">\n      {{ tutorial.name }}\n    </li>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind } from 'feathers-vuex'\n\nexport default {\n  name: 'UserGuide',\n  setup(props, context) {\n    // 1. Get a reference to the model class\n    const { Tutorial } = context.root.$FeathersVuex.api\n\n    // 2. Create a computed property for the params\n    const tutorialsParams = computed(() => {\n      return {\n        query: {}\n      }\n    })\n    // 3. Provide the model and params in the options\n    const tutorialsData = useFind({ model: Tutorial, params: tutorialsParams })\n\n    // 4. Return the data, named as you prefer\n    return {\n      tutorials: tutorialsData.items\n    }\n  }\n}\n</script>\n```\n\nLet's review each of the numbered comments, above:\n\n1. Get a reference to the model class.  With the Vue Composition API, there's no `this` object.  It has been replaced by the context object.  So, only when using the composition API, the `$FeathersVuex` object is found at `context.root.$FeathersVuex`\n2. Create a computed property for the params. Return an object with a nested `query` object.\n\n### Options\n\nSince we learned earlier [how to read a TypeScript interface](#detour-reading-a-typescript-interface), let's look at the TypeScript definition for the `UseFindOptions` interface.\n\n```ts\ninterface UseFindOptions {\n  model: Function\n  params: Params | Ref<Params>\n  fetchParams?: Params | Ref<Params>\n  queryWhen?: Ref<Function>\n  qid?: string\n  immediate?: boolean\n}\n```\n\nAnd here's a look at each individual property:\n\n- `model` must be a Feathers-Vuex Model class. The Model's `find` and `findInStore` methods are used to query data.\n- `params` is a FeathersJS Params object OR a Composition API `ref` (or `computed`, since they return a `ref` instance) which returns a Params object.\n  - When provided alone (without the optional `fetchParams`), this same query is used for both the local data store and the API requests.\n  - Explicitly returning `null` will prevent an API request from being made.\n  - You can use `params.qid` to dynamically specify the query identifier for any API request. The `qid` is used for tracking pagination data and enabling the fall-through cache across multiple queries.\n  - Set `params.paginate` to `true` to turn off realtime updates for the results and defer pagination to the API server.  Pagination works the same as for the [makeFindMixin pagination](./mixins.html#pagination-with-fall-through-cacheing).\n  - Set `params.debounce` to an integer and the API requests will automatically be debounced by that many milliseconds.  For example, setting `debounce: 1000` will assure that the API request will be made at most every 1 second.\n  - Set `params.temps` to `true` to include temporary (local-only) items in the results. Temporary records are instances that have been created but not yet saved to the database.\n  - Set `params.copies` to `true` to include cloned items in the results. The queried items get replaced with the corresponding copies from `copiesById`\n- `fetchParams` This is a separate set of params that, when provided, will become the params sent to the API server.  The `params` will then only be used to query data from the local data store.\n  - Explicitly returning `null` will prevent an API request from being made.\n- `queryWhen` must be a `computed` property which returns a `boolean`. It provides a logical separation for preventing API requests *outside* of the `params`.\n- `qid` allows you to specify a query identifier (used in the pagination data in the store).  This can also be set dynamically by returning a `qid` in the params.\n- `immediate`, which is `true` by default, determines if the internal `watch` should fire immediately.  Set `immediate: false` and the query will not fire immediately.  It will only fire on subsequent changes to the params.\n\n### Returned Attributes\n\nNotice the `tutorialsData` in the previous example.  You can see that there's an `tutorialsData.items` property, which is returned at the bottom of the `setup` method as `tutorials`.  There are many more attributes available in the object returned from `useFind`. We can learn more about the return values by looking at its TypeScript interface, below.\n\n```ts\ninterface UseFindData {\n  items: Ref<any>\n  paginationData: Ref<object>\n  servicePath: Ref<string>\n  qid: Ref<string>\n  isPending: Ref<boolean>\n  haveBeenRequested: Ref<boolean>\n  haveLoaded: Ref<boolean>\n  error: Ref<Error>\n  debounceTime: Ref<number>\n  latestQuery: Ref<object>\n  isLocal: Ref<boolean>\n  find: Function\n}\n```\n\nLet's look at the functionality that each one provides:\n\n- `items` is the list of results. By default, this list will be reactive, so if new items are created which match the query, they will show up in this list automagically.\n- `servicePath` is the FeathersJS service path that is used by the current model. This is mostly only useful for debugging.\n- `isPending` is a boolean that indicates if there is an active query.  It is set to `true` just before each outgoing request.  It is set to `false` after the response returns.  Bind to it in the UI to show an activity indicator to the user.\n- `haveBeenRequested` is a boolean that is set to `true` immediately before the first query is sent out.  It remains true throughout the life of the component.  This comes in handy for first-load scenarios in the UI.\n- `haveLoaded` is a boolean that is set to true after the first API response.  It remains `true` for the life of the component. This also comes in handy for first-load scenarios in the UI.\n- `isLocal` is a boolean that is set to true if this data is local only.\n- `qid` is currently the primary `qid` provided in params.  It might become more useful in the future.\n- `debounceTime` is the current number of milliseconds used as the debounce interval.\n- `latestQuery` is an object that holds the latest query information.  It populates after each successful API response. The information it contains can be used to pull data from the `paginationData`.\n- `paginationData` is an object containing all of the pagination data for the current service.\n- `error` is null until an API error occurs. The error object will be serialized into a plain object and available here.\n- `find` is the find method used internally.  You can manually make API requests.  This is most useful for when you have `paginate: true` in the params.  You can manually query refreshed data from the server, when desired.\n\n### Working with Refs\n\nPay special attention to the properties of type `Ref`, in the TypeScript interface, above.  Those properties are Vue Composition API `ref` instances.  This means that you need to reference their value by using `.value`.  In the next example the `completeTodos` and `incompleteTodos` are derived from the `todos`, using `todos.value`\n\n```html\n<template>\n  <div>\n    <li v-for=\"tutorial in tutorials\" :key=\"tutorial._id\">\n      {{ tutorial.name }}\n    </li>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind } from 'feathers-vuex'\n\nexport default {\n  name: 'UserGuide',\n  setup(props, context) {\n    const { Todo } = context.root.$FeathersVuex.api\n\n    const todosParams = computed(() => {\n      return {\n        query: {}\n      }\n    })\n    const { items: todos } = useFind({ model: Todo, params: todosParams })\n    // Notice the \"todos.value\"\n    const completeTodos = computed(() => todos.value.filter(todo => todo.isComplete))\n    const incompleteTodos = computed(() => todos.value.filter(todo => !todo.isComplete))\n\n    return {\n      todos,\n      completeTodos,\n      incompleteTodos\n    }\n  }\n}\n</script>\n```\n\n### Comparison to `makeFindMixin`\nIf you have already used the `makeFindMixin`, the `useFind` composition function will be very familiar, since it offers the same functionality in a more powerful way.  There are a few differences, though.\n\n1. `useFind` is more TypeScript friendly. Since the mixins depended on adding dynamic attribute names that wouldn't overlap, TypeScript tooling and autocomplete weren't very effective.  The attributes returned from `useFind` are always consistent.\n1. Instead of providing a service name, you provide a service Model from the `$FeathersVuex` Vue plugin.\n1. The default behavior of `useFind` is to immediately query the API server. The `makeFindMixin`, by default, would wait until the watcher noticed the change.  This is to match the default behavior of `watch` in the Vue Composition API.  You can pass `{ immediate: false }` in the `useFind` options, which will be passed directly to the internal `watch` on the params.\n\nNote that with the Vue Options API (aka the only way to write components in Vue 2.0) the models are found in `this.$FeathersVuex`.  With the Vue Composition API, this object is now at `context.root.$FeathersVuex`.\n\n## useGet <Badge text=\"3.0.0+\" />\n\nThe `useGet` Composition API utility provides the same fall-through cache functionality as `useFind`.  It has a slightly simpler API, only requiring a `model` and `id` instead of the `params` object.  Still, the `params` object can be used to send along additional query parameters in the request.  Below is an example of how you might use the `useGet` utility.\n\n```html\n<template>\n  <div>\n    <div v-if=\"post\">{{ post.body }}</div>\n    <div v-else-if=\"isPending\">Loading</div>\n    <div v-else>Post not found.</div>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind, useGet } from 'feathers-vuex'\n\nexport default {\n  name: 'BlogPostView',\n  props: {\n    id: {\n      type: String,\n      required: true\n    }\n  },\n  setup(props, context) {\n    const { Post } = context.root.$FeathersVuex.api\n\n    // Get the patient record\n    const { item: post, isPending } = useGet({\n      model: Post,\n      id: props.id\n    })\n\n    return {\n      post,\n      isPending\n    }\n  }\n}\n<script>\n```\n\nSee the [Routing with useGet](#routing-with-useget) portion of the patterns section, below, to see how to hook up the above component to vue-router.\n\n### Options\n\nWe learned earlier [how to read a TypeScript interface](#detour-reading-a-typescript-interface), so let's look at the TypeScript interface for the `UseGetOptions`.\n\n```ts\ninterface UseGetOptions {\n  model: Function\n  id: null | string | number | Ref<null> | Ref<string> | Ref<number>\n  params?: Params | Ref<Params>\n  queryWhen?: Ref<Function>\n  local?: boolean\n  immediate?: boolean\n}\n```\n\nAnd here's a look at each individual property:\n\n- `model` must be a Feathers-Vuex Model class. The Model's `get` and `getFromStore` methods are used to query data.\n- `id` must be a record's unique identifier (`id` or `_id`, usually) or a ref or computed property which returns one.\n  - When the `id` changes, the API will be queried for the new record (unless `queryWhen` evaluates to `false`).\n  - If the `id` is `null`, no query will be made.\n- `params` is a FeathersJS Params object OR a Composition API `ref` (or `computed`, since they return a `ref` instance) which returns a Params object.\n  - Unlike the `useFind` utility, `useGet` does not currently have built-in debouncing.\n- `queryWhen` must be a `computed` property which returns a `boolean`. It provides a logical separation for preventing API requests apart from `null` in the `id`.\n- `immediate`, which is `true` by default, determines if the internal `watch` should fire immediately.  Set `immediate: false` and the query will not fire immediately.  It will only fire on subsequent changes to the `id` or `params`.\n\n### Returned Attributes\n\n```ts\ninterface UseGetData {\n  item: Ref<any>\n  servicePath: Ref<string>\n  isPending: Ref<boolean>\n  hasBeenRequested: Ref<boolean>\n  hasLoaded: Ref<boolean>\n  isLocal: Ref<boolean>\n  error: Ref<Error>\n  get: Function\n}\n```\n\n## FeathersVuexPagination\n\nAs of version `3.8.0`, Feathers-Vuex includes the `FeathersVuexPagination` renderless component.  This component pairs with the [`useFind` utility](#usefind) to simplify handling server-side pagination.  If you use the FeathersVuex Vue plugin, the `FeathersVuexPagination` component is registered as a global component.  Since it's a renderless component, you'll need to supply your own UI to the default slot.\n\n### Usage Steps\n\nThe steps to using the `FeathersVuexPagination` component include\n\n1. Create a `pagination` ref containing an object with `$limit` and `$skip` properties.\n2. Create a `params` computed property. Be sure to include a `query` object and `paginate: true` in the params.  It's also recommended that you use a `qid` to identify the component that you're using, otherwise pagination data could mix with other parts of the UI and create an inconsistent experience.\n3. Merge the `pagination` into the `params.query`.\n4. Pass the `params` object to the `useFind` utility.\n5. Pull the `items` array and `latestQuery` object from `useFind`'s return value.\n6. Make the `items`, `latestQuery`, and `pagination` available to the component's template by returning them in the setup method.\n7. Provide the above two variables to the `FeathersVuexPagination` component as props.\n8. Provide a UI inside the default slot, binding to variables and desired events.\n\n### Props\n\nThe `FeathersVuexPagination` component only accepts two props.\n\n- `v-model` (or `value`): Receives the `pagination` object, which must contain the `$limit` and `$skip` properties.\n- `latest-query`, which receives the `latestQuery` object returned by the `useFind` utility.\n\n### Default Slot Scope\n\nThe following variables and functions are provided by the default slot:\n\n- `currentPage` {Number} The current page number, based on the current `$limit` and `$skip` values provided to the `v-model`.\n- `pageCount` {Number} If the response from the API server includes a `total` attribute, the `pageCount` will be the total number of pages, based on the current value of `$limit`.\n- `canPrev` {Boolean} Will be true if not on the first page (can go to a previous page).  This value is useful for enabling/disabling a button in the UI, if you desire to give that kind of feedback to the user.\n- `canNext` {Boolean} Will be true if not on the last page (can go to a next page).  This value is useful for enabling/disabling a button in the UI, if you desire to give that kind of feedback to the user.\n- `toStart` {Function} When called, will move to the first page.\n- `toEnd` {Function} When called, will move to the last page.\n- `toPage(n: number)` {Function} When called with a page number as its first argument, will move to that page number.  If the page number is greater than the total number of pages, will go to the last page.  If the page number is less than 0, will go to the first page.\n- `prev` {Function} When called, moves to the previous page. Will not go below page 1.\n- `next` {Function} When called, moves tot the next page.  Will not go past the last page.\n\n### Example\n\nHere is an example of how to use it.  It assumes that you have a `/listings` service with a `Listing` model.  The next code example below this one shows the `PaginationUi` component.  Note that not all slot scope variables and functions are required.  To implement basic pagination, only the `currentPage`, `pageCount`, `next`, and `prev` values are really necessary.  The other slot scope values allow providing a customized pagination experience.\n\n```html\n<template>\n  <div>\n    <!-- 7. ^ -->\n    <FeathersVuexPagination v-model=\"pagination\" :latest-query=\"latestQuery\">\n      <!-- 8. ^ -->\n      <template #default=\"{ currentPage, pageCount, toStart, toEnd, toPage, next, prev, canNext, canPrev }\">\n        <PaginationUi\n          :current-page=\"currentPage\"\n          :page-count=\"pageCount\"\n          :can-prev=\"canPrev\"\n          :can-next=\"canNext\"\n          @to-start=\"toStart\"\n          @to-end=\"toEnd\"\n          @to-page=\"toPage\"\n          @next=\"next\"\n          @prev=\"prev\"\n        />\n      </template>\n    </FeathersVuexPagination>\n\n    <!-- Results -->\n    <div>\n      <div v-for=\"item in items\" :key=\"item._id\">\n        {{ item }}\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { ref, computed, watch } from '@vue/composition-api'\nimport { models, useFind, FeathersVuexPagination } from 'feathers-vuex'\nimport PaginationUi from './PaginationUi.vue'\n\nexport default {\n  name: 'PaginationExample',\n  components: {\n    FeathersVuexPagination,\n    PaginationUi\n  },\n  setup(props, context) {\n    const { Listing } = models.api\n\n    // 1.^\n    const pagination = ref({\n      $limit: 20,\n      $skip: 0\n    })\n    // 2.^\n    const params = computed(() => {\n      const query = {}\n      // 3.^\n      Object.assign(query, pagination.value)\n\n      return { query, qid: 'listingsPage', paginate: true }\n    })\n    // 4.^\n    const data = useFind({ model: Listing, params: params })\n\n    // 5.^  (can be merged into previous line)\n    const { items, latestQuery } = data\n\n    // 6.^\n    return { items, pagination, latestQuery }\n  }\n}\n</script>\n```\n\nAs promised, here is the example code for the `PaginationUi` component used above.  It includes some TailwindCSS utility classes in the markup in order to show how one might use the `canPrev` and `canNext` properties.  Keep in mind that this is just an example.  You can provide whatever experience you want for your users by creating your own component.\n\n```html\n<template>\n  <div class=\"flex flex-row items-center mt-2\">\n    <button\n      type=\"button\"\n      class=\"rounded py-0.5 flex flex-row items-center justify-center w-24 mr-1\"\n      :class=\"[\n        canPrev\n          ? 'bg-gray-600 text-white'\n          : 'bg-gray-400 text-gray-500 cursor-not-allowed'\n      ]\"\n      :disabled=\"!canPrev\"\n      @click=\"e => $emit('prev', e)\"\n    >\n      <ChevronLeftIcon />\n      <p class=\"pr-2\">Previous</p>\n    </button>\n\n    <button\n      type=\"button\"\n      class=\"bg-gray-600 text-white rounded py-0.5 flex flex-row items-center justify-center w-24 mr-1\"\n      @click=\"e => $emit('to-start', e)\"\n    >\n      <p class=\"pr-2\">To Start</p>\n    </button>\n\n    <div class=\"flex flex-grow justify-center items-center\">\n      Page\n      <input\n        :value=\"currentPage\"\n        type=\"number\"\n        :min=\"1\"\n        :max=\"pageCount\"\n        class=\"w-12 rounded py-0.5 mx-1.5 dark:bg-gray-900 text-center\"\n        @input=\"event => $emit('to-page', event.target.value)\"\n      />\n      of {{ pageCount }}\n    </div>\n\n    <button\n      type=\"button\"\n      class=\"bg-gray-600 text-white rounded py-0.5 flex flex-row items-center justify-center w-24 mr-1\"\n      @click=\"e => $emit('to-end', e)\"\n    >\n      <p class=\"pr-2\">To End</p>\n    </button>\n\n    <button\n      type=\"button\"\n      class=\"rounded py-0.5 flex flex-row items-center justify-center w-24\"\n      :class=\"[\n        canNext\n          ? 'bg-gray-600 text-white'\n          : 'bg-gray-400 text-gray-500 cursor-not-allowed'\n      ]\"\n      :disabled=\"!canNext\"\n      @click=\"e => $emit('next', e)\"\n    >\n      <div class=\"w-12\">Next</div>\n      <ChevronRightIcon />\n    </button>\n  </div>\n</template>\n\n<script>\nimport { ChevronLeftIcon, ChevronRightIcon } from 'vue-feather-icons'\n\nexport default {\n  name: 'SidebarPagination',\n  components: {\n    ChevronLeftIcon,\n    ChevronRightIcon\n  },\n  props: {\n    currentPage: {\n      type: Number,\n      required: true\n    },\n    pageCount: {\n      type: Number,\n      required: true\n    },\n    canPrev: {\n      type: Boolean,\n      required: true\n    },\n    canNext: {\n      type: Boolean,\n      required: true\n    }\n  }\n}\n</script>\n```\n\n## Patterns & Examples\n\n### Server-Side Pagination\n\nSimilar to what was introduced with the `makeFindMixin` in Feathers-Vuex 2.0, the `useFind` API supports server-side pagination.  It is enabled by passing `paginate: true` in the `params` (or the `fetchParams` if you're using separate queries).  For an overview of how it works, refer to the [makeFindMixin pagination docs](./mixins.html#pagination-with-fall-through-cacheing).\n\n### Simultaneous Queries\n\nLet's look at an example where we have two separate tables and we want live-queried lists for both of them.  This example will show a component for a doctor's office that pulls up a patient by `id` using `useGet` then retrieves all of the patient's `appointments` using `useFind`.\n\n```html\n<template>\n  <div>\n    <div>{{ patient.name }}</div>\n\n    <li v-for=\"appointment in appointments\" :key=\"appointment._id\">\n      {{ appointment.date }}\n    </li>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind, useGet } from 'feathers-vuex'\n\nexport default {\n  name: 'PatientAppointments',\n  props: {\n    id: {\n      type: String,\n      required: true\n    }\n  },\n  setup(props, context) {\n    const { Patient, Appointment } = context.root.$FeathersVuex.api\n\n    // Get the patient record\n    const { item: patient } = useGet({ model: Patient, id: props.id })\n\n    // Get all of the appointments belonging to the current patient\n    const appointmentsParams = computed(() => {\n      return {\n        query: {\n          userId: props.id,\n          $sort: { date: -1 }\n        }\n      }\n    })\n    const { items: appointments } = useFind({\n      model: Appointment,\n      params: appointmentsParams\n    })\n\n    return {\n      patient,\n      appointments\n    }\n  }\n}\n</script>\n```\n\n### Deferring Queries\n\nIn the previous example, the requests for the `patient` and `appointments` are made at the same time because the user's `id` is available, already.  What if we were required to load `appointments` after the `patient` record finished loading?  We could change the `appointmentsParams` to return `null` until the `patient` record becomes available, as shown in the following example:\n\n```html\n<template>\n  <div>\n    <div>{{ patient.name }}</div>\n\n    <li v-for=\"appointment in appointments\" :key=\"appointment._id\">\n      {{ appointment.date }}\n    </li>\n\n    <div v-if=\"!appointments.length && haveLoaded\">\n      No appointments have been scheduled for this patient.\n    </div>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind, useGet } from 'feathers-vuex'\n\nexport default {\n  name: 'PatientAppointments',\n  props: {\n    id: {\n      type: String,\n      required: true\n    }\n  },\n  setup(props, context) {\n    const { Patient, Appointment } = context.root.$FeathersVuex.api\n\n    // Get the patient record\n    const { item: patient } = useGet({ model: Patient, id: props.id })\n\n    // Get all of the appointments belonging to the current patient\n    const appointmentsParams = computed(() => {\n      // (1)\n      if (!patient.value) {\n        return null\n      }\n      // (2)\n      return {\n        query: {\n          userId: patient.value._id,\n          $sort: { date: -1 }\n        }\n      }\n    })\n    const { items: appointments, haveLoaded } = useFind({\n      model: Appointment,\n      params: appointmentsParams\n    })\n\n    return {\n      patient,\n      appointments,\n      haveLoaded\n    }\n  }\n}\n</script>\n```\n\nReviewing the above snippet, while there is no `patient` record, the `appointmentsParams` computed property returns `null` at comment `(1)`.  This will prevent any query from going out to the API server.\n\nOnce the `patient` has loaded, the full params object is returned at comment `(2)`.  This allows the `useFind` utility to make the request.\n\n### Showing Loading State\n\nThis next example builds on the previous one and adds loading state for both the `patient` and the `appointments`.\n\n```html\n<template>\n  <div>\n    <div v-if=\"isPatientLoading\">Loading</div>\n    <div v-else>{{ patient.name }}</div>\n\n    <li v-for=\"appointment in appointments\" :key=\"appointment._id\">\n      {{ appointment.date }}\n    </li>\n\n    <div v-if=\"!appointments.length && haveLoaded\">\n      No appointments have been scheduled for this patient.\n    </div>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind, useGet } from 'feathers-vuex'\n\nexport default {\n  name: 'PatientAppointments',\n  props: {\n    id: {\n      type: String,\n      required: true\n    }\n  },\n  setup(props, context) {\n    const { Patient, Appointment } = context.root.$FeathersVuex.api\n\n    const {\n      item: patient,\n      isPending: isPatientLoading\n    } = useGet({ model: Patient, id: props.id })\n\n    const appointmentsParams = computed(() => {\n      if (!patient.value) {\n        return null\n      }\n      return {\n        query: {\n          userId: patient.value._id,\n          $sort: { date: -1 }\n        }\n      }\n    })\n    const { items: appointments, haveLoaded } = useFind({\n      model: Appointment,\n      params: appointmentsParams\n    })\n\n    return {\n      patient,\n      isPatientLoading,\n      appointments,\n      haveLoaded\n    }\n  }\n}\n</script>\n```\n\n### Using queryWhen\n\nThe `queryWhen` option for both `useFind` and `useGet` comes in handy when you want to conditional prevent API queries.  One use case for this is to prevent extra queries by checking if an item already exists in the vuex store.  This next example shows how to stop the `get` request if you already have a patient record with the current `id`.\n\n```html\n<template>\n  <div>\n    <div v-if=\"isPatientLoading\">Loading</div>\n    <div v-else>{{ patient.name }}</div>\n  </div>\n</template>\n\n<script>\nimport { computed } from '@vue/composition-api'\nimport { useFind, useGet } from 'feathers-vuex'\n\nexport default {\n  name: 'PatientInfo',\n  props: {\n    id: {\n      type: String,\n      required: true\n    }\n  },\n  setup(props, context) {\n    const { Patient } = context.root.$FeathersVuex.api\n\n    const patientQueryWhen = computed(() => {\n      return !Patient.getFromStore(props.id)\n    })\n    const { item: patient, isPending: isPatientLoading } = useGet({\n      model: Patient,\n      id: props.id,\n      queryWhen: patientQueryWhen\n    })\n\n    return {\n      patient,\n      isPatientLoading\n    }\n  }\n}\n</script>\n```\n\nIn the above example, the `patientQueryWhen` computed property will return `true` if we don't already have a `Patient` record in the store with the current `props.id`.  While you could also achieve similar results by performing this logic inside of a `params` computed property, the `queryWhen` option works great as a \"master override\" to prevent unneeded queries.\n\n### Routing with useGet\n\nApps will commonly have one or more routes with an `:id` param.  This might be for viewing or editing data.  Vue Router has a feature that makes it easy to write reusable components without having to directly reference the `$route` object.  The key is to set the `props` attribute in a route definition to `true`.  Here's an example route:\n\n```js\n// router.js\nimport Vue from 'vue'\nimport Router from 'vue-router'\n\nVue.use(Router)\n\nexport default new Router({\n  routes: [\n    {\n      name: 'Post View',\n      path: '/posts/:id',\n      component: () =>\n        import(/* webpackChunkName: \"posts\" */ './views/Post.vue'),\n      props: true\n    }\n  ]\n})\n```\n\nNow, the `Post.vue` file only requires to have a `prop` named `id`.  Vue Router will pass the params from the route as props to the component.  See the [first useGet example](#useget) for a component that would work with the above route.  The vue-router documentation has more information about [Passing Props to Route Components](https://router.vuejs.org/guide/essentials/passing-props.html#passing-props-to-route-components)\n\n### Composing with Model types\n\nBoth `useGet` and `useFind` have an optional type parameter for the Model type which is used as the type for the returned item(s).\n\n```ts\n// Destructure Model class from global models object\nconst { User } = Vue.$FeathersVuex.api\n\n// use useGet with User Model\nuseGet<typeof User.prototype>(/* ... */)\n\n// use useFind with User Model\nuseFind<typeof User.prototype>(/* ... */)\n```\n\n## Conventions for Development\n\n### Params are Computed\n\nYou might notice throughout these docs that the params are consistently shown as `computed` properties.  For long-term maintainability, this is the recommended practice.\n\nComputed properties are read-only, so you can't push changes into them. This encourages declarative programming.  Think of a declarative query as having all of the instructions it needs to pull in data from whatever sources are required to build the query object. Writing declarative params will assist you in avoiding complex conditional conflicts as queries become more complex.\n\nIn contrast, an imperatively-written query would be a reactive object that you directly modify.  Think of imperative as pushing information into the query.  eg: `params.query.user = props.userId`.  When you have a lot of imperative code pushing parameters into the query, it's really easy to create conflicting logic.  So, keep in mind that while Feathers-Vuex will definitely handle an imperative-style query, your code will likely be less maintainable over the long run.\n\n### Naming Variables\n\nHaving a variable naming convention can really assist the developer onboarding process and long run ease of use.  Here are some guidelines that could prove useful while using the composition API utilities:\n\n- Params for `useFind` result in a list of records, and should therefore indicate plurality.\n- When used, params for `useGet` result in a single record, and should indicate singularity.\n\n```js\nimport { computed } from '@vue/composition-api'\nimport { useFind, useGet } from 'feathers-vuex'\n\nexport default {\n  name: 'MyComponent',\n  props: {\n    id: {\n      type: String,\n      required: true\n    }\n  },\n  setup(props, context) {\n    const { Comment } = context.root.$FeathersVuex.api\n\n    // Plural \"comments\" in the params for useFind\n    const commentsParams = computed(() => {\n      return { query: {} }\n    })\n    const commentsData = useFind({\n      model: Comment,\n      params: commentsParams\n    })\n    const { items: comments } = commentsData\n\n    // Singular \"comment\" in the params for useGet\n    const commentParams = computed(() => {\n      return { query: {} }\n    })\n    const commentData = useGet({\n      model: Comment,\n      id: props.id,\n      params: commentParams\n    })\n    const { item: comment } = commentData\n\n    return {\n      comments,\n      comment\n    }\n  }\n}\n```\n\nVariable naming becomes even more important when one service consumes the results of a previous service to make a query.\n\nNote: the destructuring of `commentsData` and `commentData`, above, could happen on the same line as `useFind` and `useGet`, but it's a bit more clear in the example to split it into separate steps.  For users who are accustomed to destructuring, it makes perfect sense to do so:\n\n```js\n// Destructure and rename \"item\" to \"comment\" in the same line as the call to `useGet`\nconst { item: comment } = useGet({\n  model: Comment,\n  id: props.id,\n  params: commentParams\n})\n\nreturn { comment }\n```\n"
  },
  {
    "path": "docs/data-components.md",
    "content": "---\ntitle: Renderless Data Components\nsidebarDepth: 3\n---\n\n# Renderless Data Components\n\nThere are three renderless data provider components: `<FeathersVuexFind>`, `<FeathersVuexGet>` and `<FeathersVuexCount>`. They simplify performing queries against the store and/or the API server. They make the data available inside each component's default slot.\n\nTo see why you might want to use these components, below are two example components that are functionally equivalent.\n\nHere's what it looks like to use the new component:\n\n```html\n<template>\n  <FeathersVuexFind\n    v-slot=\"{ items: categories }\"\n    service=\"categories\"\n    :params=\"{ query: {} }\"\n    watch=\"params\"\n  >\n    <section class=\"admin-categories\">\n      {{categories}}\n    </section>\n  </FeathersVuexFind>\n</template>\n\n<script>\nexport default {\n  name: 'admin-categories'\n}\n</script>\n```\n\nThe above example is functionally equivalent to this much longer example which doesn't use the new component:\n\n```html\n<template>\n  <section class=\"admin-categories\">\n    {{categories}}\n  </section>\n</template>\n\n<script>\nimport { mapState, mapGetters, mapActions } from 'vuex'\n\nexport default {\n  name: 'admin-categories',\n  computed: {\n    ...mapState('categories', { areCategoriesLoading: 'isFindPending' }),\n    ...mapGetters('categories', { findCategoriesInStore: 'find' }),\n    query () {\n      return {}\n    },\n    categories () {\n      return this.findCategoriesInStore({ query: this.query }).data\n    }\n  },\n  methods: {\n    ...mapActions('categories', { findCategories: 'find' })\n  },\n  created () {\n    this.findCategories({ query: this.query })\n  }\n}\n</script>\n```\n\n> To level up your skills, consider this content by Adam Wathan.  He wrote a terrific *free* article about [Renderless Components in Vue.js](https://adamwathan.me/renderless-components-in-vuejs/). I highly recommend you read it. He also created the *paid/premium* [Advanced Vue Component Design](https://adamwathan.me/advanced-vue-component-design/) course. His material influenced the creation of this component.\n\n## FeathersVuexFind\n\nThe `FeathersVuexFind` component retrieves data from the API server, puts it in the Vuex store, then transparently retrieves the live, reactive data from the store and displays it to the user.\n\n### Example\n\n```vue\n<FeathersVuexFind\n  v-slot=\"{ items: users }\"\n  service=\"users\"\n  :params=\"{ query: {} }\"\n  watch=\"query\"\n>\n  <section>\n    {{users}}\n  </section>\n</FeathersVuexFind>\n```\n\n### Props\n\n- `service {String}` - **required** the service path. This must match a service that has already been registered with FeathersVuex.\n- `query {Object}` <Badge text=\"deprecated\" type=\"warning\"/> **use `params` instead** - the query object. If only the `query` attribute is provided, the same query will be used for both the `find` getter and the `find` action. See the `fetchQuery` attribute for more information. When using server-side pagination, use the `fetchQuery` prop and the `query` prop for querying data from the local store. If the query is `null` or `undefined`, the query against both the API and store will be skipped. The find getter will return an empty array.\n- `watch {String|Array}` - specify the attributes of the `params` or `fetchParams` to watch. Pass 'params' to watch the entire params object. Pass 'params.query.name' to watch the 'name' property of the query. Watch is turned off by default, so the API server will only be queried once, by default. **Default: []**\n- `fetchQuery {Object}` <Badge text=\"deprecated\" type=\"warning\"/> **use `fetchParams` instead** - when provided, the `fetchQuery` serves as the query for the API server. The `query` param will be used against the service's local Vuex store. **Default: undefined**\n- `params {Object}` - the params object. If only the `params` attribute is provided, te same params will be used for both the `find` getter and the `find` action. See the `fetchParams` attribute for more information. <Badge text=\"3.12.0+\" />\n- `fetchParams {Object}` - when provided, the `fetchParams` servers as the params for the API server. The `params` will be used against the service's local Vuex store. <Badge text=\"3.12.0+\" />\n- `queryWhen {Boolean|Function}` - the query to the server will only be made when this evaluates to true.  **Default: true**\n- `local {Boolean}` - when set to true, will only use the `query` prop to get data from the local Vuex store. It will disable queries to the API server. **Default:false**\n- `editScope {Function}` - a utility function that allows you to modify the scope data, and even add attributes to it, before providing it to the default slot. You can also use it to pull data into the current component's data (though that may be less recommended, it can come in handy).  See the \"Scope Data\" section to learn more about what props are available in the scope object. **Default: scope => scope**\n- `temps {Boolean}` <Badge text=\"deprecated\" type=\"warning\"/> **use `params: { query: {}, temps: true }` instead** - Enable `temps` to include temporary records (from `state.tempsById`) in the find getter results. **Default: false**\n- `qid {String}` - The query identifier used for storing pagination data in the Vuex store. See the service module docs to see what you'll find inside. The default value is a random 10-character string. This means that by default, in theory, no two components will share the same pagination data, nor will they overwrite each other's pagination data. You can, of course, force them to use the same pagination data by giving them both the same `qid`, if there's a use case for that. **Default: randomString(10)**\n\n### Scope Data\n\n- `items {Array}` - The resulting array of records for find operations.\n- `isFindPending {Boolean}` - When there's an active request to the API server, this will be `true`.  This is not the same as the `isFindPending` from the Vuex state.  The value in the Vuex state is `true` whenever **any** component is querying data from that same service.  This `isFindPending` attribute is specific to each component instance.\n- `pagination {Object}` - pagination data from the Vuex store, keyed by the `qid` attribute.  By default, this will be specific to this component instance. (If you find a use case for sharing pagination between component instances, you can give both components the same `qid` string as a prop.)\n- `queryInfo {Object}` - the queryInfo for the `pagination` object. Includes the `total` prop for server side pagination\n- `pageInfo {Object}` - the pageInfo includes the queried ids and is necessary for server side pagination\n\n## FeathersVuexGet\n\nThe `FeathersVuexGet` component allows fetching data from directly inside a template.  It makes the slot scope available to the child components.  Note that in `feathers-vuex@3.3.0` the component now includes support for `params` and `fetchParams` props.  These are meant to replace the `query` and `fetchQuery` props.  The `params` allow you, for example, to configure a project to pass custom params to the server.  This would require use of custom hooks.\n\n### Example\n\n```html\n<template>\n  <FeathersVuexGet\n    v-slot=\"{ item: user }\"\n    service=\"users\"\n    :id=\"id\"\n    :params=\"params\"\n    :watch=\"[id, params]\"\n  >\n      {{ user }}\n  </FeathersVuexGet>\n</template>\n\n<script>\nexport default {\n  name: 'UserProfile',\n  computed: {\n    id() {\n      return this.$route.params.id\n    },\n    params() {\n      return {\n        $populateParams: {\n          name: 'withFollowers'\n        }\n      }\n    }\n  }\n}\n</script>\n```\n\n### Props\n\n- `service {String}` - **required** the service path. This must match a service that has already been registered with FeathersVuex.\n- `id {Number|String}` - when performing a `get` request, serves as the id for the request. This is automatically watched, so if the `id` changes, an API request will be made and the data will be updated.  **Default: undefined**\n- `query {Object}` <Badge text=\"deprecated\" type=\"warning\"/> **use `params` instead** - the query object. If only the `query` attribute is provided, the same query will be used for both the `get` getter and the `get` action. See the `fetchQuery` attribute for more information. When using server-side pagination, use the `fetchQuery` prop and the `query` prop for querying data from the local store. If the query is `null` or `undefined`, the query against both the API and store will be skipped. The find getter will return an empty array.\n- `watch {String|Array}` - specify the attributes of the `params` or `fetchParams` to watch. Pass 'params' to watch the entire params object. Pass 'params.query.name' to watch the 'name' property of the query. Watch is turned off by default, so the API server will only be queried once, by default.  The only exception is for the `id` prop.  The `id` prop in the `FeathersVuexGet` component is always watched.  **Default: []**\n- `fetchQuery {Object}` <Badge text=\"deprecated\" type=\"warning\"/> **use `fetchParams` instead** - when provided, the `fetchQuery` serves as the query for the API server. The `query` param will be used against the service's local Vuex store. **Default: undefined**\n- `params {Object}` - the params object. If only the `params` attribute is provided, te same params will be used for both the `get` getter and the `get` action. See the `fetchParams` attribute for more information.\n- `fetchParams {Object}` - when provided, the `fetchParams` servers as the params for the API server. The `params` will be used against the service's local Vuex store.\n- `queryWhen {Boolean|Function}` - the query to the server will only be made when this evaluates to true.  **Default: true**\n- `local {Boolean}`: when set to true, will only use the `params` prop to get data from the local Vuex store. It will disable queries to the API server. **Default:false**\n- `editScope {Function}` - a utility function that allows you to modify the scope data, and even add attributes to it, before providing it to the default slot. You can also use it to pull data into the current component's data (though that may be less recommended, it can come in handy).  See the \"Scope Data\" section to learn more about what props are available in the scope object. **Default: scope => scope**\n\n### Scope Data\n\n- `item {Object}` - The resulting record for the get operation.\n- `isGetPending {Boolean}` - When there's an active request to the API server, this will be `true`.  This is not the same as the `isGetPending` from the Vuex state.  The value in the Vuex state is `true` whenever **any** component is querying data from that same service.  This `isGetPending` attribute is specific to each component instance.\n\n## FeathersVuexCount <Badge text=\"3.12.0+\" />\n\nThe `FeathersVuexCount` component allows displaying a count of records. It makes the slot scope available to the child components. It adds `$limit: 0` to the passed params in the background. This will only run a (fast) counting query against the database.\n\n> **Note:** it only works for services with enabled pagination!\n\n```vue\n<FeathersVuexCount v-slot=\"{ total }\" service=\"users\" :params=\"{ query: {} }\">\n  <span>\n    {{ total }}\n  </span>\n</FeathersVuexCount>\n```\n\n### Props\n\n- `service {String}` - The path of the service\n- `params {Object}` - The params object passed to the `count` getter/action.\n- `fetchParams {Object}` - A seperate params object for the `count` action\n- `queryWhen {Boolean|Function(params)}` - the query to the server will only be made when this evaluates to true.  **Default: true**\n- `watch {String|Array}` - specify the attributes of the `params` or `fetchParams` to watch. Pass 'params' to watch the entire params object. Pass 'params.query.name' to watch the 'name' property of the query. Watch is turned off by default, so the API server will only be queried once, by default. **Default: []**\n- `local {Boolean}`: when set to true, will only use the `params` prop to get data from the local Vuex store. It will disable queries to the API server. **Default:false**\n\n### Scope Data\n\n- `total {Number}` - The number of found records.\n- `isCountPending {Boolean}` - When there's an active request to the API server, this will be `true`.\n\n## A note about the internal architecture\n\nThese components use Vuex getters (to query data from the local store) and actions (to query data from the API server).  When a `params` or `id` is provided, the components pull data from the API server and put it into the store. That same `params` or `id` is then used to pull data from the local Vuex store. Keep this in mind, especially when attempting to use server-side pagination. To use server-side pagination, use the `params` prop for pulling data from the local vuex store, then use the `fetchParams` prop to retrieve data from the API server.\n\n## Registering the components\n\nThese components are automatically registered globally when using the Feathers-Vuex Vue plugin.\n\nIf you prefer to manually register the component, pass `{ components: false }` as options when using the FeathersVuex Vue plugin, then do the following:\n\n```js\nimport { FeathersVuexFind, FeathersVuexGet, FeathersVuexCount } from 'feathers-vuex'\n\n// in your component\ncomponents: {\n  FeathersVuexFind,\n  FeathersVuexGet,\n  FeathersVuexCount\n}\n\n// or globally registered\nVue.component('FeathersVuexFind', FeathersVuexFind)\nVue.component('FeathersVuexGet', FeathersVuexGet)\nVue.component('FeathersVuexCount', FeathersVuexCount)\n```\n\n## Scope Data\n\nWhen using these components, the scope data will become available to the `FeathersVuexFind`, `FeathersVuexGet` and `FeathersVuexCount` tags. It's accessible using the `v-slot=\"props\"` attribute:\n\n```html\n<FeathersVuexFind v-slot=\"props\" service=\"categories\" :params=\"{ query: {} }\">\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\nIt's also possible to modify the scope data by passing a function as the `edit-scope` prop. See the example for [modifying scope data](#Modify-the-scope-data)\n\n### Destructuring props\n\nUse the object destructuring syntax to pull specific variables out of the `v-slot` object.  In the following example, instead of using `v-slot=\"props\"`, it directly accesses the `items` prop through destructuring:\n\n```html\n<FeathersVuexFind v-slot=\"{ items }\" service=\"categories\" :params=\"{ query: {} }\">\n  <div>\n    {{items}}\n  </div>\n</FeathersVuexFind>\n```\n\n### Renaming props with destructuring\n\nYou can also rename scope props through the Object destructuring syntax.  The  `v-slot` in the next example shows how to give the items a more-descriptive name:\n\n```html\n<FeathersVuexFind\n  v-slot=\"{ items: categories }\"\n  service=\"categories\"\n  :params=\"{ query: {} }\"\n>\n  <div>\n    {{categories}}\n  </div>\n</FeathersVuexFind>\n```\n\n## Usage Examples\n\n#### A basic find all\n\nIn this example, only the `service` attribute is provided. There is no `query` nor `id` provided, so no queries are made. So `props.items` in this example returns an empty array.\n\n```html\n<FeathersVuexFind v-slot=\"props\" service=\"todos\">\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\n#### Fetch data from the API and the same data from the Vuex store\n\nThis example fetches data from the API server because a query was provided.  Internally, this same `query` is used for both the `find` action and the `find` getter. Read other examples to see how to use distinct queries. Be aware that if you use pagination directives like `$skip` or `$limit`, you must use two queries to get the records you desire.\n\n```html\n<FeathersVuexFind v-slot=\"props\" service=\"todos\" :params=\"{ query: {} }\">\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\n#### Only get data from the local Vuex store\n\nIf you've already pulled a bunch of data from the server, you can use the `local` prop to only query the local data:\n\n```html\n<FeathersVuexFind v-slot=\"props\" service=\"todos\" :params=\"{ query: {} }\" local>\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\n#### Watch the query and re-fetch from the API\n\nSometimes you want to query new data from the server whenever the query changes.  Pass an array of attribute names to the `watch` attribute re-query whenever upon change.  This example watches the entire query object:\n\n```html\n<FeathersVuexFind\n  v-slot=\"props\"\n  service=\"todos\"\n  :params=\"{ query: { isComplete: true } }\"\n  watch=\"params\"\n>\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\nThis next example watches a single prop from the query:\n\n```html\n<FeathersVuexFind\n  v-slot=\"props\"\n  service=\"todos\"\n  :params=\"{ query: { isComplete: true, dueDate: 'today' } }\"\n  watch=\"params.query.dueDate\"\n>\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\nYou can also provide an array of strings to watch multiple properties:\n\n```html\n<FeathersVuexFind\n  v-slot=\"props\"\n  service=\"dogs\"\n  :params=\"{ query: { breed: 'mixed', bites: true, hasWorms: false }}\"\n  :watch=\"['params.query.breed', 'params.query.bites']\"\n>\n  <div>\n    {{props.items}}\n  </div>\n</FeathersVuexFind>\n```\n\n#### Use a distinct `params` and `fetchParams`\n\nIn this scenario, the `fetchParams` is be used to grab a larger dataset from the API server (all todos with a matching `userId`). The `params` is used by the `find` getter to display a subset of this data from the store.  If the `isComplete` attribute gets set to `true`, only completed todos will be displayed.  Since a `fetchParams` is provided, the `watch` strings will be modified internally to watch the `fetchParams` object.  This means if you are watching `params.query.userId` and you add a `fetchParams`, the component is smart enough to know you meant `fetchParams.query.userId`. You don't have to rewrite your `watch` attribute after adding a `fetchParams` prop.\n\n```html\n<template>\n  <FeathersVuexFind\n    v-slot=\"{ items: todos }\"\n    service=\"todos\"\n    :params=\"{ query: { isComplete } }\"\n    :fetch-params=\"{ query: { userId } }\"\n    watch=\"params.query.userId\"\n  >\n    <div>\n      {{todos}}\n    </div>\n  </FeathersVuexFind>\n</template>\n\n<script>\nexport default {\n  data: () => ({\n    isComplete: false,\n    userId: 1\n  })\n}\n</script>\n```\n\n#### Modify the scope data\n\nThe `edit-scope` function allows you to modify the scope before passing it down to the default slot.  This feature can be super useful for preparing the data for the template.  The `prepareCategories` method in this next example adds two properties to the scope data, which are used to create a nested category structure:\n\n```html\n<template>\n  <FeathersVuexFind\n    v-slot=\"{ parentCategories, categoriesByParent }\"\n    service=\"categories\"\n    :params=\"{ query: {} }\"\n    :edit-scope=\"prepareCategories\"\n  >\n    <ul>\n      <li v-for=\"parent in parentCategories\" :key=\"parent._id\">\n        <p>{{parent.name}}</p>\n        <ul>\n          <li v-for=\"child in categoriesByParent[parent.path]\" :key=\"child._id\">\n            {{child.name}}\n          </li>\n        </ul>\n      </li>\n    </ul>\n  </FeathersVuexFind>\n</template>\n\n<script>\nexport default {\n  methods: {\n    prepareCategories (scope) {\n      scope.parentCategories = scope.items.filter(cat => !cat.path.includes('/'))\n      scope.categoriesByParent = scope.parentCategories.reduce((acc, parentCat) => {\n        acc[parentCat.path] = scope.items.filter(cat => {\n          return cat.path.includes(parentCat.path) && cat.path !== parentCat.path\n        })\n        return acc\n      }, {})\n    }\n  }\n}\n</script>\n```\n\n#### server-side pagination\n\nWhen you want to use server-side pagination you need to pass the ids from the server to vuex. It can be done by a combination of `params`, `fetchParams` and `editScope` as described below. The `fetchParams`-prop is only computed after items from the server arrived. The ids for the `find` getter as well as the total amount of available values `total` are extracted by the `edit-scope` function and stored in `data`:\n\n```html\n<template>\n  <FeathersVuexFind\n    v-slot=\"{ items: todos }\"\n    :service=\"service\"\n    :params=\"internalParams\"\n    :fetch-params=\"fetchParams\"\n    :edit-scope=\"getPaginationInfo\"\n  >\n    <div>\n      {{todos}}\n    </div>\n  </FeathersVuexFind>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      service: 'users',\n      ids: [],\n      params: {\n        query: {\n          isComplete: true\n        },\n      },\n      total: 0,\n      limit: 10,\n      skip: 0\n    };\n  },\n  computed: {\n    internalParams() {\n      const { idField } = this.$store.state[this.service];\n      return {\n        query: {\n          [idField]: {\n            $in: this.ids\n          }\n        }\n      };\n    },\n    fetchParams() {\n      const query = Object.assign({}, this.params.query, { $limit: this.limit, $skip: this.skip });\n\n      return Object.assign({}, this.params, { query });\n    }\n  },\n  methods: {\n    getPaginationInfo (scope) {\n      const { queryInfo, pageInfo } = scope;\n\n      this.total = queryInfo.total;\n      if (pageInfo && pageInfo.ids) {\n        this.ids = pageInfo.ids;\n      }\n    }\n  }\n}\n</script>\n```\n\n#### Query when certain conditions are met\n\nSometimes you only want to query the API server when certain conditions are met.  This example shows how to query the API server when the `userSearch` has as least three characters.  This property does not affect the internal `find` getter, so the `items` will still update when the `userSearch` property has fewer than three characters, just no API request will be made.  The `isFindPending` attribute is used to indicate when data is being loaded from the server.\n\n```html\n<template>\n  <div>\n    <input type=\"text\" v-model=\"userSearch\"/>\n\n    <FeathersVuexFind\n      v-slot=\"{ items: users, isFindPending: areUsersLoading }\"\n      service=\"users\"\n      :params=\"usersParams\"\n      watch=\"params\"\n      :queryWhen=\"userSearch.length > 2\"\n    >\n      <ul :class=\"[ areUsersLoading && 'is-loading' ]\">\n        <li v-for=\"user in users\" :key=\"user._id\">\n          {{user.email}}\n        </li>\n      </ul>\n    </FeathersVuexFind>\n  </div>\n</template>\n\n<script>\nexport default {\n  data: () => ({\n    userSearch: ''\n  }),\n  computed: {\n    usersParams () {\n      return {\n        query: {\n          email: { $regex: this.userSearch, $options: 'igm' },\n          $sort: { email: 1 }\n        }\n      }\n    }\n  }\n}\n</script>\n```\n\n#### Use a get request\n\nYou can perform `get` requests with the `FeathersVuexGet` component and its `id` property.  In the next example, when the `selectedUserId` changes, a get request will automatically fetch and display the matching user record.  It also shows how to use the `isGetPending` prop to update the UI\n\n```html\n<FeathersVuexGet\n  v-slot=\"{ item: currentUser, isGetPending }\"\n  service=\"todos\"\n  :id=\"selectedUserId\"\n>\n  <div>\n    <div v-if=\"isGetPending\" class=\"loading\"> loading... </div>\n    {{currentUser}}\n  </div>\n</FeathersVuexGet>\n```\n"
  },
  {
    "path": "docs/example-applications.md",
    "content": "---\ntitle: Example Applications\nsidebarDepth: 3\n---\n\n# Example Applications\n\nOn this page you will find any example applications using Feathers-Vuex that have been shared by the community.  If there's something you would like to see here, feel free to make a PR to add it to the [Community Examples list](#community-examples).\n\n## Feathers Chat\n\nThe [Feathers Chat Example for Feathers Vuex](https://github.com/feathersjs-ecosystem/feathers-chat-vuex) has been updated to `feathers-vuex@3.x` and everything has been rewritten with the Vue composition API.  The old repo is now available at [https://github.com/feathersjs-ecosystem/feathers-chat-vuex-0.7](https://github.com/feathersjs-ecosystem/feathers-chat-vuex-0.7).  The following information will assist you in seeing the \"before\" and \"after\" of the refactor to feathers-vuex@3.x.\n\n![Feathers Chat](https://camo.githubusercontent.com/14b6b2d6dd2475c3b83eb1ade6aedbcd8cf94139/68747470733a2f2f646f63732e66656174686572736a732e636f6d2f6173736574732f696d672f66656174686572732d636861742e39313936303738352e706e67)\n\n### Before and After Comparisons\n\n- The folder structure is similar, since this is a VueCLI application.  Some of the components in the old version have been moved into the `views` folder.\n  - `/components/Home.vue` is now `/views/Home.vue`\n  - `/components/Signup.vue` is now `/views/Signup.vue`\n  - `/components/Login.vue` is now `/views/Login.vue`\n  - `/components/Chat/Chat.vue` is now `/views/Chat.vue`\n- The `/components` folder has been flattened. There are no more subfolders.\n- Component refactors:\n  - [Login.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/eb9ba377c5705c1378bee72661a13dd0db48be05)\n  - [Signup.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/478710ed84869d33a9286078496c1e5974a95067)\n  - [Users.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/02b47149c80c27cdeb611c2f4438b4c62159c644)\n  - [Messages.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/930743c1679cc4ed9d691532a7dff1d6a34398e6)\n  - [Compuser.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/cd5c8898ede270d5e22f9c6ef1450d3f3c6278c9)\n  - [Chat.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/39eb3e13f6921b0d0524ae4ac7942b9ce78b222c)\n  - [Messages.vue](https://github.com/feathersjs-ecosystem/feathers-chat-vuex/commit/e5cf7fb0cc8eab80ee3dc441afafb1399d69059e)\n\n### More to Come\n\nThe Feathers Chat example is a pretty simple application.  Its primary purpose is to show off how easy it is to do realtime with FeathersJS.  (FeathersJS continues to be the only framework that treats real-time communication as a first-class citizen with the same API across multiple transports.)  But it doesn't properly showcase all of the great features in Feathers-Vuex 3.0.  This requires a solution that:\n\n1. Still allows comparison of Feathers Chat applications made with other frameworks.\n2. Allows the version of Feathers Chat built with Feathers-Vuex to add features and showcase things you might actually use in production.\n\nIf there are features which you would like to see implemented, please open an issue in the [feathers-chat-vuex Repo](https://github.com/feathersjs-ecosystem/feathers-chat-vuex) for your idea to be considered.\n\n## Community Examples\n\nIf you have created or know of an example application, please add it, here.\n\n- [Feathers-Chat-Vuex](https://github.com/feathersjs-ecosystem/feathers-chat-vuex)\n"
  },
  {
    "path": "docs/feathers-vuex-forms.md",
    "content": "---\ntitle: Working with Forms\nsidebarDepth: 4\n---\n\n# Working with Forms\n\nThe `FeathersVuexFormWrapper` and `FeathersVuexInputWrapper` are renderless components which assist in connecting your feathers-vuex data to a form.  The next two sections review why they exist by looking at a couple of common patterns.  Proceed to the [FeathersVuexFormWrapper](#feathersvuexformwrapper) or [FeathersVuexInputWrapper](#feathersvuexinputwrapper) sections to learn how to implement.\n\n## The Mutation Multiplicity (anti) Pattern\n\nWhen working with Vuex, it's considered an anti-pattern to modify store data directly.  Turn on Vuex strict mode, and it will throw an error every time you modify store data outside of a mutation.  In my experience, the most common (anti)pattern that beginners use to work around this \"limitation\" is to\n\n1. Read data from the store and use it for display in the UI.\n2. Create custom mutations intended to modify the data in specific ways.\n3. Use the mutations wherever they apply (usually implemented as one mutation per form).\n\nThere are times when defining custom mutations is the most supportive pattern for the task, but I consider them to be more rare.  The above pattern can result in a huge number of mutations, extra lines of code, and increased long-term maintenance costs.\n\n## The Clone and Commit Pattern\n\nThe \"Clone and Commit\" pattern provides an alternative to using a lot of mutations. This patterns looks more like this:\n\n1. Read data from the store and use it for display in the UI.  (Same as above)\n2. Create and modify a clone of the data.\n3. Use a single mutation to commit the changes back to the original record in the store.\n\nSending most edits through a single mutation can really simplify the way you work with Vuex data.  The Feathers-Vuex `BaseModel` class has `clone` and `commit` instance methods.   These methods provide a clean API for working with items in the Vuex store and supporting Vuex strict mode:\n\n```js\nimport { models } from 'feathers-vuex'\n\nexport default {\n  name: 'MyComponent',\n  created() {\n    const { Todo } = models.api\n\n    const todo = new Todo({\n      description: 'Plant the garden',\n      isComplete: false\n    })\n\n    const todoClone = todo.clone()\n    todoClone.description = 'Plant half of the garden.\"\n    todoClone.commit()\n  }\n}\n```\n\nIn the example above, modifying the `todo` variable would directly modify part of the Vuex store outside of a mutation (also known as a reducer in Redux), which is a generally unsupportive practice.  Calling `todo.clone()` returns a reactive clone of the instance and keeps it outside the Vuex store.  This means you can make changes to it all you want without causing any trouble with Vuex.  You can then call `todoClone.commit()` to update the original record in the store.\n\nThe `clone` and `commit` methods are used inside the FeathersVuexFormWrapper component.\n\n## FeathersVuexFormWrapper\n\nThe `FeathersVuexFormWrapper` component uses the \"clone and commit\" pattern to connect a single record to a child form within its default slot.\n\n```vue\n<template>\n  <FeathersVuexFormWrapper :item=\"currentItem\" watch>\n    <template v-slot=\"{ clone, save, reset, remove }\">\n      <SomeEditor\n        :item=\"clone\"\n        @save=\"save().then(handleSaveResponse)\"\n        @reset=\"reset\"\n        @remove=\"remove\"\n      ></SomeEditor>\n    </template>\n  </FeathersVuexFormWrapper>\n</template>\n\n<script>\n\nimport { FeathersVuexFormWrapper } from 'feathers-vuex'\n\nexport default {\n  name: 'MyComponent',\n  components: { FeathersVuexFormWrapper },\n  props: {\n    currentItem: {\n      type: Object,\n      required: true\n    }\n  },\n  methods: {\n    handleSaveReponse(savedItem) {\n      console.log(savedItem) // The item returned from the API call\n    }\n  }\n}\n</script>\n```\n\nHere's another example of how you could use the form wrapper to both save the form and close a modal at the same time.  (The modal is not shown in the template markup.) Notice how the `@save` handler is an inline function that sets `isModalVisible` to false, then on a new line it calls save. This is handled perfectly by Vue.\n\n```vue\n<template>\n  <FeathersVuexFormWrapper :item=\"currentItem\" watch>\n    <template v-slot=\"{ clone, save, reset, remove }\">\n      <SomeEditor\n        :item=\"clone\"\n        @save=\"\n          () => {\n            isModalVisible = false\n            save({ populateParams: {} })\n          }\n        \"\n        @reset=\"reset\"\n        @remove=\"remove\"\n      ></SomeEditor>\n    </template>\n  </FeathersVuexFormWrapper>\n</template>\n\n<script>\n\nimport { FeathersVuexFormWrapper } from 'feathers-vuex'\n\nexport default {\n  name: 'MyComponent',\n  components: { FeathersVuexFormWrapper },\n  props: {\n    currentItem: {\n      type: Object,\n      required: true\n    }\n  },\n  data: () => ({\n    isModalVisible: true\n  }),\n  methods: {\n    handleSaveReponse(savedItem) {\n      console.log(savedItem) // The item returned from the API call\n    }\n  }\n}\n</script>\n```\n### Props\n\n- `item`: {Object} a model instance from the Vuex store.\n- `watch`: {Boolean|Array} when enabled, if the original record is updated, the data will be re-cloned.  The newly-cloned data will overwrite the `clone` data (in the slot scope).  Default: `false`.\n- `eager`: {Boolean} While this is enabled, using the `save` method will first commit the result to the store then it will send a network request.  The UI display will update immediately, without waiting for any response from the API server.  Default: `true`.\n\n### Slot Scope\n\nThe default slot contains only four attributes.  The `clone` data can be passed to the child component.  The `save`, `reset`, and `remove` are meant to be bound to events emitted from the child component.\n\n- `clone`: {Object} The cloned record.  Each record in the store can have a single clone.  The clones are stored on the service's model class, by default.\n- `save`: {Function} When called, it commits the data and saves the record (with eager updating, by default.  See the `eager` prop.)  The save method calls `instance.save()`, internally, so you can pass a params object, if needed.\n- `reset`: {Function} When called, the clone data will be reset back to the data that is currently found in the store for the same record.\n- `remove`: {Function} When called, it removes the record from the API server and the Vuex store.\n\n### Usage with `diffOnPatch`\n\nIf you plan to use the `diffOnPatch` static Model method together with the `FeathersVuexFormWrapper`, be sure to set the `eager` prop to `false`.  See [this GitHub issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/520) for more details.\n\n## FormWrapper Example: CRUD Form\n\n### TodoView\n\nIt's a pretty common scenario to have the same form handle editing and creating data.  Below is a basic example of how you could use the FeathersVuexFormWrapper for this.  A few things to notice about the example:\n\n1. It uses a `Todo` Model class to create and edit todos.  The `$FeathersVuex` object is available on `this` only when the [Feathers-Vuex Vue plugin](./vue-plugin.md) is used.\n2. It assumes that you have a route setup with an `:id` parameter.\n3. It assumes that the data has a MongoDB-style `_id` property, where an SQL-based service would probably use `id`.\n\n```vue\n<template>\n  <FeathersVuexFormWrapper :item=\"item\" watch>\n    <template v-slot=\"{ clone, save, reset, remove }\">\n      <TodoEditor\n        :item=\"clone\"\n        @save=\"save().then(handleSaveResponse)\"\n        @reset=\"reset\"\n        @remove=\"remove\"\n      ></TodoEditor>\n    </template>\n  </FeathersVuexFormWrapper>\n</template>\n\n<script>\nimport { FeathersVuexFormWrapper } from 'feathers-vuex'\nimport TodoEditor from './TodoEditor.vue'\n\nexport default {\n  name: 'TodoView',\n  components: {\n    FeathersVuexFormWrapper,\n    TodoEditor\n  },\n  props: {\n    currentItem: {\n      type: Object,\n      required: true\n    }\n  },\n  computed: {\n    id() {\n      return this.$route.params.id\n    },\n    // Returns a new Todo if the route `id` is 'new', or returns an existing Todo.\n    item() {\n      const { Todo } = this.$FeathersVuex.api\n\n      return this.id === 'new' ? new Todo() : Todo.getFromStore(this.id)\n    },\n  },\n  watch: {\n    id: {\n      handler(val) {\n        // Early return if the route `:id` is 'new'\n        if (val === 'new') {\n          return\n        }\n        const { Todo } = this.$FeathersVuex.api\n        const existingRecord = Todo.getFromStore(val)\n\n        // If the record doesn't exist, fetch it from the API server\n        // The `item` getter will automatically update after the data arrives.\n        if (!existingRecord) {\n          Todo.get(val)\n        }\n      },\n      // We want the above handler handler to run immediately when the component is created.\n      immediate: true\n    }\n  },\n  methods: {\n    handleSaveReponse(savedTodo) {\n      // Redirect to the newly-saved item\n      if (this.id === 'new') {\n        this.$router.push({ params: { id: savedTodo._id } })\n      }\n    }\n  }\n}\n</script>\n```\n\n\n### TodoEditor\n\nNext let's look at a minimal example of a 'TodoEditor' component which is a child of the `FeathersVuexFormWrapper` in the above example.  A few things to notice about the below `TodoEditor` component:\n\n1. It's minimal on purpose to show you the important parts of working with the `FeathersVuexFormWrapper`.\n1. It emits the `save`, `reset`, and `remove` events, which are connected to the `FeathersVuexFormWrapper` in the above code snippet.\n1. It's not styled to keep it simple.  You'll probably want to add some styles.  ;)\n1. The Delete button immediately emits remove, so the instance will be deleted immediately.  You probably want, instead, to show a prompt or confirmation dialog to ask the user to confirm deletion.\n1. This is HTML, so the button `type` is important.  If you forget to add `type=\"button\"` to a button, it will default to `type=\"submit\"`.  Clicking the button would submit the form and call the `@submit.prevent` handler on the `<form>` element.  This even applies to buttons inside child components of the form.  You definitely want to remember to put `type` attributes on all of your buttons.\n\n```vue\n<template>\n  <form @submit.prevent=\"handleSubmit\">\n    <input type=\"checkbox\" v-model=\"item.isComplete\" />\n    <input type=\"text\" v-model=\"item.description\" />\n\n    <!-- Submits the form, see the @submit handler, above -->\n    <button type=\"submit\">Save</button>\n\n    <!-- Emitting reset will restore the item back to the stored version. -->\n    <button type=\"button\" @click=\"$emit('reset')\">Reset</button>\n\n    <!-- Delete's the instance -->\n    <button type=\"button\" @click=\"$emit('remove')\">Delete</button>\n  </form>\n</template>\n\n<script>\nexport default {\n  name: 'TodoEditor',\n  props: {\n    item: {\n      type: Object,\n      required: true\n    }\n  },\n  setup(props, context) {\n    function handleSubmit() {\n      // This is a placeholder for checking form validity, (with Vuelidate, for example)\n      const isValid = true || false\n\n      if (formIsValid) {\n        context.emit('save')\n      } else {\n        // Show any form errors in the UI.\n      }\n    }\n    return { handleSubmit }\n  }\n}\n</script>\n```\n\n### Vuelidate 2 Example\n\nHere's an example of how you might use the upcoming Vuelidate 2 (which is being rewritten to work with the Vue Composition API) to do form validation.  Just to be clear, the validation library you use doesn't change how FeathersVuex will work.  Since Vuelidate is likely the most popular validation library, this is an example to get you started.  There may be some things to figure out to implement your use case.  First, you'll need to install these dependencies:\n\n```json\n{\n  \"dependencies\": {\n    \"@vuelidate/core\": \"^2.0.0-alpha.0\",\n    \"@vuelidate/validators\": \"^2.0.0-alpha.0\"\n  }\n}\n```\n\nHere's the full example, complete with TailwindCSS styles.\n\n```html\n<template>\n  <div class=\"permission-creator flex flex-row items-end\">\n    <!-- Org Selector -->\n    <label class=\"block w-full\">\n      <span class=\"select-label text-gray-700\">Add Organization</span>\n      <XSelect\n        v-model=\"selectedOrg\"\n        :items=\"filteredOrgs\"\n        :label=\"makeOrgLabel\"\n        clearable\n        placeholder=\"Select an Organization\"\n        class=\"block\"\n        :input-class=\"[\n          'x-select-button px-2 py-2 border rounded bg-white',\n          $v.org.$dirty && $v.org.$invalid\n            ? 'border-red-400'\n            : 'border-gray-400'\n        ]\"\n        @click=\"$v.org.$touch\"\n      />\n    </label>\n\n    <!-- Permission Selector -->\n    <label class=\"block ml-0.5\">\n      <span class=\"select-label text-gray-700\">Permission</span>\n      <PermissionSelect v-model=\"selectedAccessType\" />\n    </label>\n\n    <button\n      class=\"form-button primary ml-0.5\"\n      :disabled=\"$v.$invalid\"\n      @click=\"validateAndCreate\"\n    >\n      Add\n    </button>\n  </div>\n</template>\n\n<script>\nimport { XSelect } from '@rovit/x-select'\nimport PermissionSelect from '../PermissionSelect/PermissionSelect'\nimport { models, useFind } from 'feathers-vuex'\nimport { computed, ref } from '@vue/composition-api'\nimport keyBy from 'lodash/keyBy'\nimport capitalize from 'voca/capitalize'\nimport useVuelidate from '@vuelidate/core'\nimport { required } from '@vuelidate/validators'\n\nexport default {\n  name: 'PermissionCreatorOrg',\n  components: {\n    XSelect,\n    PermissionSelect\n  },\n  props: {\n    excludeIds: {\n      type: Array,\n      default: () => []\n    }\n  },\n  setup(props, context) {\n    const { Org } = models.api\n\n    const selectedOrg = ref(null)\n    const selectedAccessType = ref('view')\n\n    // Fetch orgs\n    const orgsParams = computed(() => {\n      return { query: {} }\n    })\n    const { items: orgs } = useFind({ model: Org, params: orgsParams })\n\n    const filteredOrgs = computed(() => {\n      const excludeIds = keyBy(props.excludeIds)\n      return orgs.value.filter(org => {\n        return !excludeIds[org._id]\n      })\n    })\n\n    const $v = useVuelidate(\n      {\n        org: { required, $autoDirty: true },\n        accessType: { required, $autoDirty: true }\n      },\n      { org: selectedOrg, accessType: selectedAccessType }\n    )\n\n    function validateAndCreate() {\n      const org = selectedOrg.value\n      const accessType = selectedAccessType.value\n\n      if (!$v.$invalid) context.emit('create', { org, accessType })\n\n      selectedOrg.value = null\n      selectedAccessType.value = 'view'\n\n      // Not currently working, so the org select turns red after removal\n      $v.org.$reset()\n    }\n\n    function makeOrgLabel(org) {\n      let label = `${org.name}`\n      if (org.nameOfOwner) {\n        label += ` (${org.nameOfOwner})`\n      }\n      return label\n    }\n\n    return {\n      selectedOrg,\n      selectedAccessType,\n      filteredOrgs,\n      validateAndCreate,\n      capitalize,\n      $v,\n      makeOrgLabel\n    }\n  }\n}\n</script>\n\n<style lang=\"postcss\"></style>\n```\n\n## FeathersVuexInputWrapper\n\nBuilding on the same ideas as the FeathersVuexFormWrapper, the FeathersVuexInputWrapper reduces boilerplate for working with the clone and commit pattern on a single input.  One use case for this component is implementing an \"edit-in-place\" workflow.  The following example shows how to use the FeathersVuexInputWrapper to automatically save a record upon `blur` on a text input:\n\n```html\n<template>\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @focus=\"createClone\"\n          @blur=\"e => handler(e, save)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <!-- Simple readout to show that it's working. -->\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'InputWrapperExample',\n  methods: {\n    // Optionally make the event handler async.\n    async save({ event, clone, prop, data }) {\n      const user = clone.commit()\n      return user.patch({ data })\n    }\n  }\n}\n</script>\n```\n\nNotice that in the `save` handler in the above example, the `.patch` method is called on the user, passing in the data.  Because the data contains only the user property which changed, the patch request will only send the data which has changed, saving precious bandwidth.\n\n### Props\n\nThe `FeathersVuexInputWrapper` has two props, both of which are required:\n\n- `item`: The original (non-cloned) model instance.\n- `prop`: The property name on the model instance to be edited.\n\n### Default Slot Scope\n\nOnly the default slot is used. The following props are available in the slot scope:\n\n- `current {clone|instance}`: returns the clone if it exists, or the original record. `current = clone || item`\n- `clone { clone }`: the internal clone. This is exposed for debugging purposes.\n- `prop {String}`: the value of the `prop` prop. If you have the prop stored in a variable in the outer scope, this is redundant and not needed. You could just use this from the outer scope.  It mostly comes in handy when you are manually specifying the `prop` name on the component.\n- `createClone {Function}`: sets up the internal clone. Meant to be used as an event handler.\n- `handler {Function}`: has the signature `handler(event, callback)`.  It prepared data before calling the callback function that must be provided from the outer scope.\n\n### The Callback Function\n\nThe `handler` function in the slot scope requires the use of a callback function as its second argument.  Here's an example callback function followed by an explanation of its properties:\n\n```js\nmyCallback({ event, clone, prop, data }) {\n  clone.commit()\n}\n```\n\n- `event {Event}`: the event which triggered the `handler` function in the slot scope.\n- `clone {clone}`: the cloned version of the `item` instance that was provided as a prop.\n- `prop {String}`: the name of the `prop` that is being edited (will always match the `prop` prop.)\n- `data {Object}`: An object containing the changes that were made to the object. Useful for calling `.patch({ data })` on the original instance.\n\nThis callback needs to be customized to fit your business logic.  You might patch the changes right away, as shown in this example callback function.\n\n```js\nasync save({ event, clone, prop, data }) {\n  const user = clone.commit()\n  return user.patch({ data })\n}\n```\n\nNotice in the example above that the `save` function is `async`.  This means that it returns a promise, which in this case is the `user.patch` request.  Internally, the `handler` method will automatically set the internal `clone` object to `null`, which will cause the `current` computed property to return the original instance.\n\nNote that some types of HTML input elements will call `handler` repeatedly, so the handler needs to be debounced.  See an example, below.\n\n## InputWrapper Examples\n\n### Text Input\n\nWith a text input, you can use the `focus` and `blur` events\n\n```html\n<template>\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @focus=\"createClone\"\n          @blur=\"e => handler(e, save)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <!-- Simple readout to show that it's working. -->\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'InputWrapperExample',\n  props: {\n    user: {\n      type: Object,\n      required: true\n    }\n  },\n  methods: {\n    // The callback can be async\n    async save({ event, clone, prop, data }) {\n      const user = clone.commit()\n      return user.patch({ data })\n    }\n  }\n}\n</script>\n```\n\n### Color Input\n\nHere is an example of using the FeathersVuexInputWrapper on a color input.  Color inputs emit a lot of `input` and `change` events, so you'll probably want to debounce the callback function if you are going to immediately save changes.  The example after this one shows how you might debounce.\n\n```html\n<template>\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @click=\"createClone\"\n          @change=\"e => handler(e, save)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <!-- Simple readout to show that it's working. -->\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'InputWrapperExample',\n  props: {\n    user: {\n      type: Object,\n      required: true\n    }\n  },\n  methods: {\n    // The callback can be async\n    async save({ event, clone, prop, data }) {\n      const user = clone.commit()\n      return user.patch({ data })\n    }\n  }\n}\n</script>\n```\n\n### Color Input with Debounce\n\nHere is an example of using the FeathersVuexInputWrapper on a color input.  Notice how the debounced callback function is provided to the `handler`.  This is because color inputs trigger a `change` event every time their value changes.  To prevent sending thousands of patch requests as the user changes colors, we use the debounced function to only send a request after 100ms of inactivity.\n\nNotice also that this example uses the Vue Composition API because creating a debounced function is much cleaner this way.\n\n```vue\n<template>\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @click=\"createClone\"\n          @change=\"e => handler(e, debouncedSave)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <!-- Simple readout to show that it's working. -->\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n</template>\n\n<script>\nimport _debounce from 'lodash/debounce'\n\nexport default {\n  name: 'InputWrapperExample',\n  props: {\n    user: {\n      type: Object,\n      required: true\n    }\n  },\n  setup() {\n    // The original, non-debounced save function\n    async function save({ event, clone, prop, data }) {\n      const user = clone.commit()\n      return user.patch({ data })\n    }\n    // The debounced wrapper around the save function\n    const debouncedSave = _debounce(save, 100)\n\n    // We only really need to provide the debouncedSave to the template.\n    return { debouncedSave }\n  }\n}\n</script>\n```\n"
  },
  {
    "path": "docs/feathervuex-in-vuejs3-setup.md",
    "content": "# Using Vuejs 3 setup()\nVuejs 3 introduced a new way of passing data from a parent to its child. This is valuable if the child is deep down the hierarchy chain. We want to include `FeathersVuex` in many child components. Through [Inject/Provide](https://v3.vuejs.org/guide/component-provide-inject.html#working-with-reactivity) we now have the ability to `inject` the `FeathersVuex`.`api` into our setup method.\n\n\n## Setup() method\nThe context is no longer passed into the setup() as a parameter:\n\n```\nsetup(props, context) {\n    // old way\n    const { User } = root.$FeathersVuex.api\n}\n```\n\nWe now must `inject` it into setup():\n\n```\nexport default defineComponent({\n    import { inject } from 'vue';\n\n    setup() {\n        // both $FeatherVuex and $fv work here\n        const models: any = inject('$FeathersVuex')\n        const newUser = new models.api.User()\n\n        return {\n            newUser\n        }\n    }\n})\n```\n\nIf an custom alias is desired, pass the `alias` into the module install as detailed [here](https://github.com/feathersjs-ecosystem/feathers-vuex/blob/vue-demi/packages/feathers-vuex-vue3/src/app-plugin.ts).\n\n**Note:** You may auto import `inject` and other `vue` utilities using [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import). Make sure to adjust the `auto-import.d.ts` file to match your `include[]` directory (`src` for vue-cli generated apps)\n\n"
  },
  {
    "path": "docs/getting-started.md",
    "content": "---\ntitle: Getting Started\nsidebarDepth: 3\n---\n\n# Getting Started with Feathers-Vuex\n\n## Installation\n\n```bash\nnpm install feathers-vuex @vue/composition-api --save\n```\n\n```bash\nyarn add feathers-vuex @vue/composition-api\n```\n\nIMPORTANT: Feathers-Vuex is (and requires to be) published in ES6 format for full compatibility with JS classes.  If your project uses Babel, it must be configured properly.  See the [Project Configuration](#projectconfiguration) section for more information.\n\n### With feathers-socketio\n\nA realtime-transport like Socket.io or Primus is required in order to take advantage of the real-time socket events built into Feathers-Vuex. The `feathers-hooks-common` package, specified below, is not required to work with Feathers-Vuex.\n\n```bash\nnpm i @feathersjs/feathers @feathersjs/socketio-client @feathersjs/authentication-client socket.io-client @vue/composition-api feathers-vuex feathers-hooks-common --save\n```\n\n```bash\nyarn add @feathersjs/feathers @feathersjs/socketio-client @feathersjs/authentication-client socket.io-client @vue/composition-api feathers-vuex feathers-hooks-common\n```\n\n### With feathers-rest\n\nFeathers-Vuex works with Feathers-Rest, but keep in mind that the `feathers-rest` client does not listen to socket events. The `feathers-hooks-common` package, specified below, is not required to work with Feathers-Vuex.\n\n```bash\nnpm i @feathersjs/feathers @feathersjs/rest-client @feathersjs/authentication-client @vue/composition-api feathers-hooks-common feathers-vuex --save\n```\n\n```bash\nyarn add @feathersjs/feathers @feathersjs/rest-client @feathersjs/authentication-client @vue/composition-api feathers-hooks-common feathers-vuex\n```\n\n## Project Configuration\n\n### Vue-CLI\n\nIf your project runs on Vue-CLI, add the following to your `vue.config.js` file:\n\n```js\nmodule.exports = {\n  transpileDependencies: ['feathers-vuex']\n}\n```\n\n### Quasar\n\n> In newer Quasar apps, the following `transpileDependencies` setup may not be necessary, anymore. See [this issue on GitHub](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/450)\n\nFor Quasar apps, `transpileDependencies` can be updated in `quasar.conf.js` under build as\n\n```\nbuild: {\n  transpileDependencies: ['feathers-vuex']\n}\n```\n\n### Nuxt\n\nIf your project uses Nuxt, add the following to your `nuxt.config.js` file:\n\n```\nbuild: {\n  transpile: ['feathers-vuex'],\n}\n```\n\n### Resolving Build Issues\n\nIf you have issues with sub-dependencies not loading correctly, you may want to check out [this GitHub issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/399).  One of the suggestions is likely to fix the issue.\n\n\nBe sure to read the section of the docs dedicated to [Working With Nuxt](./nuxt.md).\n\n## Vue DevTools\n\nSince Feathers-Vuex extensively uses Vuex under the hood, you'll want to make sure your VueJS developer tools are up to date AND setup properly.  Specifically, the \"New Vuex Backend\" needs to be enabled.  To setup the devtools\n\n1. Open the Vue tab of the developer tools while viewing your Vue project in the browser.\n1. Go to the Settings panel.\n1. Enable the new Vuex backend:\n\n![New Vuex Backend in Vue DevTools](/img/devtools.jpg)\n\nWhen the above setting is not enabled, the Vue Devtools will likely hang when you start working on a large project.\n\n## Setup\n\nUsing Feathers-Vuex happens in these steps:\n\n1. [Setup the Feathers client and Feathers-Vuex](#setup-the-feathers-client-and-feathers-vuex)\n2. [Define a Model class and service plugin for each service](#setup-one-or-more-service-plugins)\n3. [Setup the auth plugin](#setup-the-auth-plugin), if required.\n4. Register the plugins with the Vuex store.\n\n### Feathers Client & Feathers-Vuex\n\nTo setup `feathers-vuex`, we first need to setup the latest Feathers client.  We can also setup feathers-vuex in the same file.  Depending on your requirements, you'll need to install the feathers-client dependencies, as shown, above.\n\nNote that this example includes an app-level hook that removes attributes for handling temporary (local-only) records.\n\n```js\n// src/feathers-client.js\nimport feathers from '@feathersjs/feathers'\nimport socketio from '@feathersjs/socketio-client'\nimport auth from '@feathersjs/authentication-client'\nimport io from 'socket.io-client'\nimport { iff, discard } from 'feathers-hooks-common'\nimport feathersVuex from 'feathers-vuex'\n\nconst socket = io('http://localhost:3030', {transports: ['websocket']})\n\nconst feathersClient = feathers()\n  .configure(socketio(socket))\n  .configure(auth({ storage: window.localStorage }))\n  .hooks({\n    before: {\n      all: [\n        iff(\n          context => ['create', 'update', 'patch'].includes(context.method),\n          discard('__id', '__isTemp')\n        )\n      ]\n    }\n  })\n\nexport default feathersClient\n\n// Setting up feathers-vuex\nconst { makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex } = feathersVuex(\n  feathersClient,\n  {\n    serverAlias: 'api', // optional for working with multiple APIs (this is the default value)\n    idField: '_id', // Must match the id field in your database table/collection\n    whitelist: ['$regex', '$options']\n  }\n)\n\nexport { makeAuthPlugin, makeServicePlugin, BaseModel, models, FeathersVuex }\n```\n\n### Service Plugins\n\nThe following example creates a User class and registers it with the new `makeServicePlugin` utility function.  This same file is also a great place to add your service-level hooks, so they're shown, too.\n\n```js\n// src/store/services/users.js\nimport feathersClient, { makeServicePlugin, BaseModel } from '../../feathers-client'\n\nclass User extends BaseModel {\n  constructor(data, options) {\n    super(data, options)\n  }\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'User'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      email: '',\n      password: ''\n    }\n  }\n}\nconst servicePath = 'users'\nconst servicePlugin = makeServicePlugin({\n  Model: User,\n  service: feathersClient.service(servicePath),\n  servicePath\n})\n\n// Setup the client-side Feathers hooks.\nfeathersClient.service(servicePath).hooks({\n  before: {\n    all: [],\n    find: [],\n    get: [],\n    create: [],\n    update: [],\n    patch: [],\n    remove: []\n  },\n  after: {\n    all: [],\n    find: [],\n    get: [],\n    create: [],\n    update: [],\n    patch: [],\n    remove: []\n  },\n  error: {\n    all: [],\n    find: [],\n    get: [],\n    create: [],\n    update: [],\n    patch: [],\n    remove: []\n  }\n})\n\nexport default servicePlugin\n```\n\n### Auth Plugin\n\nIf your application uses authentication, the Auth Plugin will probably come in handy.  It's a couple of lines to setup:\n\n```js\n// src/store/store.auth.js\nimport { makeAuthPlugin } from '../feathers-client'\n\nexport default makeAuthPlugin({ userService: 'users' })\n```\n\n[Read more about the Auth Plugin](/auth-plugin.html).\n\n### Vuex store\n\nThis example uses Webpack's `require.context` feature.  If you're not using Webpack, you'll need to manually import each module and list them in the `plugins` array.\n\n```js\n// src/store/index.js\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport { FeathersVuex } from '../feathers-client'\nimport auth from './store.auth'\n\nVue.use(Vuex)\nVue.use(FeathersVuex)\n\nconst requireModule = require.context(\n  // The path where the service modules live\n  './services',\n  // Whether to look in subfolders\n  false,\n  // Only include .js files (prevents duplicate imports`)\n  /\\.js$/\n)\nconst servicePlugins = requireModule\n  .keys()\n  .map(modulePath => requireModule(modulePath).default)\n\nexport default new Vuex.Store({\n  state: {},\n  mutations: {},\n  actions: {},\n  plugins: [...servicePlugins, auth]\n})\n```\n\n## Begin Using Feathers-Vuex\n\nThere are a couple of ways to use Feathers-Vuex.  Version 2.0 heavily focuses on abstracting away the Vuex syntax in favor of using [Model classes](/model-classes.html).  The Model classes are a layer on top of the Vuex getters, mutations, and actions. You can, of course, also directly use the [service plugin's getters, mutations, and actions](/service-plugin.html).\n\nThere are two plugins included:\n\n1. The [Service Plugin](./service-plugin.md) adds a Vuex store for new services.\n2. The [Auth Plugin](./auth-plugin.md) sets up the Vuex store for authentication / logout.\n\nTo see `feathers-vuex` in a working vue-cli application, check out [`feathers-chat-vuex`](https://github.com/feathersjs-ecosystem/feathers-chat-vuex).\n\n### Global Configuration\n\nThe following default options are available for configuration:\n\n```js\nconst defaultOptions = {\n  // only configured globally\n  serverAlias: 'api',\n  keepCopiesInStore: false,\n\n  // also configurable per service\n  idField: 'id',\n  tempIdField: '__id',\n  nameStyle: 'short',\n\n  debug: false,\n  addOnUpsert: false,\n  autoRemove: false,\n  enableEvents: true,\n  preferUpdate: false,\n  replaceItems: false,\n  skipRequestIfExists: false,\n\n  paramsForServer: ['$populateParams'],\n  whitelist: [],\n\n  handleEvents: {\n    created: (item, { model, models }) => options.enableEvents,\n    patched: (item, { model, models }) => options.enableEvents,\n    updated: (item, { model, models }) => options.enableEvents,\n    removed: (item, { model, models }) => options.enableEvents\n  },\n}\n```\n- `serverAlias` - **Default:** `api` - Models are keyed by `serverAlias`. Access the `$FeathersVuex` Plugin and its models in your components by `this.$FeathersVuex.api.${Model}`\n- `keepCopiesInStore` - **Default:** `false` - Set to true to store cloned copies in the store instead of on the Model. <Badge text=\"deprecated\" type=\"warning\" />\n\n- `idField {String}` - **Default:** `'id'` - The field in each record that will contain the id\n- `tempIdField {Boolean}` - **Default:** `'__id'` - The field in each temporary record that contains the id\n- `nameStyle {'short'|'path'}` - **Default:** `'short'` - Use the full service path as the Vuex module name, instead of just the last section.\n- `debug {Boolean}` - **Default:** `false` - Enable some logging for debugging\n- `addOnUpsert {Boolean}` - **Default:** `false` - If `true` add new records pushed by 'updated/patched' socketio events into store, instead of discarding them.\n- `autoRemove {Boolean}` - **Default:** `false` - If `true` automatically remove records missing from responses (only use with feathers-rest)\n- `preferUpdate {Boolean}` - **Default:** `false` - If `true`, calling `model.save()` will do an `update` instead of a `patch`.\n- `replaceItems {Boolean}` - **Default:** `false` - If `true`, updates & patches replace the record in the store. Default is false, which merges in changes.\n- `skipRequestIfExists {Boolean}` - **Default:** `false` - For get action, if `true` the record already exists in store, skip the remote request.\n- `paramsForServer {Array}` - **Default:** `['$populateParams']` - Custom query operators that are ignored in the find getter, but will pass through to the server. It is preconfigured to work with the `$populateParams` custom operator from [feathers-graph-populate](https://feathers-graph-populate.netlify.app/).\n- `whitelist {Array}` - **Default:** `[]` - Custom query operators that will be allowed in the find getter.\n- `enableEvents {Boolean}` - **Default:** `true` - If `false` socket event listeners will be turned off. See the services\n- `handleEvents {Object}`: For this to work `enableEvents` must be `true`\n  - `created {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `created` events, return true to add to the store\n  - `patched {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `created` events, return true to update in the store\n  - `updated {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `created` events, return true to update in the store\n  - `removed {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `removed` events, return true to remove from the store\n\nAlso see the [Configs per Service](/service-plugin.html#configuration)\n\n### Note about feathers-reactive\n\nPrevious versions of this plugin required both RxJS and `feathers-reactive` to receive realtime updates.  `feathers-vuex@1.0.0` has socket messaging support built in and takes advantage of Vuex reactivity, so RxJS and `feathers-reactive` are no longer required or supported.\n\nEach service module can also be individually configured.\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\nhome: true\nheroImage: https://github.com/feathersjs-ecosystem/feathers-vuex/raw/master/service-logo.png\nheroText: Feathers-Vuex 3.x\ntagLine: Integration of FeathersJS, Vue, and Nuxt for the artisan developer\nactionText: Get Started\nactionLink: ./api-overview.md\nfeatures:\n- title: Realtime by Default\n  details: It's fully powered by Vuex and FeathersJS, lightweight, & realtime out of the box.\n- title: Simplified Auth & Services\n  details: Includes service and auth plugins powered by Vuex. All plugins can be easily customized to fit your app.  Fully flexible.\n- title: Best Practices, Baked In\n  details: Vue Composition API 😎 Common Redux patterns included. Fall-through cache by default. Query the Vuex store like a database.\nfooter: MIT Licensed | Copyright © 2017-present Marshall Thompson\n---\n"
  },
  {
    "path": "docs/mixins.md",
    "content": "---\ntitle: Mixins\nsidebarDepth: 3\n---\n\n# Mixins\n\n`Feathers-Vuex` mixins provide quick and easy best practices directly inside a component's viewModel.  They are similar to [Renderless Data Components](./components.md), but are more powerful for two reasons.\n\n1. You can do lots of them together. Handle multiple queries against multiple services at the same time.  The Renderless Data Components aren't capable of handling more than one query without doing ugly nesting.\n2. They bring the data directly into the component's actual viewModel.  The Renderless Data Components only pull the data into the template scope, so the only clean way to get access to the data was by passing it to a component as props.  This is a great solution until you run into number 1, above.\n\nIf you're not using the [Feathers-Vuex Composition API](./composition-api.md), the mixins are probably going to be your preferred solution for development.\n\n## Usage\n\nHere are the steps to using mixins:\n\n1. Import the `makeFindMixin` utility from FeathersVuex.\n2. Register it in a component's mixins once for each query to be made in the component.\n3. Provide a set of params in a computed property (getter only)\n4. Iterate over the computed \"items\" prop named after the service.\n\n```vue\n<script>\nimport { makeFindMixin } from 'feathers-vuex' // Step 1\n\nexport default {\n  name: 'ServerTaskList',\n  mixins: [ makeFindMixin({ service: 'server-tasks' })], // Step 2\n  computed: {\n    serverTasksParams() {\n      return { query: {} } // Step 3\n    }\n  }\n}\n</script>\n\n<template>\n  <div>\n    <ul>\n      <!-- Step 4 -->\n      <li v-for=\"task in serverTasks\" :key=\"task._id\">\n        {{task.name}}\n      </li>\n    </ul>\n  </div>\n</template>\n```\n\nIn the above example, any records returned from the server will automatically show up when they become available.  It also automatically responds to realtime events when you're using one of FeathersJS's realtime transports, like Socket.io.\n\nNotice in the above example that using the mixin automatically makes the `serverTasks` available in the template.  The mixins automatically setup a few properties in the viewModel based on the camelCased name of the service.  You can also provide a `name` attribute to override the defaults:\n\n## makeFindMixin\n\n### Options\n\n- `service {String|Function}` - **required** the service path. This must match a service that has already been registered with FeathersVuex.\n  - **{String}** - The service namespace\n  - **{Function}** - Any provided function will become a computed property in the component and will be used to determine its value.\n- `name {String}` - The name to use in all of the dynamically-generated property names. See the section about Dynamically Generated Props\n- `items {String}` - The attribute name to use for the records.\n- `params {String|Function}` - One of two possible params attributes.  (The other is `fetchParams`)  When only `params` is provided, it will be used for both the `find` getter and the `find` action.  When using server-side pagination, use `fetchParams` for server communciation and the `params` prop for pulling data from the local store. If the params is `null` or `undefined`, the query against both the API will be skipped. The find getter will return an empty array. **Default {String}: `${camelCasedService}Params`** (So, by default, it will attempt to use the property on the component called serviceName + \"Params\")\n  - **{String}** - The name of the attribute in the current component which holds or returns the query object.\n  - **{Function}** - A provided function will become a computed property in the current component.\n- `watch {String|Array<String>}` - specifies the attributes of the `params` or `fetchParams` to watch.  When a watched prop changes, a new request will be made to the API server. Pass 'params' to watch the entire params object.  Pass 'params.query.name' to watch the 'name' property of the query. Watch is turned off by default, meaning only one initial request is made. **Default {String}: `${camelCasedService}Params`**\n  - **{Boolean}** - If `true`: `[${camelCasedService}Params]` will be watched, else `[]`\n  - **{String}** - The name of the component's prop to use as the value. Transformed to an `Array<String>`\n  - **{Array<String>}** - Names of the component's prop\n- `fetchParams {String|Function}` - when provided, the `fetchParams` serves as the params for the API server request. When `fetchParams` is used, the `param` attribute will be used against the service's local Vuex store. **Default: undefined**\n  - **{String}** - The name of the attribute in the current component which holds or returns the params object.\n  - **{Function}** - A provided function will become a computed property in the current component.\n- `queryWhen {Boolean|String|Function}` - the query to the server will only be made when this evaluates to true.  **Default: true**\n  - **{Boolean}** - As a boolean, the value provided determines whether this is on or off.\n  - **{String}** - The name of the component's prop to use as the value.\n  - **{Function}** - Any provided function will become a method in the component and will receive the current params object as an argument.\n- `local {Boolean|String|Function}` - when true, will only use the `params` prop to pull data from the local Vuex store. It will disable queries to the API server. The value of `local` will override `queryWhen`. **Default:false**\n  - **{Boolean}** - As a boolean, the value provided determines whether this is on or off.\n  - **{String}** - The name of the component's prop to use as the value.\n  - **{Function}** - Any provided function will become a computed property in the component and will be used to determine its value.\n- `qid {String}` - The \"query identifier\" (\"qid\", for short) is used for storing pagination data in the Vuex store. See the service module docs to see what you'll find inside.  The `qid` and its accompanying pagination data from the store will eventually be used for cacheing and preventing duplicate queries to the API.\n\n### Injected Properties\n\nWith `makeFindMixin` the following properties will be injected into your component and can become handy to use manually. Since the names of the mixin are basically dynamically generated by the `service` and `name` props you pass. Here the general names to understand what's going on under the hood:\n\n#### Dynamically Generated Props:\n```vue\n<script>\n// general\nexport default {\n  data: {\n    [IS_FIND_PENDING]: false, // `isFind${capitalized}Pending`\n    [HAVE_ITEMS_BEEN_REQUESTED_ONCE]: false, // `have${capitalized}BeenRequestedOnce`\n    [HAVE_ITEMS_LOADED_ONCE]: false, // `have${capitalized}LoadedOnce`\n    [MOST_RECENT_QUERY]: null, // `${prefix}LatestQuery`\n    [ERROR]: null // ${prefix}Error\n  },\n  computed: {\n    [PAGINATION]() {/* ... */} // `${prefix}PaginationData`\n    [ITEMS]() {/* ... */}, // `${items}` || `${name}` || `${camelCasedPluralService}`\n    [ITEMS_FETCHED]() {/* ... */} // `${items}Fetched` || `${name}Fetched` || `${camelCasedPluralService}Fetched`\n    [FIND_GETTER]() {/* ... */} // `find${capitalized}InStore``\n  },\n  methods: {\n    [`${FIND_ACTION}DebouncedProxy`](params) {/* ... */}  // `get${capitalized}`\n    [FIND_ACTION](params) {/* ... */}\n  }\n}\n```\n#### Example with `service: 'server-tasks`\n```vue\n<script>\n// example with 'server-tasks' service\nexport default {\n  mixins: [\n    makeGetMixin({\n      service: 'server-tasks' // depending on service\n    })\n  ],\n  data: {\n    isFindServerTasksPending: false,\n    haveServerTasksBeenRequestedOnce: false,\n    haveServerTasksLoadedOnce: false,\n    serverTasksError: null\n  },\n  computed: {\n    serverTasksPaginationData() { /* ... */ }\n    serverTasks() { /* ... */ },\n    serverTasksFetched() { /* .. */ },\n    findServerTasksInStore(params) { /* ... */ },\n  },\n  methods: {\n    findServerTasksDebouncedProxy(params) { /* ... */ },\n    findServerTasks(params) { /* ... */ }\n  }\n}\n</script>\n\n```\n\n## makeGetMixin\n\n### Options\n\n- `id {String|Function}` - when performing a `get` request, serves as the id for the request. This is automatically watched, so if the `id` changes, an API request will be made and the data will be updated.  If `undefined` or `null`, no request will be made.  **Default: undefined**\n  - **{String}** - The name of the component's prop to use as the value.\n  - **{Function}** - Any provided function will become a computed property in the component and will be used to determine its value.\n- `service {String|Function}` - **required** the service path. This must match a service that has already been registered with FeathersVuex.\n  - **{String}** - The service namespace\n  - **{Function}** - Any provided function will become a computed property in the component and will be used to determine its value.\n- `name {String}` - The name to use in all of the dynamically-generated property names. See the section about Dynamically Generated Props\n- `item {String}` - The attribute name to use for the record.\n- `params {String|Function}` - One of two possible params attributes.  (The other is `fetchParams`)  When only `params` is provided, it will be used for both the `find` getter and the `find` action.  When using server-side pagination, use `fetchParams` for server communciation and the `params` prop for pulling data from the local store. If the params is `null` or `undefined`, the query against both the API will be skipped. The find getter will return an empty array. **Default {String}: `${camelCasedSingularizedService}Params`** (So, by default, it will attempt to use the property on the component called serviceName + \"Params\")\n  - **{String}** - The name of the attribute in the current component which holds or returns the query object.\n  - **{Function}** - A provided function will become a computed property in the current component.\n- `watch {Boolean|String|Array<String>}` - specifies the attributes of the `params` or `fetchParams` to watch.  When a watched prop changes, a new request will be made to the API server. Pass 'params' to watch the entire params object.  Pass 'params.query.name' to watch the 'name' property of the query. Watch is turned off by default, meaning only one initial request is made. **Default {Array}: `[]`**\n  - **{Boolean}** - If `true`: `[${camelCasedService}Params]` will be watched, else `[]`\n  - **{String}** - The name of the component's prop to use as the value. Transformed to an `Array<String>`\n  - **{Array<String>}** - Names of the component's prop\n- `fetchParams {String|Function}` - when provided, the `fetchParams` serves as the params for the API server request. When `fetchParams` is used, the `param` attribute will be used against the service's local Vuex store. **Default: undefined**\n  - **{String}** - The name of the attribute in the current component which holds or returns the params object.\n  - **{Function}** - A provided function will become a computed property in the current component.\n\n- `queryWhen {Boolean|String|Function}` - the query to the server will only be made when this evaluates to true.  **Default: true**\n  - **{Boolean}** - As a boolean, the value provided determines whether this is on or off.\n  - **{String}** - The name of the component's prop to use as the value.\n  - **{Function}** - Any provided function will become a method in the component and will receive the current params object as an argument.\n\n- `local {Boolean|String|Function}` - when true, will only use the `params` prop to pull data from the local Vuex store. It will disable queries to the API server. The value of `local` will override `queryWhen`. **Default:false**\n  - **{Boolean}** - As a boolean, the value provided determines whether this is on or off.\n  - **{String}** - The name of the component's prop to use as the value.\n  - **{Function}** - Any provided function will become a computed property in the component and will be used to determine its value.\n\n### Injected Properties\n\nWith `makeGetMixin` the following properties will be injected into your component and can become handy to use manually. Since the names of the mixin are basically dynamically generated by the `service` and `name` props you pass. Here the general names to understand what's going on under the hood:\n\n#### Dynamically Generated Props:\n```vue\n<script>\nexport default {\n  data: {\n    [IS_GET_Pending]: false, // `isGet${capitalized}Pending`\n    [HAS_ITEM_BEEN_REQUESTED_ONCE]: false, // `has${capitalized}BeenRequestedOnce`\n    [HAS_ITEM_LOADED_ONCE]: false, // `has${capitalized}LoadedOnce`\n    [ERROR]: null // `${prefix}Error`\n  },\n  computed: {\n    [ITEM]() { /* ... */ }, // `${item}` || `${name}` || ${camelCasedSingularService}`\n    [GET_GETTER]() { /* ... */ } // `get${capitalized}FromStore`\n  },\n  methods: {\n    [GET_ACTION]() { /* ... */ }  // `get${capitalized}`\n  }\n}\n```\n#### Example with `service: 'server-tasks`\n```vue\n<script>\n// example\nexport default {\n  mixins: [\n    makeGetMixin({\n      service: 'server-tasks' // depending on service\n    })\n  ],\n  data: {\n    isGetServerTaskPending: false,\n    hasServerTaskBeenRequestedOnce: false,\n    hasServerTaskLoadedOnce: false,\n    serverTaskError: null\n  },\n  computed: {\n    serverTask() { /* ... */ },\n    getServerTaskFromStore(params) { /* ... */ },\n  },\n  methods: {\n    getServerTask(params) { /* ... */ }\n  }\n}\n</script>\n\n```\n\n## Patterns & Examples\n\n### Dynamically Generated Props\n\nBased on what options you provide to each mixin, some dynamically-generated props will be added to the current component.  Note that the example below only shows the return values from the computes, not the functions.\n\n```js\nmakeFindMixin({ service: 'videos' }) = {\n  data: () => ({\n    isFindVideosPending: false,\n    haveVideosBeenRequestedOnce: false,\n    haveVideosLoadedOnce: false,\n    videosLocal: false,\n    videosQid: 'default',\n    videosQueryWhen: true,\n    videosWatch: []\n  }),\n  // Only showing the return values, not the actual functions\n  computed: {\n    // pulled from the store using the find getter\n    videos: [ /* results */ ],\n\n    // The pagination data with matching qid from the store\n    videosPaginationData: {\n      queriedAt: 1539682100148, // the timestamp of the last query\n      query: {}, // The last query used with this qid\n      ids: [], // The ids of the records returned in the response\n      limit: 20, // based on the response from the server\n      skip: 0, // The value of the $skip param in the query\n      total: 1 // The total as reported by the server.\n    },\n\n    // The mixin will expect to find this. This won't be created automatically.\n    videosQuery () {}\n  }\n}\n```\n\nIf you were to handle two queries from the same service, you would use the `name` attribute to rename one of them.  The results would be named accordingly.  Note that the example below only shows the return values from the computes, not the functions.\n\n```js\nmakeFindMixin({ service: 'videos', name: 'myVideos' }) = {\n  data: () => ({\n    isFindMyVideosPending: false,\n    haveMyVideosBeenRequestedOnce: false,\n    haveMyVideosLoadedOnce: false,\n    myVideosLocal: false,\n    myVideosQid: 'default',\n    myVideosQueryWhen: true,\n    myVideosWatch: []\n  }),\n  // Only showing the return values, not the actual functions\n  computed: {\n    // pulled from the store using the find getter\n    myVideos: [ /* results */ ],\n\n    // The pagination data with matching qid from the store\n    myVideosPaginationData: {\n      queriedAt: 1539682100148, // the timestamp of the last query\n      query: {}, // The last query used with this qid\n      ids: [], // The ids of the records returned in the response\n      limit: 20, // based on the response from the server\n      skip: 0, // The value of the $skip param in the query\n      total: 1 // The total as reported by the server.\n    },\n\n    // The mixin will expect to find this. This won't be created automatically.\n    myVideosQuery () {}\n  }\n}\n```\n\n### Using a dynamic service\n\nIt's possible to change the service name on the fly.  To do this, pass a function (which becomes a computed property) that returns another string property from the viewModel.  Below is an example of how to set that up.  The `serviceName` attribute is set to `\"videos\"`, initially.  The `setTimeout` in the `created` method changes the value to `\"users\"` after three seconds.  When the serviceName changes, the users service is queried automatically.  The `items` property will then update to be the newly fetched users instead of the video records that it contained before.  The `items` option is used to rename the items to something more generic.\n\n```html\n<template>\n  <div>\n    {{items}}\n  </div>\n</template>\n\n<script>\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'my-component',\n  data: () => ({\n    serviceName: 'videos'\n  }),\n  mixins: [\n    makeFindMixin({\n      service () { return this.serviceName },\n      name: 'service', // the default value when `service` is a function.\n      items: 'items' // the default value when `service` is a function.\n    })\n  ],\n  computed: {\n    serviceParams () {\n      return { query: { $limit: 1 } }\n    }\n  },\n  created () {\n    setTimeout(() => {\n      this.serviceName = 'users'\n    }, 3000)\n  }\n}\n</script>\n\n<style lang=\"scss\">\n</style>\n```\n\nIn the above example, the mixin data would look like this:\n\n```js\nconst mixedInDataFromAboveExample = {\n  data: () => ({\n    isFindServicePending: false,\n    serviceLocal: false,\n    serviceQid: 'default',\n    serviceQueryWhen: true,\n    serviceWatch: []\n  }),\n  // Only showing the return values, not the actual functions\n  computed: {\n    items: [ /* results */ ],\n\n    // The pagination data with matching qid from the store\n    servicePaginationData: {},\n\n    // The mixin will expect to find this. This won't be created automatically.\n    serviceQuery () {}\n  }\n}\n```\n\n### Pagination with fall-through cacheing\n\nThe `makeFindMixin` in `feathers-vuex@2.x` features a great new, high performance, fall-through cacheing feature, which only uses a single query!  Read the service module documentation for details of how it works under the hood.  It really makes easy work of high-performance pagination.  To use the pagination, provide `$limit` and `$skip` attributes in `params.query`.  This is exactly the same way you would normally do with any FeathersJS query.  So this is completely transparent to how you'd normally do it.\n\n> Note: By default, the pagination feature is turned on.  To simplify updating existing apps using `feathers-vuex`, you can turn this feature off in any part of your app by passing `paginate: false` in the params for that particular query.  This will completely restore the previous behavior and re-enable live lists.\n\nLet's extend the first example on this page to support pagination.  We'll do the following:\n\n1. Setup the `makeFindMixin` to use the `watch` property.\n2. Add a `data` attribute to the component with `limit` and `skip` properties.\n3. Reference the `limit` and `skip` in `params.query`.\n4. Add methods for `previousPage` and `nextPage`\n5. Create buttons for changing the limit and skip.\n\n```vue\n<script>\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'ServerTaskList',\n  mixins: [ makeFindMixin({ service: 'server-tasks', watch: true })], // Step 1\n  data: () => ({ // Step 2\n    limit: 5,\n    skip: 0\n  }),\n  computed: {\n    serverTasksParams() {\n      return { query: { $limit: this.limit, $skip: this.skip } } // Step 3\n    }\n  },\n  methods: { // Step 4\n    previousPage() {\n      this.skip = this.skip - this.limit\n    },\n    nextPage() {\n      this.skip = this.skip + this.limit\n    }\n  }\n}\n</script>\n\n<template>\n  <div>\n    <ul>\n      <li v-for=\"task in serverTasks\" :key=\"task._id\">\n        {{task.name}}\n      </li>\n    </ul>\n    <!-- Step 5 -->\n    <button @click=\"previousPage\">Previous Page</button>\n    <button @click=\"\">Next Page</button>\n  </div>\n</template>\n```\n\nIn the above example, since we've enabled the `watch` attribute on the makeFindMixin, every time the params change, the query will run again.  `feathers-vuex` will keep track of the queries and the pages that are visited, noting which records are returned on each page.  When a page is revisited, the data in the store will *immedately* display to the user.  The query will (by default) go out to the API server, and data will be updated in the background when the response arrives.\n\n### Debouncing requests\n\nWhat happens when a query with a watcher is attached to an attribute that might change rapidly?  A lot of API requests can get sent in succession.  If too many are sent, some of them will start to fail (a.k.a. bounce).  The `makeFindMixin` has a built-in utility for debouncing requests.  Enabling it makes it so requests only are sent after a specific amount of time has passed.  To enable it, pass a `debounce` attribute in the `params`, as shown in the next example.\n\nLet's build on our previous example by adding a `search` feature where the user can type some input.  Here are the steps:\n\n1. Add an attribute to the data to which we will bind user input. We'll call it `search`.\n2. Modify params to include the `search` attribute in a supportive way.\n3. Enable the the debounce feature.\n4. Add an `input:text` to the template which binds to the attribute in step 1.\n\n```vue\n<script>\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'ServerTaskList',\n  mixins: [ makeFindMixin({ service: 'server-tasks', watch: true })],\n  data: () => ({\n    limit: 5,\n    skip: 0,\n    search: '' // Step 1\n  }),\n  computed: {\n    serverTasksParams() {\n      return {\n        query: {\n          $limit: this.limit,\n          $skip: this.skip,\n          name: { $regex: this.search, $options: 'igm' } // Step 2\n        },\n        debounce: 500 // Step 3\n      }\n    }\n  },\n  methods: {\n    previousPage() {\n      this.skip = this.skip - this.limit\n    },\n    nextPage() {\n      this.skip = this.skip + this.limit\n    }\n  }\n}\n</script>\n\n<template>\n  <div>\n    <!-- Step 4 -->\n    <label for=\"server-task-search\">\n      Search Server Tasks by Name\n    </label>\n    <input\n      v-model=\"search\"\n      id=\"server-task-search\"\n      type=\"text\"\n      placeholder=\"Enter a task name\"\n    />\n\n    <ul>\n      <li v-for=\"task in serverTasks\" :key=\"task._id\">\n        {{task.name}}\n      </li>\n    </ul>\n    <button @click=\"previousPage\">Previous Page</button>\n    <button @click=\"\">Next Page</button>\n  </div>\n</template>\n```\n\nNotice a couple of things in the above example.  We enabled the internal `debounce` feature by simply adding `debounce: 500` to the params (outside the query).  This means that as the user types, requests will be queued inside a 500 ms interval.  The request will be sent as soon as the user stops typing for 500 milliseconds.  For example, if the user types a single character, waits ~400ms, then types a second character, the first request will be cancelled and another request will be sent 500ms after typing the second character.  It's more likely that these requests will not bounce. :)\n\nWe also added a `$regex` search to the params.  This is a MongoDB feature, which naturally also works with Mongoose services (since Mongoose is a tool built for MongoDB).  If you're using another type of service, you will need to come up with a solution for performing searches safely.  The solution will vary depending on the database used.\n\nFeel free to make a PR for using something else that could be useful to the community!  We love those!\n\n### Enabling live lists with pagination\n\nThe new fall-through cacheing pagination does not currently support live sorting of lists.  This means that when a new record arrives from the database, it doesn't automatically get sorted into the correct page and shuffle the other records around it.  The lists will update as the user navigates to previous/next pages.  Coming up with a solution for this will be a top priority after 2.x ships.  In the meantime, here are some alternatives.\n\n### Use `paginate:false` in the params\n\nRestore the previous default behavior by putting `paginate:false` in the params.  This is the easiest way to upgrade existing apps using the `makeFindMixin`.  Look at the `todosParams` in this example:\n\n```vue\n<script>\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'SomeExampleComponent',\n  mixins: [makeFindMixin({ service: 'todos', watch: true })]\n  computed: {\n    todosParams() {\n      return {\n        query: {},\n        paginate: false // This restores previous functionality\n      }\n    }\n  },\n  methods: {\n    refresh() {\n      this.findTodos()\n    }\n  }\n}\n</script>\n\n<template>\n  <div>\n    <TodosList :items=\"todos\"></TodosList>\n    <TodoEntryForm @created=\"refresh\"></TodoEntryForm>\n  </div>\n</template>\n```\n\nThe `paginate` property will not be sent to the server, but it will locally disable the pagination and enable the live lists from the `find` getter.\n\n### Refresh the current query after changes\n\nThis is a simplistic approach. In some cases, when you expect the data to have changed in whatever list you are currently showing to the user, you can simply call the find action for that list and get new data from the server.  In the below example, whenever the `TodoEntryForm` component emits the `created` event, it triggers the `findTodos` method.  Note: you wouldn't want to do `@created=\"findTodos` because that would potentially pass the new todo as the params like `findTodos(newTodo)` instead of `findTodos()`.  Passing no params will automatically use the `todosParams` for the query.\n\n```vue\n<script>\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'SomeExampleComponent',\n  mixins: [makeFindMixin({ service: 'todos', watch: true })]\n  computed: {\n    todosParams() {\n      return { query: {} }\n    }\n  },\n  methods: {\n    refresh() {\n      this.findTodos()\n    }\n  }\n}\n</script>\n\n<template>\n  <div>\n    <TodosList :items=\"todos\"></TodosList>\n    <TodoEntryForm @created=\"refresh\"></TodoEntryForm>\n  </div>\n</template>\n```\n\n### Use the \"FetchQuery\" params\n\nLet's suppose we have a todos service that we're mixing into our component:\n\n```js\nmakeFindMixin({ service: 'todos', watch: true })\n```\n\nThe `makeFindMixin` by default will look for a single set of params called `todosParams`. If it finds only this set of params, the params will be used for fetching data and pulling it from the Vuex store.  However, if another set of params, called the `todosFetchParams`, this new set of params will be used to fetch data and the `todosParams` will be used against the internal store.  In this scenario, the internal pagination tracking is also turned off, which allows you to make queries directly against the Vuex store again.\n\n```vue\n<script>\nimport { makeFindMixin } from 'feathers-vuex'\n\nexport default {\n  name: 'SomeExampleComponent',\n  mixins: [makeFindMixin({ service: 'todos', watch: true })]\n  computed: {\n    // This query will retrieve all records from the local Vuex store.\n    todosParams() {\n      return { query: {} }\n    },\n    // This query will retrieve 10 records from the API server.\n    todosFetchParams() {\n      return {\n        query: {\n          $limit: 10,\n          $skip: 0\n        }\n      }\n    }\n  },\n  methods: {\n    refresh() {\n      this.findTodos()\n    }\n  }\n}\n</script>\n\n<template>\n  <div>\n    <TodosList :items=\"todos\"></TodosList>\n    <TodoEntryForm @created=\"refresh\"></TodoEntryForm>\n  </div>\n</template>\n```\n\n### Debugging the makeFindMixin\n\n**Important: For the built in pagination features to work, you must not directly manipulate the `context.params` object in any hooks.**\n\nIf the makeFindMixin is not returning any results, but you can see the results coming in across the websocket  or rest transport, make sure you're not directly modifying the `context.params` object in a hook, as mentioned in bold, above. ;)  The best place to debug if this is your issue is in `make-find-mixin` in the `[ITEMS]` computed property.  Set a breakpoint at `const items = getItemsFromQueryInfo(pagination, queryInfo, keyedById)`.  Maybe even make it a conditional breakpoint around the `serviceName` variable: `serviceName === 'assets' && Object.keys(keyedById).length > 0`.\n\nWhen you hit the above breakpoint, check the `keyedById` variable.  If it has records, but the `items` is an empty array, there may be a problem with the `queryInfo` not matching from the `context.params` getting modified.\n"
  },
  {
    "path": "docs/model-classes.md",
    "content": "---\ntitle: Data Modeling\nsidebarDepth: 3\n---\n\n# Data Modeling with Model Classes\n\nFeathers-Vuex 1.0 introduced some lightweight data modeling.  Every service had its own, internal `FeathersVuexModel`.  In version 2.0 this `FeathersVuexModel` is now called the `BaseModel` and is extendable, so you can add your own functionality.\n\n\n## Extending the BaseModel Class\n\nWhile [setting up Feathers-Vuex](/getting-started.html#feathers-client-feathers-vuex), we exported the `BaseModel` class so that we could extend it.  The below example shows how to import and extend the `BaseModel`.  Each service must now have its own unique Model class.\n\n```js\nimport feathersClient, { makeServicePlugin, BaseModel } from '../feathers-client'\n\nclass User extends BaseModel {\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'User'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      email: '',\n      password: ''\n    }\n  }\n}\n\nconst servicePath = 'users'\nconst servicePlugin = makeServicePlugin({\n  Model: User,\n  service: feathersClient.service(servicePath),\n  servicePath\n})\n```\n\nIn case you're wondering, the `modelName` property is used to get around transpilation errors when using Babel with ES3 or ES5.  Babel is still installed by default in most projects and generators.  The `modelName` is used instead of the `name` property to provide a reliable name AFTER transpilation.\n\nIf you're working in an environment that doesn't support static properties on classes, you can always specify the static properties using the dot operator:\n\n```js\nclass User extends BaseModel {}\n\nUser.modelName = 'User'\nUser.instanceDefaults = function() {\n  return {\n    email: '',\n    password: ''\n  }\n}\n```\n\n### BaseModel typing\n\nBaseModel typing gives helpful IDE autocomplete and errors when using Model classes.\n\nSince Feathers-Vuex doesn't know what your data looks like, you will need to help define your underlying model data's interface.\n\nBy default, Model classes are `string` indexable with value of type `any`. This isn't super helpful...\n\n```ts\n// Just like before, we define our class as an extension of BaseModel\nclass User extends BaseModel { /* ... */ }\n\n// Augment the User Model interface\ninterface User {\n  email: string\n  password: string\n}\n```\n\nNow, whenever we access a `User` model, all fields defined in the interface will be available in IDE auto-complete/intellisense along with the model methods/props.\n\nIf you already have a User interface defined under a different name, just define a new interface with the same name as your Model class like so\n\n```ts\n// if our User interface already exists as UserRecord\ninterface User extends UserRecord {}\n```\n\nTo further enhance typing, you can augment FeathersVuex types to aid development in other parts of your app. It's important to note the differences in the following example if you do or do not setup a `serverAlias`.\n\n```ts\n// src/store/user.store.ts\nimport { ServiceState } from 'feathers-vuex'\n\nclass User extends BaseModel { /* ... */ }\ninterface User { /* ... */ }\nconst servicePath = 'users'\n\ndeclare module \"feathers-vuex\" {\n  interface FeathersVuexStoreState {\n    [servicePath]: ServiceState<User>\n  }\n\n  // Only if you setup FeathersVuex without a serverAlias!!\n  interface FeathersVuexGlobalModels {\n    User: typeof User\n  }\n}\n\n// Only if you setup FeathersVuex with a serverAlias!!\ndeclare module \"src/store\" {\n  interface MyApiModels {\n    User: typeof User\n  }\n}\n```\n\nIf you have setup a `serverAlias`, you need to add the following to `src/store/index.ts`.\n\n```ts\n// src/store/index.ts\nexport interface MyApiModels { /* Let each service augment this interface */ }\ndeclare module \"feathers-vuex\" {\n  interface FeathersVuexGlobalModels {\n    'my-api-name': MyApiModels\n  }\n}\n```\n\nReplace `my-api-name` with the `serverAlias` you used when setting up FeathersVuex.\n\n## Model attributes\n\nThe following attributes are available on each model:\n\n- `servicePath {String}` - `Model.servicePath` is the path passed to create the FeathersClient service.\n- `namespace {String}` - `Model.namespace` holds the value that was used to register the module with Vuex. This will match the `servicePath` unless you've provided a custom namespace in the [Service Module options](./index.md#Use).\n- `store {Vuex Store}` - Use `Model.store` to access the Vuex store. [example](./common-patterns.md#Accessing-the-store-from-hooks)\n\n## Model Methods\n\n### find(params)\n\nModel classes have a `find` method, which is a proxy to the [`find` action](./service-plugin.html#find-params). <Badge text=\"1.7.0+\" />\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  Todo.find({ query: {} }).then(/* ... */)\n}\n```\n\n### findInStore(params)\n\nModel classes have a `findInStore` method, which is a proxy to the [`find` getter](./service-plugin.html#Service-Getters).  <Badge text=\"1.7.0+\" />\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  const todos = Todo.findInStore({ query: {} })\n}\n```\n\n### count(params) <Badge text=\"3.12.0+\" />\n\nModel classes have a `count` method, which is a proxy to the `count` action. On the Feathers server, `$limit: 0` results in a fast count query. (./service-plugin.html#find-params).\n\n> **Note:** it only works for services with enabled pagination!\n\n```js\n// In your Vue component\nasync created () {\n  const { Todo } = this.$FeathersVuex.api\n  const todosCount = await Todo.count({ query: { priority: 'critical' }})\n  // or\n  Todo.count().then((total) => { this.todoCount = total })\n}\n```\n\n### countInStore(params) <Badge text=\"3.12.0+\" />\n\nModel classes have a `countInStore` method, which is a proxy to the [`count` getter](./service-plugin.html#Service-Getters).\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  const todosCount = Todo.countInStore({ query: { priority: 'critical' }})\n}\n```\n\n### get(id, params)\n\nModel classes have a `get` method, which is a proxy to the [`get` action](./service-plugin.html#get-id-or-get-id-params).   <Badge text=\"1.7.0+\" /> Notice that the signature is more Feathers-like, and doesn't require using an array to passing both id and params.\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  Todo.get(this.id).then(/* ... */)\n}\n```\n\n### getFromStore(id, params)\n\nModel classes have a `getFromStore` method, which is a proxy to the [`get` getter](./service-plugin.html#Service-Getters).   <Badge text=\"1.7.0+\" /> Notice that the signature is more Feathers-like, and doesn't require using an array to passing both id and params.\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  const todo = Todo.getFromStore(this.id)\n}\n```\n\n### instanceDefaults  <Badge text=\"1.7.0+\" />\n\n`instanceDefaults(data, { store, models })`\n\nThe `instanceDefaults` API was created in version 1.7 to prevent requiring to specify data for new instances created throughout the app.  Depending on the complexity of the service's \"business logic\", it can save a lot of boilerplate.  Notice that it is similar to the `setupInstance` method added in 2.0.  The instanceDefaults method should ONLY be used to return default values for a new instance.  Use `setupInstance` to handle other transformations on the data.\n\nStarting with version 2.0, `instanceDefaults` must be provided as a function.  The function will be called with the following arguments and should return an object of default properties for new instances.\n\n- `data {Object}` - The instance data\n- An `utils` object containing these props:\n  - `store` - The vuex store\n  - `models {Object}` The `globalModels` object, which is the same as you'll find inside a component at `this.$FeathersVuex`.\n\nAs an example, a User model class might look like this:\n\n```js\ninstanceDefaults(data, { store, models }) {\n  return {\n    firstName: '',\n    lastName: '',\n    email: '',\n    password: '',\n    isAdmin: false\n  }\n}\n```\n\nWith the above attributes in place, you no longer have to manually specify any of the listed attributes.  You can, however, provided data to replace them.  Calling `new User({ firstName: 'Marshall' })` will create the instance with the `firstName` filled in, already.\n\nOne important note, the `isAdmin` attribute is specified in the above example in order to allow immediate binding in a form.  You would pretty much NEVER allow specifying `isAdmin` from the client and storing it on the server.  Attributes related to roles and app security should pretty much ALWAYS be written in hooks on the API server.\n\n### setupInstance  <Badge text=\"2.0.0+\" />\n\n`setupInstance(data, { store, models })`\n\nA new `setupinstance` class method is now available in version 2.0.  This method allows you to transform the data and setup the final instance based on incoming data.  For example, you can access the `models` object to reference other service Model classes and create data associations.\n\nThe function will be called during model instance construction with the following arguments and should return an object containing properties that'll be merged into the new instance.\n\n- `data {Object}` - The instance data\n- A `utils` object containing these props:\n  - `store` - The vuex store\n  - `models {Object}` The `globalModels` object, which is the same as you'll find inside a component at `this.$FeathersVuex`.\n\nFor an example of how you might use `setupInstance`, suppose we have two services: Users and Posts.  Assume that the API request to get a user includes their `posts`, already populated on the data.  The `instanceDefaults` allows us to convert the array of `posts` into an array of `Post` instances.\n\n> If you're looking for a great solution for populating data to work with Feathers-Vuex, check out [feathers-graph-populate](https://feathers-graph-populate.netlify.app/).\n\n\n```js\n// The setupInstance method on an imaginary User model.\nsetupInstance(data, { store, models }) {\n  if (data.posts) {\n    // Turn posts into an array of Post instances\n    data.posts = data.posts.map(post => new models.api.Post(post))\n  }\n  return data\n}\n```\n\nWith the above `setupInstance` method in place, each `User` instance now stores a direct reference to the `Post` records in the store.\n\n### on <Badge text=\"2.3.0+\" />\n\nRegister event handlers to listen to events.\n\n### once <Badge text=\"2.3.0+\" />\n\nRegister an event handler that only occurs once.\n\n### off <Badge text=\"2.3.0+\" />\n\nRemove an event handler.\n\n## Model Events <Badge text=\"2.3.0+\" />\n\nModel classes are EventEmitter instances which emit service events when received (technically, EventEmitter methods are mixed onto each Model class).  All FeathersJS events are supported.  Oh, and one more thing: it works with `feathers-rest` (you won't receive socket events, but you can listen for when instances are created in other parts of the app.)\n\nHere’s an example of how to use it in a component:\n\n```js\nexport default {\n   created() {\n      this.$FeathersVuex.api.Todo.on(‘created’, this.handleTodoCreated)\n   },\n   destroyed() {\n      this.$FeathersVuex.api.Todo.off(‘created’, this.handleTodoCreated)\n   },\n   methods: {\n      handleTodoCreated(todo) {\n         console.log(todo)\n      }\n   }\n}\n```\n\nSince they have all of the EventEmitter methods, Model classes can be used as a data-layer Event Bus.  You can even use custom event names:\n\n```js\nconst { Todo } = this.$FeathersVuex.api\n\nTodo.on('custom-event', data => {\n  console.log(data) // { test: true }\n})\n\nTodo.emit('custom-event', { test: true })\n```\n\n## Creating instances\n\nThe [FeathersVuex plugin for Vue](./vue-plugin.md) allow convenient access to all Model constructors. You can create a Model instance by getting a reference to a Model class from the `$FeathersVuex` object:\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  const todo = new Todo({ description: 'Do something!' })\n}\n```\n\nYou can also reference this directly from the Vue module:\n\n```js\nimport Vue from 'vue'\n\nconst { Todo } = Vue.$FeathersVuex.api\nconst todo = new Todo({ description: 'Do something!' })\n```\n\nThe examples above show instantiating a new Model instance without an `id` field. In this case, the record is not added to the Vuex store.  If you instantiate a record **with an `id`** field, it **will** get added to the Vuex store. *Note: This field is customizable using the `idField` option for this service.*\n\nNow that we have Model instances, let's take a look at the functionality they provide.\n\nEach instance will include the following methods:\n\n- `.save()`\n- `.create()`\n- `.patch()`\n- `.update()`\n- `.clone()`\n- `.commit()`\n- `.reset()`\n\nand the following readonly attributes:\n\n- `isCreatePending` - `create` is currently pending on this model\n- `isUpdatePending` - `update` is currently pending on this model\n- `isPatchPending` - `patch` is currently pending on this model\n- `isRemovePending` - `remove` is currently pending on this model\n- `isSavePending` - Any of `create`, `update` or `patch` is currently pending on this model\n- `isPending` - Any method is currently pending on this model\n\n*Remember, if a record already has an attribute with any of these method names, it will be overwritten with the method.*\n\nThese methods give access to many of the store `actions` and `mutations`.  Using Model instances, you no longer have to use `mapActions` for `create`, `patch`, `update`, or `remove`.  You also no longer have to use `mapMutations` for `createCopy`, `commitCopy`, or `resetCopy`.\n\n```js\nstore.dispatch('todos/find', { query: {} })\n  .then(response => {\n    const { data } = response\n    const todo = data[0]\n\n    todo.description = 'Read Nuxt.js docs'\n    todo.save() // Calls store.dispatch('todos/patch', [item.id, item, {}])\n  })\n```\n\n## Instance Methods\n\n### `instance.save(params)`\n\nThe `save` method is a convenience wrapper for the `create/patch` methods, by default. If the records has no `_id`, the `instance.create()` method will be used. The `params` argument will be used in the Feathers client request.  See the [Feathers Service](https://docs.feathersjs.com/guides/basics/services.html#service-methods) docs, for reference on where params are used in each method.\n\n```js\n// In your Vue component\ncreated () {\n  const { Todo } = this.$FeathersVuex.api\n  const todo = new Todo({ description: 'Do something!' })\n\n  todo.save() // --> Creates the todo on the server.\n}\n```\n\nOnce the `create` response returns, the record will have an `_id`.  If you call `instance.save()` again, it will call `instance.patch()`.  Which method is used depends soletly on the data having an id (that matches the `options.idfield` for this service).\n\nAs mentioned, `save` performs either `create` or `patch`, but you can use the `preferUpdate` option to change the behavior to `create/update`.\n\n### `instance.create(params)`\n\nThe `create` method calls the `create` action (service method) using the instance data. The `params` argument will be used in the Feathers client request.  See the [Feathers Service](https://docs.feathersjs.com/guides/basics/services.html#service-methods) docs, for reference.\n\nYou might not ever need to use `.create()`, but can instead use the `.save()` method. Let `feathers-vuex` call `create` or `patch`.\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst data = { description: 'Do something!' }\nconst todo = new Todo(data)\n\ntodo.create() // --> Creates the todo on the server using the instance data\n```\n\n### `instance.patch(params)`\n\nThe `patch` method calls the `patch` action (service method) using the instance data. The instance's id field is used for the `patch` id.  The `params` argument will be used in the Feathers client request.  See the [Feathers Service](https://docs.feathersjs.com/guides/basics/services.html#service-methods) docs, for reference.\n\nSimilar to the `.create()` method, you might not ever need to use `.patch()` if you just use `.save()` and let `feathers-vuex` figure out how to handle it.\n\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst todo = new Todo({ id: 1, description: 'Do something!' })\n\ntodo.description = 'Do something else'\n\ntodo.patch() // --> Sends a `patch` request the with the id and description.\n```\n\n<Badge text=\"3.9.0+\" /> As of version 3.9.0, you can provide an object as `params.data`, and Feathers-Vuex will use `params.data` as the patch data.  This allows patching with partial data:\n\n```js\nimport { models } from 'feathers-vuex'\nconst { Todo } = models.api\n\nconst todo = new Todo({ description: 'Do Something', isComplete: false })\n\ntodo.patch({ data: { isComplete: true } })\n```\n\n### `instance.update(params)`\n\nThe `update` method calls the `update` action (service method) using the instance data. The instance's id field is used for the `update` id. The `params` argument will be used in the Feathers client request.  See the [Feathers Service](https://docs.feathersjs.com/guides/basics/services.html#service-methods) docs, for reference.\n\nUse `.update()` whenever you want to completely replace the data on the server with the instance data.  You can also set the `preferUpdate` option to `true` to make `.save()` call `.update()` when an id field is present on the instance.\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst todo = new Todo({ id: 1, description: 'Do something!' })\n\ntodo.description = 'Do something else'\n\ntodo.update() // --> Sends a `update` request the with all instance data.\n```\n\n### `instance.remove(params)`\n\nThe `remove` method calls the `remove` action (service method) using the instance data. The instance's id field is used for the `remove` id. The `params` argument will be used in the Feathers client request.  See the [Feathers Service](https://docs.feathersjs.com/guides/basics/services.html#service-methods) docs, for reference.\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst todo = new Todo({ id: 1, description: 'Do something!' })\n\ntodo.save()\n  .then(todo => {\n    todo.remove() // --> Deletes the record from the server\n  })\n```\n\n### `instance.clone()`\n\nThe `.clone()` method creates a deep copy of the record and stores it on `Model.copiesById`. This allows you to make changes to the clone and not update visible data until you commit or save the data.\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst todo = new Todo({ id: 1, description: 'Do something!' })\nconst todoCopy = todo.clone()\n\ntodoCopy.description = 'Do something else!'\ntodoCopy.commit() // --> Update the data in the store.\n\nconsole.log(todo.description) // --> 'Do something else!'\nconsole.log(todoCopy.description) // --> 'Do something else!'\n```\n\nThere's another use case for using `.clone()`.  Vuex has a `strict` mode that's really useful in development.  It throws errors if any changes occur in the Vuex store `state` outside of mutations.  Clone really comes in handy here, because you can make changes to the clone without having to write custom Vuex mutations. When you're finished making changes, call `.commit()` to update the store. This gives you `strict` mode compliance with little effort!\n\n> Note: You could previously use the `keepCopiesInStore`<Badge text=\"deprecated\" type=\"warning\"/> option to keep copies in `state.copiesById`.  In 2.0, this feature is deprecated and will be removed from the next release.\n\n### `instance.commit()`\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst todo = new Todo({ id: 1, description: 'Do something!' })\nconst todoCopy = todo.clone()\n\ntodoCopy.description = 'Do something else!'\ntodoCopy.commit() // --> Update the data in the store.\n\nconsole.log(todo.description) // --> 'Do something else!'\nconsole.log(todoCopy.description) // --> 'Do something else!'\n```\n\n### `instance.reset()`\n\n```js\nconst { Todo } = this.$FeathersVuex.api\nconst todo = new Todo({ id: 1, description: 'Do something!' })\nconst todoCopy = todo.clone()\n\ntodoCopy.description = 'Do something else!'\ntodoCopy.reset() // --> Resets the record to match the one in the store.\n\nconsole.log(todo.description) // --> 'Do something!'\nconsole.log(todoCopy.description) // --> 'Do something!'\n```\n"
  },
  {
    "path": "docs/nuxt.md",
    "content": "---\ntitle: Nuxt\n---\n\n# Nuxt\n\n### Access `$FeathersVuex` models in Nuxt `asyncData`\n\nIn `feathers-vuex@2.x`, you can get access to the `$FeathersVuex` object by importing the `models` object from the main export:\n\n```\nimport { models } from 'feathers-vuex'\n```\n\nThe `models` and `$FeathersVuex` variables are the same object.\n\n## Preventing Memory Leaks\n\nThe default settings of Feathers-Vuex include having realtime events enabled by default.  This will result in increased memory usage over time on the SSR server.  It can be turned off when you configure `feathers-vuex`.  The example below has been modified from the example of [Setting up the Feathers Client & Feathers-Vuex](./getting-started.html#feathers-client-feathers-vuex).  Look specifically at the `enableEvents` option.\n\n```js\nconst { makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex } = feathersVuex(\n  feathersClient,\n  {\n    serverAlias: 'api',\n    idField: '_id',\n    whitelist: ['$regex', '$options'],\n    enableEvents: process.client // No events for SSR server\n  }\n)\n```\n\n## Working with Auth & Nuxt\n\n`feathers-vuex@1.0.0^` ships with utilities that help with Nuxt auth related to JSON Web Tokens (JWT).  The most important utility is the `initAuth` utility.  It's for use during Nuxt's `nuxtServerInit` method, and sets up auth data automatically.\n\n`initAuth` will do the following:\n1. Get the accessToken from the `req` passed in\n2. Get the payload from the token\n3. commit the token and payload to the store with `setAccessToken` and `setPayload`\n4. Set the access token on the feathers client instance so that the next time authenticate is called, it will have the JWT from the `req` to authenticate with the server.\n\nHere's an example store that uses it:\n\n```js\n// ~/plugins/feathers-client.js\nimport feathers from '@feathersjs/feathers'\nimport socketio from '@feathersjs/socketio-client'\nimport auth from '@feathersjs/authentication-client'\nimport io from 'socket.io-client'\nimport { iff, discard } from 'feathers-hooks-common'\nimport feathersVuex, { initAuth } from 'feathers-vuex'\n\nconst socket = io('http://localhost:3030', {transports: ['websocket']})\n\nconst feathersClient = feathers()\n  .configure(socketio(socket))\n  .configure(auth({ storage: window.localStorage }))\n  .hooks({\n    before: {\n      all: [\n        iff(\n          context => ['create', 'update', 'patch'].includes(context.method),\n          discard('__id', '__isTemp')\n        )\n      ]\n    }\n  })\n\nexport default feathersClient\n\n// Setting up feathers-vuex\nconst { makeServicePlugin, makeAuthPlugin, BaseModel, models, FeathersVuex } = feathersVuex(\n  feathersClient,\n  {\n    serverAlias: 'api', // optional for working with multiple APIs (this is the default value)\n    idField: '_id', // Must match the id field in your database table/collection\n    whitelist: ['$regex', '$options'],\n    enableEvents: process.client // No events for SSR server\n  }\n)\n\nexport { makeAuthPlugin, makeServicePlugin, initAuth, BaseModel, models, FeathersVuex }\n```\n\n```js\n// ~/store/index.js\nimport { makeAuthPlugin, initAuth, models } from '~/plugins/feathers'\nconst auth = makeAuthPlugin({\n  userService: 'users',\n  state: {\n    publicPages: [\n      'login',\n      'signup'\n    ]\n  }\n})\n\nconst requireModule = require.context(\n  // The path where the service modules live\n  './services',\n  // Whether to look in subfolders\n  false,\n  // Only include .js files (prevents duplicate imports`)\n  /.js$/\n)\nconst servicePlugins = requireModule\n  .keys()\n  .map(modulePath => requireModule(modulePath).default)\n\nexport const state = () => ({\n  // Your custom state\n})\n\nexport const mutations = {\n  // Your custom mutations\n}\n\nexport const actions = {\n  nuxtServerInit ({ commit, dispatch }, { req }) {\n    return initAuth({\n      commit,\n      dispatch,\n      req,\n      moduleName: 'auth',\n      cookieName: 'feathers-jwt'\n    })\n  }\n}\n\nexport const getters = {\n  // Your custom getters\n}\n\nexport const plugins = [ ...servicePlugins, auth ]\n```\n\nNotice in the above example, I've added a `publicPages` property to the auth state.  Let's now use this state to redirect the browser when it's not on a public page and there's no auth:\n\nIn your Nuxt project, create the file `/middleware/auth.js`.  Then edit the `nuxt.config.js` and add after the `head` property, add a string that references this routing middleware so it looks like this:\n\n```js\n// nuxt.config.js\nrouter: {\n  middleware: ['auth']\n}\n```\n\nNow open the middleware and paste the following content.  All it does is redirect the page if there's no auth data in the store.\n\n```js\n// If it's a private page and there's no payload, redirect.\nexport default function (context) {\n  const { store, redirect, route } = context\n  const { auth } = store.state\n\n  if (!auth.publicPages.includes(route.name) && !auth.payload) {\n    return redirect('/login')\n  }\n}\n```\n\nFor a summary, the `initAuth` function will make auth available in the state without much configuration.\n\n## Authentication storage with Nuxt\n\nSince Nuxt is running both client- and server side, it has limits on the availability of certain browser specific variables like `window`. Because of that, trying to configure the feathers client to use `window.localStorage` will result in an error or unexpected / not working behaviour. There's a simple solution though:\n\nWhen you configure the auth module in your feathers-client, use [cookie-storage](https://www.npmjs.com/package/cookie-storage) instead of `window.localStorage` to store the authentication data inside a cookie.\n\n```js\nimport { CookieStorage } from 'cookie-storage'\n\nconst feathersClient = feathers()\n  .configure(auth({ storage: new CookieStorage() }))\n```\n\n## Server and Client in different end points\n\nIf you have your feathersjs server in a different end point from your client (Ex. api.yourdomain.com - feathers / yourdomain.com - vue) your cookies wont be shared beetween server and client so you will have to authenticate your client manualy.\n\nThe best solution is to use [nuxt-client-init-module](https://www.npmjs.com/package/nuxt-client-init-module).\n\nFirst we add it to our app using `npm install nuxt-client-init-module` or `yarn add nuxt-client-init-module`, and add it to our `nuxt.config.js` modules:\n\n```js\nexport default {\n  ...\n  modules: [\n    'nuxt-client-init-module'\n  ],\n}\n```\n\nNow, based on the auth example above, we will edit our `~/store/index.js` file.\n\n```js\n// ~/store/index.js\nimport { makeAuthPlugin, initAuth, models } from '~/plugins/feathers'\nconst auth = makeAuthPlugin({\n  userService: 'users',\n  state: {\n    publicPages: []\n  },\n  actions: {\n    // Handles initial authentication\n    onInitAuth ({ state, dispatch }, payload) {\n      if (payload) {\n        dispatch('authenticate', { strategy: 'jwt', accessToken: state.accessToken })\n          .then((result) => {\n            // handle success like a boss\n            console.log('loged in')\n          })\n          .catch((error) => {\n            // handle error like a boss\n            console.log(error)\n          })\n      }\n    }\n  }\n})\n\n...\n\nexport const actions = {\n  nuxtServerInit ({ commit, dispatch }, { req }) {\n    return initAuth({\n      commit,\n      dispatch,\n      req,\n      moduleName: 'auth',\n      cookieName: 'feathers-jwt'\n    })\n  },\n  nuxtClientInit ({ state, dispatch, commit }, context) {\n    // Run the authentication with the access token hydrated from the server store\n    if (state.auth.accessToken) {\n      return dispatch('auth/onInitAuth', state.auth.payload)\n    }\n  }\n}\n\n...\n\n```\n\n## Server side hydration <Badge text=\"3.0.0+\" />\n\nWhen using nuxt SSR and you make requests in the server, using `fetch` or `asyncData`, nuxt will send this data and hydrate the store on client init.\n\nBecause this hydration is done by nuxt, the documents do not inherit from their right classes and all documents are created as simple javascript objects.\n\n`feathers-vuex@3.x.x^` ships with the `hydrateApi` utility for this use case.\n\nWe only have to pass the api's that we need to hydarate on client start to the `nuxtClientInit`.\n\n```js\n// ~/plugins/feathers-client.js\nimport feathers from '@feathersjs/feathers'\nimport socketio from '@feathersjs/socketio-client'\nimport auth from '@feathersjs/authentication-client'\nimport io from 'socket.io-client'\nimport { iff, discard } from 'feathers-hooks-common'\nimport feathersVuex, { initAuth, hydrateApi } from 'feathers-vuex'\n\n...\n\nexport { makeAuthPlugin, makeServicePlugin, initAuth, hydrateApi, BaseModel, models, FeathersVuex }\n```\n\n```js\n// ~/store/index.js\nimport { makeAuthPlugin, initAuth, hydrateApi, models } from '~/plugins/feathers'\n\n...\n\nexport const actions = {\n  nuxtServerInit ({ commit, dispatch }, { req }) {\n    return initAuth({\n      commit,\n      dispatch,\n      req,\n      moduleName: 'auth',\n      cookieName: 'feathers-jwt'\n    })\n  },\n  nuxtClientInit ({ state, dispatch, commit }, context) {\n    hydrateApi({ api: models.api })\n    // Call once for each API to be updated\n    hydrateApi({ api: models.otherApi })\n    // Run the authentication with the access token hydrated from the server store\n    if (state.auth.accessToken) {\n      return dispatch('auth/onInitAuth', state.auth.payload)\n    }\n  }\n}\n\n...\n\n```\n\n## Resolving Build Issues\n\nIf you have issues with sub-dependencies not loading correctly, you may want to check out [this GitHub issue](https://github.com/feathersjs-ecosystem/feathers-vuex/issues/399).  One of the suggestions is likely to fix the issue.\n\n## Full nuxt configuration example\n\n[Check a full nuxt exemple in the common patterns section](./common-patterns.md#full-nuxt-example)\n"
  },
  {
    "path": "docs/service-plugin.md",
    "content": "---\ntitle: Service Plugin\nsidebarDepth: 3\n---\n\n# Service Plugin\n\n<!-- markdownlint-disable MD002 MD033 MD041 -->\n\nThe `makeServicePlugin` method creates a vuex plugin which connects a Feathers service to the Vuex store.  Once you create a plugin, you must register it in the Vuex store's `plugins` section.\n\n## Setup\n\nSee the [setup documentation](./getting-started.html#service-plugins) to learn the basics of setting up a Service Plugin.\n\n## Configuration\n\nThe following options are supported on `makeServicePlugin`:\n\n```js\nimport Model from \"../users.model\";\n\nconst servicePath = 'users'\nconst servicePlugin = makeServicePlugin({\n  // necesarry\n  Model,\n  service: feathersClient.service(servicePath),\n\n  // optional and configurable also by global config\n  idField: 'id',\n  tempIdField: '__id',\n  nameStyle: 'short',\n  debug: false,\n  addOnUpsert: false,\n  autoRemove: false,\n  preferUpdate: false,\n  replaceItems: false,\n  skipRequestIfExists: false,\n\n  paramsForServer: ['$populateParams'],\n  whitelist: [],\n\n  enableEvents: true,\n  handleEvents: {\n    created: (item, { model, models }) => options.enableEvents,\n    patched: (item, { model, models }) => options.enableEvents,\n    updated: (item, { model, models }) => options.enableEvents,\n    removed: (item, { model, models }) => options.enableEvents\n  },\n\n  // optional and only configurable per service\n  servicePath: '',\n  namespace: null,\n  modelName: 'User',\n\n  instanceDefaults: () => ({}),\n  setupInstance: instance => instance,\n\n  state: {},\n  getters: {},\n  mutations: {},\n  actions: {},\n\n  //...\n});\n```\nThe following options can also be configured in [Global Configuration](getting-started.html#global-configuration) for every service initialized using `feathers-client.js`:\n\n- `idField {String}` - **Default:** `globalConfig: 'id'` - The field in each record that will contain the id\n- `tempIdField {Boolean}` - **Default:** `globalConfig: '__id'` - The field in each temporary record that contains the id\n- `nameStyle {'short'|'path'}` - **Default:** `globalConfig: 'short'` - Use the full service path as the Vuex module name, instead of just the last section.\n- `debug {Boolean}` - **Default:** `globalConfig: false` - Enable some logging for debugging\n- `addOnUpsert {Boolean}` - **Default:** `globalConfig: false` - If `true` add new records pushed by 'updated/patched' socketio events into store, instead of discarding them.\n- `autoRemove {Boolean}` - **Default:** `globalConfig: false` - If `true` automatically remove records missing from responses (only use with feathers-rest)\n- `preferUpdate {Boolean}` - **Default:** `globalConfig: false` - If `true`, calling `model.save()` will do an `update` instead of a `patch`.\n- `replaceItems {Boolean}` - **Default:** `globalConfig: false` - If `true`, updates & patches replace the record in the store. Default is false, which merges in changes.\n- `skipRequestIfExists {Boolean}` - **Default:** `globalConfig: false` - For get action, if `true` the record already exists in store, skip the remote request.\n- `paramsForServer {Array}` - **Default:** `['$populateParams']` - Custom query operators that are ignored in the find getter, but will pass through to the server. It is preconfigured to work with the `$populateParams` custom operator from [feathers-graph-populate](https://feathers-graph-populate.netlify.app/).\n- `whitelist {Array}` - Custom query operators that will be allowed in the find getter.\n- `enableEvents {Boolean}` - **Default:** `globalConfig: true` - If `false` socket event listeners will be turned off\n- `handleEvents {Object}`: For this to work `enableEvents` must be `true`\n  - `created {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `created` events, return true to add to the store\n  - `patched {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `created` events, return true to update in the store\n  - `updated {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `created` events, return true to update in the store\n  - `removed {Function}` - **Default:** `(item, { model, models }) => options.enableEvents` - handle `removed` events, return true to remove from the store\n  -\n\nThe following options can only configured individually per service plugin\n\n- `servicePath {String}`- Not all Feathers service plugins expose the service path, so it can be manually specified when missing.\n- `namespace {String}`, - **Default:** `nameStyle === 'short' ? ${afterLastSlashOfServicePath} : ${stripSlashesFromServicePath}` - Customize the Vuex module name. Overrides nameStyle.\n- `modelName {String}` - **Default:** `${ServicePlugin.Model.modelName}`\n- `instanceDefaults {Function}` - **Default:** `() => ({})` - Override this method to provide default data for new instances. If using Model classes, specify this as a static class property.\n- `setupInstance {Function}` - **Default:** `instance => instance` - Override this method to setup data types or related data on an instance. If using Model classes, specify this as a static class property.\n\n  - `state {Object}` - **Default:**: `null` - Pass custom `states` to the service plugin or modify existing ones\n  - `getters {Object}` - **Default:** `null` - Pass custom `getters` to the service plugin or modify existing ones\n  - `mutations {Object}` - **Default:** `null` - Pass custom `mutations` to the service plugin or modify existing ones\n  - `actions {Object}` - **Default:** `null` - Pass custom `actions` to the service plugin or modify existing ones\n\n## Realtime by Default\n\nService plugins automatically listen to all socket messages received by the Feathers Client.  This can be disabled by setting `enableEvents: false` in the options, as shown above.\n\n## The FeathersClient Service\n\nOnce the service plugin has been registered with Vuex, the FeathersClient Service will have a new `service.FeathersVuexModel` property.  This provides access to the service's [Model class](./model-classes.html).\n\n```js\nimport { models } from 'feathers-vuex'\n\nfeathersClient.service('todos').FeathersVuexModel === models.api.Todo // true\n```\n\n## Service State\n\nEach service comes loaded with the following default state:\n\n```js\n{\n    ids: [],\n    idField: 'id',\n    keyedById: {},\n    tempsById: {},\n    tempsByNewId: {},\n    pagination: {\n      defaultLimit: null,\n      defaultSkip: null\n    },\n    servicePath: 'v1/todos'\n    modelName: 'Todo',\n    autoRemove: false,\n    replaceItems: false,\n\n    pagination: {\n      ids: []\n      limit: 0\n      skip: 0\n      ip: 0\n      total: 0,\n      mostRecent: any\n    },\n\n    paramsForServer: ['$populateParams'],\n    whitelist: [],\n\n    isFindPending: false,\n    isGetPending: false,\n    isCreatePending: false,\n    isUpdatePending: false,\n    isPatchPending: false,\n    isRemovePending: false,\n\n    errorOnfind: undefined,\n    errorOnGet: undefined,\n    errorOnCreate: undefined,\n    errorOnUpdate: undefined,\n    errorOnPatch: undefined,\n    errorOnRemove: undefined,\n\n    isIdCreatePending: [],\n    isIdUpdatePending: [],\n    isIdPatchPending: [],\n    isIdRemovePending: [],\n  }\n```\n\n- `ids {Array}` - an array of plain ids representing the ids that belong to each object in the `keyedById` map.\n- `idField {String}` - the name of the field that holds each item's id. *Default: `'id'`*\n- `keyedById {Object}` - a hash map keyed by the id of each item.\n- `tempsById {Object}` - a hash map of temporary records, [keyed by tempIdField](./getting-started.html#global-configuration) of each item\n- `tempsByNewId {Object}` - temporary storage for temps while getting transferred from tempsById to keyedById\n- `servicePath {String}` - the full service path, even if you alias the namespace to something else.\n- `modelName {String}` - the key in the $FeathersVuex plugin where the model will be found.\n- `autoRemove {Boolean}` - indicates that this service will not automatically remove results missing from subsequent requests.  Only use with feathers-rest. Default is false.\n- `replaceItems {Boolean}` - When set to true, updates and patches will replace the record in the store instead of merging changes.  Default is false\n\n- `pagination {Object}` - provides informaiton about the last made queries\n\n- `paramsForServer {Array}` - Custom query operators that are ignored in the find getter, but will pass through to the server.\n- `whitelist {Array}` - Custom query operators that will be allowed in the find getter.\n\nThe following state attributes allow you to bind to the pending state of requests:\n\n- `isFindPending {Boolean}` - `true` if there's a pending `find` request. `false` if not.\n- `isGetPending {Boolean}` - `true` if there's a pending `get` request. `false` if not.\n- `isCreatePending {Boolean}` - `true` if there's a pending `create` request. `false` if not.\n- `isUpdatePending {Boolean}` - `true` if there's a pending `update` request. `false` if not.\n- `isPatchPending {Boolean}` - `true` if there's a pending `patch` request. `false` if not.\n- `isRemovePending {Boolean}` - `true` if there's a pending `remove` request. `false` if not.\n\nThe following state attribute will be populated with any request error, serialized as a plain object:\n\n- `errorOnFind {Error}`\n- `errorOnGet {Error}`\n- `errorOnCreate {Error}`\n- `errorOnUpdate {Error}`\n- `errorOnPatch {Error}`\n- `errorOnRemo {Error}`\n\nThe following state attributes allow you to bind to the pending state of requests *per item ID*\n\n- `isIdCreatePending {Array}` - Contains `id` if there's a pending `create` request for `id`.\n- `isIdUpdatePending {Array}` -Contains `id` if there's a pending `update` request for `id`.\n- `isIdPatchPending {Array}` - Contains `id` if there's a pending `patch` request for `id`.\n- `isIdRemovePending {Array}` - Contains `id` if there's a pending `remove` request for `id`.\n\n## Service Getters\n\nService modules include the following getters:\n\n- `list {Array}` - an array of items. The array form of `keyedById`  Read only.\n- `find(params) {Function}` - a helper function that allows you to use the [Feathers Adapter Common API](https://docs.feathersjs.com/api/databases/common) and [Query API](https://docs.feathersjs.com/api/databases/querying) to pull data from the store.  This allows you to treat the store just like a local Feathers database adapter (but without hooks).\n  - `params {Object}` - an object with a `query` object and optional properties. You can set the following  properties:\n    - `params.query {Boolean}` - The `query` is in the FeathersJS query format.\n    - `params.temps {Boolean}` - **Default:** `false` - if `true` also consider temporary records from `tempsById`\n    - `params.copies {Boolean}` - **Default:** `false` - if `true`: first search for the regular records and then replace the records with the related copies from `copiesById`\n- `count(params) {Function}` - a helper function that counts items in the store matching the provided query in the params and returns this number <Badge text=\"3.12.0+\" />\n  - `params {Object}` - an object with a `query` object and an optional `temps` boolean property.\n- `get(id[, params]) {Function}` - a function that allows you to query the store for a single item, by id.  It works the same way as `get` requests in Feathers database adapters.\n  - `id {Number|String}` - the id of the data to be retrieved by id from the store.\n  - `params {Object}` - an object containing a Feathers `query` object.\n\nThe following getters ease access to per-instance pending status\n\n- `isCreatePendingById(id) {Function}` - Check if `create` is pending for `id`\n- `isUpdatePendingById(id) {Function}` - Check if `update` is pending for `id`\n- `isPatchPendingById(id) {Function}` - Check if `patch` is pending for `id`\n- `isRemovePendingById(id) {Function}` - Check if `remove` is pending for `id`\n- `isSavePendingById(id) {Function}` - Check if `create`, `update`, or `patch` is pending for `id`\n- `isPendingById(id) {Function}` - Check if `create`, `update`, `patch` or `remove` is pending for `id`\n\n## Service Mutations\n\nThe following mutations are included in each service module.\n> **Note:** you would typically not call these directly, but instead with `store.commit('removeItem', 'itemId')`. Using vuex's mapMutations on a Vue component can simplify that to `this.removeItem('itemId')`\n\n### `addItem(state, item)`\n\nAdds a single item to the `keyedById` map.\n\n- `item {Object}` - The item to be added to the store.\n\n### `addItems(state, items)`\n\nAdds an array of items to the `keyedById` map.\n\n- `items {Array}` - the items to be added to the store.\n\n### `updateItem(state, item)`\n\nUpdates an item in the store to match the passed in `item`.\n\n- `item {Object}` the item, including `id`, to replace the currently-stored item.\n\n### `updateItems(state, items)`\n\nUpdates multiple items in the store to match the passed in array of items.\n\n- `items {Array}` - An array of items.\n\n### `removeItem(state, item)`\n\nRemoves a single item.  `item` can be\n\n- `item {Number|String|Object}` - The item or id of the item to be deleted.\n\n### `removeTemps(state, tempIds)`\n\n// Removes temp records. Also cleans up tempsByNewId\n\n- `items {Array}` - An array of ids or of objects with tempIds that will be removed from the data store\n\n### `removeItems(state, items)`\n\nRemoves the passed in items or ids from the store.\n\n- `items {Array}` - An array of ids or of objects with ids that will be removed from the data store.\n\n### `clearAll(state)`\n\nClears all data from `ids`, `keyedById`, and `currentId`\n\n### Mutations for Managing Pending State\n\nThe following mutations are called automatically by the service actions, and will rarely, if ever, need to be used manually.\n\n- `setPending(state, method)` - sets the `is${method}Pending` attribute to true\n- `setIdPending(state, { method, id })` - adds `id` to `isId${method}Pending` array\n- `unsetPending(state, method)` - sets the `is${method}Pending` attribute to false\n- `unsetIdPending(state, { method, id })` - removes `id` from `isId${method}Pending` array\n\n### Mutations for Managing Errors\n\nThe following mutations are called automatically by the service actions, and will rarely need to be used manually.\n\n- `setError(state, { method, error })` - sets the `errorOn${method}` attribute to the error\n- `clearError(state, method)` - sets the `errorOn${method}` attribute to `null`\n\n## Service Actions\n\nAn action is included for each of the Feathers service interface methods.  These actions will affect changes in both the Feathers API server and the Vuex store.\n\nAll of the [Feathers Service Methods](https://docs.feathersjs.com/api/databases/common#service-methods) are supported.  Because Vuex only supports providing a single argument to actions, there is a slight change in syntax that works well.  If you need to pass multiple arguments to a service method, pass an array to the action with the order of the array elements matching the order of the arguments.  See each method for examples.\n\n> Note: If you use the Feathers service methods, directly, the store will not change. Only the actions will cause store changes.\n\n### `find(params)`\n\nQuery an array of records from the server & add to the Vuex store.\n\n- `params {Object}` - An object containing a `query` object and an optional `paginate` boolean.  You can set `params.paginate` to `false` to disable pagination for a single request.\n\n```js\nlet params = {query: {completed: true}}\nstore.dispatch('todos/find', params)\n```\n\nSee the section about pagination, below, for more information that is applicable to the `find` action.  Make sure your returned records have a unique field that matches the `idField` option for the service plugin.\n\n### `afterFind(response)`\n\nThe `afterFind` action is called by the `find` action after a successful response is added to the store.  It is called with the current response.  By default, it is a no-op (it literally does nothing), and is just a placeholder for you to use when necessary.  See the sections on [customizing the default store](#Customizing-a-Service’s-Default-Store) and [Handling custom server responses](./common-patterns.html#Handling-custom-server-responses) for example usage.\n\n### `count(params)` <Badge text=\"3.12.0+\" />\n\nCount items on the server matching the provided query.\n\n- `params {Object}` - An object containing a `query` object. In the background `$limit: 0` will be added to the `query` to perform a (fast) counting query against the database.\n\n> **Note:** it only works for services with enabled pagination!\n\n```js\nlet params = {query: {completed: false}}\nstore.dispatch('todos/count', params)\n```\n\nThis will run a (fast) counting query against the database and return a page object with the total and an empty data array.\n\n### `get(id)` or `get([id, params])`\n\nQuery a single record from the server & add to Vuex store\n\n- `id {Number|String}` - the `id` of the record being requested from the API server.\n- `params {Object}` - An object containing a `query` object.\n\n```js\nstore.dispatch('todos/get', 1)\n\n// Use an array to pass params\nlet params = {}\nstore.dispatch('todos/get', [1, params])\n```\n\nMake sure your returned records have a unique field that matches the `idField` option for the service plugin.\n\n### `create(data|ParamArray)`\n\nCreate one or multiple records.  Note that the method is overloaded to accept two types of arguments.  If you want a consistent interface for creating single or multiple records, use the array syntax, described below.  Creating multiple records requires using the `paramArray` syntax.\n\n- `data {Object|ParamArray}` - if an object is provided, a single record will be created.\n\n```js\nlet newTodo = {description: 'write good tests'}\nstore.dispatch('todos/create', newTodo)\n```\n\n- `data {ParamArray}` - if an array is provided, it is assumed to have this structure:\n\n- `ParamArray {Array}` - array containing the two parameters that Feathers' `service.create` method accepts.\n  - `data {Object|Array}` - the data to create. Providing an object creates a single record. Providing an array of objects creates multiple records.\n  - `params {Object}` - optional - an object containing a `query` object. Can be useful in rare situations.\n\nMake sure your returned records have a unique field that matches the `idField` option for the service plugin.\n\n### `update(paramArray)`\n\nUpdate (overwrite) a record.\n\n- `paramArray {Array}` - array containing the three parameters update accepts.\n  - `id {Number|String}` - the `id` of the existing record being requested from the API server.\n  - `data {Object}` - the data that will overwrite the existing record\n  - `params {Object}` - An object containing a `query` object.\n\n```js\nlet data = {id: 5, description: 'write your tests', completed: true}\nlet params = {}\n// Overwrite item 1 with the above data (FYI: Most databases won't let you change the id.)\nstore.dispatch('todos/update', [1, data, params])\n```\n\nAlternatively in a Vue component\n\n```js\nimport { mapActions } from 'vuex'\nexport default {\n  methods: {\n    ...mapActions('todos', [ 'update' ]),\n    addTodo () {\n      let data = {id: 5, description: 'write your tests', completed: true}\n      this.update([1, data, {}])\n    }\n  }\n}\n```\n\nMake sure your returned records have a unique field that matches the `idField` option for the service plugin.\n\n### `patch(paramArray)`\n\nPatch (merge in changes) one or more records\n\n- `paramArray {Array}` - array containing the three parameters patch takes.\n  - `id {Number|String}` - the `id` of the existing record being requested from the API server.\n  - `data {Object}` - the data that will be merged into the existing record\n  - `params {Object}` - An object containing a `query` object. If params.data is provided, it will be used as the patch data, providing a simple way to patch with partial data.\n\n```js\nlet data = {description: 'write your tests', completed: true}\nlet params = {}\nstore.dispatch('todos/patch', [1, data, params])\n```\n\nMake sure your returned records have a unique field that matches the `idField` option for the service plugin.\n\n### `remove(id)`\n\nRemove/delete the record with the given `id`.\n\n- `id {Number|String}` - the `id` of the existing record being requested from the API server.\n\n```js\nstore.dispatch('todos/remove', 1)\n```\n\nMake sure your returned records have a unique field that matches the `idField` option for the service plugin.\n\n## Service Events\n\nBy default, the service plugin listens to all of the FeathersJS events:\n\n- `created` events will add new record to the store.\n- `patched` events will add (if new) or update (if present) the record in the store.\n- `updated` events will add (if new) or update (if present) the record in the store.\n- `removed` events will remove the record from the store, if present.\n\nThis behavior can be turned off completely by passing `enableEvents: false` in either the global Feathers-Vuex options or in the service plugin options.  If you configure this at the global level, the service plugin level will override it.  For example, if you turn off events at the global level, you can enable them for a specific service by setting `enableEvents: true` on that service's options.\n\n### Custom Event Handlers <Badge text=\"3.1.0+\" />\n\nAs of version 3.1, you can customize the behavior of the event handlers, or even perform side effects based on the event data.  This is handled through the new `handleEvents` option on the service plugin.  Here is an example of how you might use this:\n\n```js\nhandleEvents: {\n  created: (item, { model, models }) => {\n    // Perform a side effect to remove any record with the same `name`\n    const existing = Model.findInStore({ query: { name: item.name }}).data[0]\n    if (existing) {\n      existing.remove()\n    }\n\n    // Perform side effects with other models.\n    const { SomeModel } = models.api\n    new SomeModel({ /* some custom data */ }).save()\n\n    // Access the store through model.store\n    const modelState = model.store.state[model.namespace]\n    if (modelState.keyedById[5]) {\n      console.log('we accessed the vuex store')\n    }\n\n    // If true, the new item will be stored.\n    return true\n  },\n  updated: () => false, // Ignore `updated` events.\n  patched: item => item.hasPatchedAttribute && item.isWorthKeeping,\n  removed: item => true // The default value, will remove the record from the store\n}\n```\n\nAs shown above, each handler has two possible uses:\n\n1. Control the default behavior of the event by returning a boolean.\n  - For `created`, `patched`, and `updated` a truthy return will add or update the item in the store.\n  - For `removed` a truthy return will remove the item from the store, if present.\n2. Perform side effects using the current service `model` or with other `models`.  The `models` object is the same as the `$FeathersVuex` object in the Vue plugin.\n\nEach handler receives the following arguments:\n\n- `item`: the record sent from the API server\n- `utils`: an object containing the following properties\n   - `model` The current service's Model class.\n   - `models` The same as the `$FeathersVuex` object, gives you access to each api with their respective model classes.\n\nYou do not have to specify a handler for every event. Any that are not specified in your service-specific `handleEvents`, will fall back to using the `handleEvents` handler in your global options. If none are defined for the service or globally, the default behavior is controlled by the `enableEvents` option.\n\n#### Handling complex events <Badge text=\"3.10.0+\" />\n\nIf your application emits the standard Feathers service events inside a nested object with additional data, you can use `handleEvents` to tell FeathersVuex what part of that data is actually the model data that should be used to update the store.\n\nTo do this, use `handleEvents` as described before, but return a tuple `[affectsStore, modelData]` from your handler.\n\n- `affectsStore` a truthy value indicates the event should update the store\n- `modelData` is the model data used to update the store\n\nFor example, you've configured your Feathers API to emit `patched` events for your `Todos` service that include context about the event which look like\n\n```json\n{\n  \"$context\": {\n    \"time\": 1445411009000,\n    \"userId\": 121,\n    \"deviceId\": \"Marty's iPhone\"\n  },\n  \"event\": {\n    \"id\": 88,\n    \"text\": \"Get back to the past\",\n    \"done\": true\n  }\n}\n```\n\nFor this service to play nicely with FeathersVuex, you'll need to use `handleEvents`\n\n```js\nhandleEvents: {\n  patched: (item, { model, models }) => {\n    // Perform any side effects...\n\n    // If the first element is truthy, the item will update the store\n    // The second element is the actual model data to add to the store\n    return [true, item.event]\n  }\n}\n```\n\nThe original event data is bubbled to [Model events](./model-classes.md#model-events) so listeners receive the full event context.\n\n## Pagination and the `find` action\n\nBoth the `find` action and the `find` getter support pagination.  There are differences in how they work.\n\nImportant: For the built in pagination features to work, you must not directly manipulate the `context.params` object in any before hooks.  You can still use before hooks as long as you clone the params object, then make changes to the clone.\n\n### The `find` action\n\nThe `find` action queries data from the remote server.  It returns a promise that resolves to the response from the server.  The presence of pagination data will be determined by the server.\n\n`feathers-vuex@1.0.0` can store pagination data on a per-query basis.  The `pagination` store attribute maps queries to their most-recent pagination data.  The default pagination state looks like this:\n\n```js\n{\n  pagination: {\n    defaultLimit: null,\n    defaultSkip: null\n  }\n}\n```\n\nYou should never manually change these values.  They are managed internally.\n\nThere's not a lot going on, by default.  The `defaultLimit` and `defaultSkip` properties are null until a query is made on the service without `$limit` or `$skip`.  In other words, they remain `null` until an empty query comes through, like the this one:\n\n**`params = { query: {} }`**\n\n```js\n{\n  pagination : {\n    defaultLimit: 25,\n    defaultSkip: 0,\n    default: {\n      mostRecent: {\n        query: {},\n        queryId: '{}',\n        queryParams: {},\n        pageId: '{$limit:25,$skip:0}',\n        pageParams: { $limit: 25, $skip: 0 },\n        queriedAt: 1538594642481\n      },\n      '{}': {\n        total: 155,\n        queryParams: {},\n        '{$limit:25,$skip:0}': {\n          pageParams: { $limit: 25, $skip: 0 },\n          ids: [ 1, 2, 3, 4, '...etc', 25 ],\n          queriedAt: 1538594642481\n        }\n      }\n    }\n  }\n}\n```\n\nIt looks like a lot just happened, so let's walk through it.  First, notice that we have values for `defaultLimit` and `defaultSkip`.  These come in handy for the `find` getter, which will be covered later.\n\n### The `qid`\n\nThe state now also contains a property called `default`.  This is the default `qid`, which is a \"query identifier\" that you choose.  Unless you're building a small demo, your app will require to storing pagination information for more than one query.  For example, two components could make two distinct queries against this service.  You can use the `params.qid` (query identifier) property to assignn identifier to the query.  If you set a `qid` of `mainListView`, for example, the pagination for this query will show up under `pagination.mainListView`.  The `pagination.default` property will be used any time a `params.qid` is not provided.  Here's an example of what this might look like:\n\n**`params = { query: {}, qid: 'mainListView' }`**\n\n```js\n// Data in the store\n{\n  pagination : {\n    defaultLimit: 25,\n    defaultSkip: 0,\n    mainListView: {\n      mostRecent: {\n        query: {},\n        queryId: '{}',\n        queryParams: {},\n        pageId: '{$limit:25,$skip:0}',\n        pageParams: { $limit: 25, $skip: 0 },\n        queriedAt: 1538594642481\n      },\n      '{}': {\n        total: 155,\n        queryParams: {},\n        '{$limit:25,$skip:0}': {\n          pageParams: { $limit: 25, $skip: 0 },\n          ids: [ 1, 2, 3, 4, '...etc', 25 ],\n          queriedAt: 1538594642481\n        }\n      }\n    }\n  }\n}\n```\n\nThe above example is almost exactly the same as the previous one.  The only difference is that the `default` key is now called `mainListView`.  This is because we provided that value as the `qid` in the params.  Let's move on to the properties under the `qid`.\n\n### The `mostRecent` object\n\nThe `mostRecent` propery contains information about the most recent query.  These properties provide insight into how pagination works.  The two most important properties are the `queryId` and the `pageId`.\n\n- The `queryId` describes the set of data we're querying.  It's a stable, stringified version of all of the query params **except** for `$limit` and `$skip`.\n- The `pageId` holds information about the current \"page\" (as in \"page-ination\").  A page is described using `$limit` and `$skip`.\n\nThe `queryParams` and `pageParams` are the non-stringified `queryId` and `pageId`.  The `query` attribute is the original query that was provided in the request params.  Finally, the `queriedAt` is a timestamp of when the query was performed.\n\n### The `queryId` and `pageId` tree\n\nThe rest of the `qid` object is keyed by `queryId` strings.  Currently, we only have a single `queryId` of `'{}'`.  In the `queryId` object we have the `total` numer of records (as reported by the server) and the `pageId` of `'{$limit:25,$skip:0}'`\n\n```js\n'{}': { // queryId\n  total: 155,\n  queryParams: {},\n  '{$limit:25,$skip:0}': { // pageId\n    pageParams: { $limit: 25, $skip: 0 },\n    ids: [ 1, 2, 3, 4, '...etc', 25 ],\n    queriedAt: 1538594642481\n  }\n}\n```\n\nThe `pageId` object contains the `queriedAt` timestamp of when we last queried this page of data.  It also contains an array of `ids`, holding only the `ids` of the records returned from the server.\n\n### Additional Queries and Pages\n\nAs more queries are made, the pagination data will grow to represent what we have in the store.  In the following example, we've made an additional query for sorted data in the `mainListView` `qid`.  We haven't filtered the list down any, so the `total` is the same as before.  We have sorted the data by the `isComplete` attribute, which changes the `queryId`.  You can see the second `queryId` object added to the `mainListView` `qid`:\n\n**`params = { query: {}, qid: 'mainListView' }`**<br/>\n**`params = { query: { $limit: 10, $sort: { isCompleted: 1 } }, qid: 'mainListView' }`**\n\n```js\n// Data in the store\n{\n  pagination : {\n    defaultLimit: 25,\n    defaultSkip: 0,\n    mainListView: {\n      mostRecent: {\n        query: { $sort: { isCompleted: 1 } },\n        queryId: '{$sort:{isCompleted:1}}',\n        queryParams: { $sort: { isCompleted: 1 } },\n        pageId: '{$limit:10,$skip:0}',\n        pageParams: { $limit: 10, $skip: 0 },\n        queriedAt: 1538595856481\n      },\n      '{}': {\n        total: 155,\n        queryParams: {},\n        '{$limit:25,$skip:0}': {\n          pageParams: { $limit: 25, $skip: 0 },\n          ids: [ 1, 2, 3, 4, '...etc', 25 ],\n          queriedAt: 1538594642481\n        }\n      },\n      '{$sort:{isCompleted:1}}': {\n        total: 155,\n        queryParams: {},\n        '{$limit:10,$skip:0}': {\n          pageParams: { $limit: 10, $skip: 0 },\n          ids: [ 4, 21, 19, 29, 1, 95, 62, 21, 67, 125 ],\n          queriedAt: 1538594642481\n        }\n      }\n    }\n  }\n}\n```\n\nIn summary, any time a query param other than `$limit` and `$skip` changes, we get a new `queryId`.  Whenever `$limit` and `$skip` change, we get a new `pageId` inside the current `queryId`.\n\n### Why use this pagination structure\n\nNow that we've reviewed how pagination tracking works under the hood, you might be asking \"Why?\"  There are a few reasons:\n\n1. Improve performance with cacheing.  It's now possible to skip making a query if we already have valid data for the current query.  The [`makeFindMixin`](./mixins.html) mixin makes this very easy with its built-in `queryWhen` feature.\n2. Allow fall-through cacheing of paginated data.  A common challenge occurs when you provide the same query params to the `find` action and the `find` getter.  As you'll learn in the next section, the `find` getter allows you to make queries against the Vuex store as though it were a Feathers database adapter.  But what happens when you pass `{ $limit: 10, $skip: 10 }` to the action and getter?<br/>\nFirst, lets review what happens with the `find` action.  The database is aware of all 155 records, so it skips the first 10 and returns the next 10 records.  Those records get populated in the store, so the store now has 10 records.  Now we pass the query to the `find` getter and tell it to `$skip: 10`.  It skips the only 10 records that are in the store and returns an empty array!  That's definitely not what we wanted.<br/>\nSince we're now storing this pagination structure, we can build a utility around the `find` getter which will allow us to return the same data with the same query.  The data is still reactive and will automatically update when a record changes.\n\nThere's one limitation to this solution.  What happens when you add a new record that matches the current query?  Depending on where the new record would be sorted into the current query, part or all of the cache is no longer valid.  It will stay this way until a new query is made.  To get live (reactive) lists, you have to use the `find` getter with its own distinct query, removing the `$limit` and `$skip` values.  This way, when a new record is created, it will automatically get added to the array in the proper place.\n\n## Pagination and the `find` getter\n\nThe `find` getter queries data from the local store using the same Feathers query syntax as on the server.  It is synchronous and returns the results of the query with pagination.  Pagination cannot be disabled.  It accepts a params object with a `query` attribute.  It does not use any other special attributes.  The returned object looks just like a paginated result that you would receive from the server:\n\n**`params = { query: {} }`**\n\n```js\n// The returned results object\n{\n  data: [{ _id: 1, ...etc }, ...etc],\n  limit: 0,\n  skip: 0,\n  total: 3\n}\n```\n\n## Customizing a Service's Default Store\n\n### New `extend` option for `makeServicePlugin` <Badge text=\"3.14.0+\" />\n\nAs of version `3.14.0`, the `makeServicePlugin` now supports an `extend` method that allows customizing the store and gives access to the actual Vuex `store` object, as shown in this example:\n\n```js\nimport { makeServicePlugin } from ‘feathers-vuex’\nimport { feathersClient } from ‘./feathers-client.js’\n\nclass Todo { /* truncated */ }\n\nexport default makeServicePlugin({\n  Model: Todo,\n  service: feathersClient.service(‘todos’),\n  extend({ store, module }) {\n    // Listen to other parts of the store\n    store.watch(/* truncated */)\n\n    return {\n      state: {},\n      getters: {},\n      mutations: {},\n      actions: {}\n    }\n  }\n})\n```\n\n### Deprecated options for customizing the store\n\nBefore version `3.14.0`, you can customize the store using the options for `state`, `getters`, `mutations`, and `actions`, as shown below.  This method is now deprecated and will be removed from Feathers-Vuex 4.0.\n\n```js\n// src/store/services/users.js\nimport feathersClient, { makeServicePlugin, BaseModel } from '../../feathers-client'\n\nclass Asset extends BaseModel {\n  constructor(data, options) {\n    super(data, options)\n  }\n  // Required for $FeathersVuex plugin to work after production transpile.\n  static modelName = 'Asset'\n  // Define default properties here\n  static instanceDefaults() {\n    return {\n      email: '',\n      password: ''\n    }\n  }\n}\n\nconst servicePath = 'assets'\nconst servicePlugin = makeServicePlugin({\n  Model: Asset,\n  service: feathersClient.service(servicePath),\n  servicePath,\n  state: {\n    test: true\n  },\n  getters: {\n    getSomeData () {\n      return 'some data'\n    }\n  },\n  mutations: {\n    setTest (state, val) {\n      state.test = val;\n    },\n  },\n  actions: {\n    // Overwriting the built-in `afterFind` action.\n    afterFind ({ commit, dispatch, getters, state }, response) {\n      // Do something with the response.\n      // Keep in mind that the data is already in the store.\n    },\n    asyncStuff ({ state, getters, commit, dispatch }, args) {\n      commit('setTestToTrue')\n      return new Promise.resolve(\"\")\n    }\n  }\n})\n\nexport default servicePlugin\n```\n"
  },
  {
    "path": "docs/vue-plugin.md",
    "content": "---\ntitle: Vue Plugin\n---\n\n# The Vue Plugin\n\nThis `feathers-vuex` release includes a Vue plugin which gives all of your components easy access to the  data modeling classes.  It also automatically registers the included components.  The below example is based on the [setup instructions in the API overview](/api-overview.html#setup).\n\n```js\n// src/store/store.js\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport { FeathersVuex } from '../feathers-client'\nimport auth from './store.auth'\n\nVue.use(Vuex)\nVue.use(FeathersVuex)\n\nconst requireModule = require.context(\n  // The path where the service modules live\n  './services',\n  // Whether to look in subfolders\n  false,\n  // Only include .js files (prevents duplicate imports`)\n  /.js$/\n)\nconst servicePlugins = requireModule\n  .keys()\n  .map(modulePath => requireModule(modulePath).default)\n\nexport default new Vuex.Store({\n  state: {},\n  mutations: {},\n  actions: {},\n  plugins: [...servicePlugins, auth]\n})\n```\n\n## Using the Vue Plugin\n\nOnce registered, you'll have access to the `this.$FeathersVuex` object.  *In version 2.0, there is a breaking change to this object's structure.*  Instead of directly containing references to the Model classes, the top level is keyed by `serverAlias`.  Each `serverAlias` then contains the Models, keyed by name.  This allows Feathers-Vuex 2.0 to support multiple FeathersJS servers in the same app.  This new API means that the following change is required wherever you reference a Model class:\n\n```js\n// 1.x way\nnew this.$FeathersVuex.User({})\n\n// 2.x way\nnew this.$FeathersVuex.api.User({}) // Assuming default serverAlias of `api`.\nnew this.$FeathersVuex.myApi.user({}) // If you customized the serverAlias to be `myApi`.\n```\n\nThe name of the model class is automatically inflected to singular, initial caps, based on the last section of the service path (split by `/`).  Here are some examples of what this looks like:\n\n| Service Name              | Model Name in `$FeathersVuex` |\n| ------------------------- | ----------------------------- |\n| /cart                     | Cart                          |\n| /todos                    | Todo                          |\n| /v1/districts             | District                      |\n| /some/deeply/nested/items | Item                          |\n\nThe `$FeathersVuex` object is available on the Vue object, directly at `Vue.$FeathersVuex`, as well as on the prototype, making it available in components:\n\n```js\n// In your Vue component\ncreated () {\n  const todo = new this.$FeathersVuex.Todo({ description: 'Do something!' })\n  // `todo` is now a model instance\n}\n```\n\n## New in 2.0\n\nIn Feathers-Vuex 2.0, the $FeathersVuex object is available as the 'models' export in the global package scope. This means you can do the following anywhere in your app:\n\n```js\nimport { models } from 'feathers-vuex'\n\nconst user = new models.api.User({\n  email: 'test@test.com'\n})\n```\n\n## Included Components\n\nWhen you register the Vue Plugin, a few components are automatically globally registered:\n\n- The [Renderless Data components](/data-components.html)\n- The [`FeathersVuexFormWrapper` component](/feathers-vuex-forms.html#feathersvuexformwrapper)\n- The [`FeathersVuexInputWrapper` component](/feathers-vuex-forms.html#feathersvuexinputwrapper)\n- The [`FeathersVuexPagination` component](/composition-api.html#feathersvuexpagination)\n\nYou can pass `components: false` in the options to not globally register the component:\n\n```js\nVue.use(FeathersVuex, { components: false })\n```"
  },
  {
    "path": "mocha.opts",
    "content": "--compilers js:babel-core/register\ntest/node.test.js"
  },
  {
    "path": "notes.old.md",
    "content": "## Extending the built-in Model classes\n\nIf you desire to extend the built-in Models\n\n**store/index.js:**\n```js\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport feathersVuex from 'feathers-vuex'\nimport feathersClient from '../feathers-client'\n\nconst { service, auth, FeathersVuex } = feathersVuex(feathersClient, { idField: '_id' })\nconst { serviceModule, serviceModel, servicePlugin } = service\n\nconst api1Client = feathersVuex(feathersClient, { idField: '_id', apiPrefix: 'api1' })\nconst api2Client = feathersVuex(feathersClient2, { idField: '_id' })\n\nVue.use(FeathersVuex)\n\nconst todoModule = serviceModule('todos')\n\n// const Model = serviceModel(todoModule) // TodoModel is an extensible class\nconst Model = serviceModel()\nclass TodoModel = extends Model {}\nconst todoPlugin = servicePlugin(todoModule, TodoModel)\n\nconst TaskModel extends Model {}\n\nexport { TaskModel }\n\n\ncreated () {\n  this.todo = new this.$FeathersVuex.api1.Todo(data)\n}\n\nVue.use(Vuex)\nVue.use(FeathersVuex)\n\nexport default new Vuex.Store({\n  plugins: [\n    servicePlugin('/tasks', TaskModel), // With our potentially customized TodoModel\n\n    service('todos'),\n\n    // Specify custom options per service\n    service('/v1/tasks', {\n      idField: '_id', // The field in each record that will contain the id\n      nameStyle: 'path', // Use the full service path as the Vuex module name, instead of just the last section\n      namespace: 'custom-namespace', // Customize the Vuex module name.  Overrides nameStyle.\n      autoRemove: true, // Automatically remove records missing from responses (only use with feathers-rest)\n      enableEvents: false, // Turn off socket event listeners. It's true by default\n      addOnUpsert: true, // Add new records pushed by 'updated/patched' socketio events into store, instead of discarding them. It's false by default\n      skipRequestIfExists: true, // For get action, if the record already exists in store, skip the remote request. It's false by default\n      modelName: 'Task'\n    })\n\n    // Add custom state, getters, mutations, or actions, if needed.  See example in another section, below.\n    service('things', {\n      state: {},\n      getters: {},\n      mutations: {},\n      actions: {}\n    })\n\n    auth()\n  ]\n})\n```"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"feathers-vuex\",\n  \"description\": \"FeathersJS, Vue, and Nuxt for the artisan developer\",\n  \"version\": \"3.16.0\",\n  \"homepage\": \"https:feathers-vuex.feathersjs-ecosystem.com\",\n  \"main\": \"dist/\",\n  \"module\": \"dist/\",\n  \"types\": \"dist/\",\n  \"keywords\": [\n    \"vue\",\n    \"feathers\",\n    \"feathers-plugin\"\n  ],\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/feathersjs-ecosystem/feathers-vuex.git\"\n  },\n  \"author\": {\n    \"name\": \"Marshall Thompson\",\n    \"email\": \"marshall@creativeideal.net\",\n    \"url\": \"https://github.com/marshallswain\"\n  },\n  \"funding\": {\n    \"type\": \"Github sponsor\",\n    \"url\": \"https://github.com/sponsors/marshallswain\"\n  },\n  \"contributors\": [],\n  \"bugs\": {\n    \"url\": \"https://github.com/feathersjs-ecosystem/feathers-vuex/issues\"\n  },\n  \"engines\": {\n    \"node\": \">= 4.6.0\"\n  },\n  \"scripts\": {\n    \"prepublish\": \"npm run compile\",\n    \"publish\": \"git push origin --tags && git push origin\",\n    \"release:pre\": \"npm version prerelease && npm publish --tag pre\",\n    \"release:patch\": \"npm version patch && npm publish\",\n    \"release:minor\": \"npm version minor && npm publish\",\n    \"release:major\": \"npm version major && npm publish\",\n    \"changelog\": \"github_changelog_generator && git add CHANGELOG.md && git commit -am \\\"Updating changelog\\\"\",\n    \"compile\": \"shx rm -rf lib/ && tsc && npm run lint-dist\",\n    \"lint-dist\": \"prettier --write \\\"dist/**/*.js\\\"\",\n    \"watch\": \"shx rm -rf lib/ && babel --watch -d lib/ src/\",\n    \"lint\": \"standard --fix\",\n    \"coverage\": \"istanbul cover node_modules/mocha/bin/_mocha -- --opts mocha.opts\",\n    \"test\": \"cross-env TS_NODE_PROJECT='tsconfig.test.json' mocha --require ts-node/register 'test/**/*.test.ts'\",\n    \"testee\": \"testee test/index.html --browsers firefox\",\n    \"start\": \"npm run compile && node example/app\",\n    \"docs\": \"vuepress dev docs\",\n    \"docs:build\": \"vuepress build docs\"\n  },\n  \"prettier\": {\n    \"singleQuote\": true,\n    \"semi\": false,\n    \"trailingComma\": \"none\",\n    \"tabWidth\": 2\n  },\n  \"eslintConfig\": {\n    \"root\": true,\n    \"env\": {\n      \"node\": true,\n      \"mocha\": true\n    },\n    \"extends\": [\n      \"plugin:@typescript-eslint/recommended\",\n      \"prettier/@typescript-eslint\",\n      \"plugin:prettier/recommended\"\n    ],\n    \"rules\": {\n      \"linebreak-style\": [\n        \"warn\",\n        \"unix\"\n      ],\n      \"prettier/prettier\": [\n        \"warn\",\n        {\n          \"fix\": true,\n          \"singleQuote\": true,\n          \"semi\": false,\n          \"trailingComma\": \"none\",\n          \"arrowParens\": \"avoid\"\n        }\n      ]\n    },\n    \"parserOptions\": {\n      \"parser\": \"@typescript-eslint/parser\",\n      \"ecmaVersion\": 2018,\n      \"sourceType\": \"module\"\n    }\n  },\n  \"steal\": {\n    \"map\": {\n      \"assert\": \"chai/chai\"\n    },\n    \"meta\": {\n      \"chai/chai\": {\n        \"format\": \"global\",\n        \"exports\": \"chai.assert\"\n      }\n    },\n    \"plugins\": [\n      \"chai\"\n    ]\n  },\n  \"directories\": {\n    \"lib\": \"lib\"\n  },\n  \"peerDependencies\": {\n    \"@vue/composition-api\": \"*\"\n  },\n  \"dependencies\": {\n    \"@feathersjs/adapter-commons\": \"^4.5.2\",\n    \"@feathersjs/commons\": \"^4.5.3\",\n    \"@feathersjs/errors\": \"^4.5.3\",\n    \"@types/feathersjs__feathers\": \"^3.1.5\",\n    \"@types/inflection\": \"^1.5.28\",\n    \"@types/lodash\": \"^4.14.150\",\n    \"@types/npm\": \"^2.0.31\",\n    \"bson-objectid\": \"^1.3.0\",\n    \"debug\": \"^4.1.1\",\n    \"events\": \"^3.1.0\",\n    \"fast-copy\": \"^2.1.0\",\n    \"fast-json-stable-stringify\": \"^2.1.0\",\n    \"inflection\": \"^1.12.0\",\n    \"jwt-decode\": \"^2.2.0\",\n    \"lodash\": \"^4.17.15\",\n    \"lodash.isobject\": \"^3.0.2\",\n    \"lodash.isplainobject\": \"^4.0.6\",\n    \"lodash.merge\": \"^4.6.2\",\n    \"lodash.omit\": \"^4.5.0\",\n    \"lodash.pick\": \"^4.4.0\",\n    \"lodash.trim\": \"^4.5.1\",\n    \"serialize-error\": \"^5.0.0\",\n    \"sift\": \"^9.0.4\"\n  },\n  \"devDependencies\": {\n    \"@feathersjs/authentication-client\": \"^4.5.4\",\n    \"@feathersjs/authentication-jwt\": \"^2.0.10\",\n    \"@feathersjs/client\": \"^4.5.4\",\n    \"@feathersjs/feathers\": \"^4.5.3\",\n    \"@feathersjs/rest-client\": \"^4.5.4\",\n    \"@feathersjs/socketio-client\": \"^4.5.4\",\n    \"@types/chai\": \"^4.2.11\",\n    \"@types/mocha\": \"^7.0.2\",\n    \"@typescript-eslint/eslint-plugin\": \"^2.31.0\",\n    \"@typescript-eslint/parser\": \"^2.31.0\",\n    \"@vue/composition-api\": \"^1.2.4\",\n    \"@vue/eslint-config-prettier\": \"^6.0.0\",\n    \"@vue/eslint-config-typescript\": \"^5.0.2\",\n    \"@vue/test-utils\": \"^1.0.2\",\n    \"axios\": \"^0.21.1\",\n    \"babel-cli\": \"^6.26.0\",\n    \"babel-core\": \"^6.26.3\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"babel-plugin-add-module-exports\": \"^1.0.2\",\n    \"babel-preset-es2015\": \"^6.24.1\",\n    \"babel-preset-stage-2\": \"^6.24.1\",\n    \"body-parser\": \"^1.19.0\",\n    \"can-fixture-socket\": \"^2.0.3\",\n    \"chai\": \"^4.2.0\",\n    \"cross-env\": \"^7.0.2\",\n    \"date-fns\": \"^2.13.0\",\n    \"deep-object-diff\": \"^1.1.0\",\n    \"eslint\": \"^6.8.0\",\n    \"eslint-config-prettier\": \"^6.11.0\",\n    \"eslint-plugin-prettier\": \"^3.1.3\",\n    \"eslint-plugin-vue\": \"^6.2.2\",\n    \"feathers-memory\": \"^4.1.0\",\n    \"istanbul\": \"^1.1.0-alpha.1\",\n    \"jsdom\": \"^16.2.2\",\n    \"jsdom-global\": \"^3.0.2\",\n    \"mocha\": \"^7.1.2\",\n    \"omit-deep-lodash\": \"^1.1.4\",\n    \"prettier\": \"^2.0.5\",\n    \"shx\": \"^0.3.2\",\n    \"socket.io-client\": \"^2.3.0\",\n    \"standard\": \"^14.3.3\",\n    \"steal\": \"^2.2.4\",\n    \"steal-mocha\": \"^2.0.1\",\n    \"steal-typescript\": \"^0.5.0\",\n    \"testee\": \"^0.9.1\",\n    \"ts-node\": \"^8.10.1\",\n    \"typescript\": \"^3.8.3\",\n    \"vue\": \"^2.6.11\",\n    \"vue-server-renderer\": \"^2.6.11\",\n    \"vue-template-compiler\": \"^2.6.11\",\n    \"vuepress\": \"^1.4.1\",\n    \"vuepress-theme-default-prefers-color-scheme\": \"^1.0.7\",\n    \"vuex\": \"^3.3.0\"\n  }\n}\n"
  },
  {
    "path": "src/FeathersVuexCount.ts",
    "content": "import { randomString } from './utils'\n\nexport default {\n  props: {\n    service: {\n      type: String,\n      required: true\n    },\n    params: {\n      type: Object,\n      default: () => {\n        return {\n          query: {}\n        }\n      }\n    },\n    queryWhen: {\n      type: [Boolean, Function],\n      default: true\n    },\n    // If separate params are desired to fetch data, use fetchParams\n    // The watchers will automatically be updated, so you don't have to write 'fetchParams.query.propName'\n    fetchParams: {\n      type: Object\n    },\n    watch: {\n      type: [String, Array],\n      default: () => []\n    },\n    local: {\n      type: Boolean,\n      default: false\n    }\n  },\n  data: () => ({\n    isCountPending: false,\n    serverTotal: null\n  }),\n  computed: {\n    total() {\n      if (!this.local) {\n        return this.serverTotal\n      } else {\n        const { params, service, $store, temps } = this\n        return params ? $store.getters[`${service}/count`](params) : 0\n      }\n    },\n    scope() {\n      const { total, isCountPending } = this\n\n      return { total, isCountPending }\n    }\n  },\n  methods: {\n    findData() {\n      const params = this.fetchParams || this.params\n\n      if (\n        typeof this.queryWhen === 'function'\n          ? this.queryWhen(this.params)\n          : this.queryWhen\n      ) {\n        this.isCountPending = true\n\n        if (params) {\n          return this.$store\n            .dispatch(`${this.service}/count`, params)\n            .then(response => {\n              this.isCountPending = false\n              this.serverTotal = response\n            })\n        }\n      }\n    },\n    fetchData() {\n      if (!this.local) {\n        if (this.params) {\n          return this.findData()\n        } else {\n          // TODO: access debug boolean from the store config, somehow.\n          // eslint-disable-next-line no-console\n          console.log(\n            `No query and no id provided, so no data will be fetched.`\n          )\n        }\n      }\n    }\n  },\n  created() {\n    if (!this.$FeathersVuex) {\n      throw new Error(\n        `You must first Vue.use the FeathersVuex plugin before using the 'FeathersVuexFind' component.`\n      )\n    }\n    if (!this.$store.state[this.service]) {\n      throw new Error(\n        `The '${this.service}' plugin not registered with feathers-vuex`\n      )\n    }\n\n    const watch = Array.isArray(this.watch) ? this.watch : [this.watch]\n\n    if (this.fetchParams || this.params) {\n      watch.forEach(prop => {\n        if (typeof prop !== 'string') {\n          throw new Error(`Values in the 'watch' array must be strings.`)\n        }\n        if (this.fetchParams) {\n          if (prop.startsWith('params')) {\n            prop = prop.replace('params', 'fetchParams')\n          }\n        }\n        this.$watch(prop, this.fetchData)\n      })\n\n      this.fetchData()\n    }\n  },\n  render() {\n    return this.$scopedSlots.default(this.scope)\n  }\n}\n"
  },
  {
    "path": "src/FeathersVuexFind.ts",
    "content": "import { randomString, getQueryInfo } from './utils'\nimport _get from 'lodash/get'\n\nexport default {\n  props: {\n    service: {\n      type: String,\n      required: true\n    },\n    query: {\n      type: Object,\n      default: null\n    },\n    queryWhen: {\n      type: [Boolean, Function],\n      default: true\n    },\n    // If a separate query is desired to fetch data, use fetchQuery\n    // The watchers will automatically be updated, so you don't have to write 'fetchQuery.propName'\n    fetchQuery: {\n      type: Object\n    },\n    /**\n     * Can be used in place of the `query` prop to provide more params. Only params.query is\n     * passed to the getter.\n     */\n    params: {\n      type: Object,\n      default: null\n    },\n    /**\n     * Can be used in place of the `fetchQuery` prop to provide more params. Only params.query is\n     * passed to the getter.\n     */\n    fetchParams: {\n      type: Object,\n      default: null\n    },\n    watch: {\n      type: [String, Array],\n      default() {\n        return []\n      }\n    },\n    local: {\n      type: Boolean,\n      default: false\n    },\n    editScope: {\n      type: Function,\n      default(scope) {\n        return scope\n      }\n    },\n    qid: {\n      type: String,\n      default() {\n        return randomString(10)\n      }\n    },\n    /**\n     * Set `temps` to true to include temporary records from the store.\n     */\n    temps: {\n      type: Boolean,\n      default: false\n    }\n  },\n  data: () => ({\n    isFindPending: false,\n    queryId: null,\n    pageId: null\n  }),\n  computed: {\n    items() {\n      let { query, service, $store, temps } = this\n      let { params } = this\n      \n      query = query || {}\n\n      params = params || { query, temps }\n\n      return $store.getters[`${service}/find`](params).data\n    },\n    pagination() {\n      return this.$store.state[this.service].pagination[this.qid]\n    },\n    queryInfo() {\n      if (this.pagination == null || this.queryId == null) return {}\n      return _get(this.pagination, this.queryId, {})\n    },\n    pageInfo() {\n      if (\n        this.pagination == null ||\n        this.queryId == null ||\n        this.pageId == null\n      )\n        return {}\n      return _get(this.pagination, [this.queryId, this.pageId], {})\n    },\n    scope() {\n      const { items, isFindPending, pagination, queryInfo, pageInfo } = this\n      const defaultScope = {\n        isFindPending,\n        pagination,\n        items,\n        queryInfo,\n        pageInfo\n      }\n\n      return this.editScope(defaultScope) || defaultScope\n    }\n  },\n  methods: {\n    findData() {\n      const query = this.fetchQuery || this.query\n      let params = this.fetchParams || this.params\n\n      if (\n        typeof this.queryWhen === 'function'\n          ? this.queryWhen(this.params || this.query)\n          : this.queryWhen\n      ) {\n        this.isFindPending = true\n\n        if (params || query) {\n          if (params) {\n            params = Object.assign({}, params, { qid: this.qid || 'default' })\n          } else {\n            params = { query, qid: this.qid || 'default' }\n          }\n\n          return this.$store\n            .dispatch(`${this.service}/find`, params)\n            .then(response => {\n              this.isFindPending = false\n              const { queryId, pageId } = getQueryInfo(params, response)\n              this.queryId = queryId\n              this.pageId = pageId\n            })\n        }\n      }\n    },\n    fetchData() {\n      if (!this.local) {\n        if (this.params || this.query) {\n          return this.findData()\n        } else {\n          // TODO: access debug boolean from the store config, somehow.\n          // eslint-disable-next-line no-console\n          console.log(\n            `No query and no id provided, so no data will be fetched.`\n          )\n        }\n      }\n    }\n  },\n  created() {\n    if (!this.$FeathersVuex) {\n      throw new Error(\n        `You must first Vue.use the FeathersVuex plugin before using the 'FeathersVuexFind' component.`\n      )\n    }\n    if (!this.$store.state[this.service]) {\n      throw new Error(\n        `The '${this.service}' plugin not registered with feathers-vuex`\n      )\n    }\n\n    const watch = Array.isArray(this.watch) ? this.watch : [this.watch]\n\n    if (this.fetchQuery || this.query || this.params) {\n      watch.forEach(prop => {\n        if (typeof prop !== 'string') {\n          throw new Error(`Values in the 'watch' array must be strings.`)\n        }\n        if (this.fetchQuery) {\n          if (prop.startsWith('query')) {\n            prop = prop.replace('query', 'fetchQuery')\n          }\n        }\n        if (this.fetchParams) {\n          if (prop.startsWith('params')) {\n            prop = prop.replace('params', 'fetchParams')\n          }\n        }\n        this.$watch(prop, this.fetchData)\n      })\n\n      this.fetchData()\n    }\n  },\n  render() {\n    return this.$scopedSlots.default(this.scope)\n  }\n}\n"
  },
  {
    "path": "src/FeathersVuexFormWrapper.ts",
    "content": "export default {\n  name: 'FeathersVuexFormWrapper',\n  model: {\n    prop: 'item',\n    event: 'update:item'\n  },\n  props: {\n    item: {\n      type: Object,\n      required: true\n    },\n    /**\n     * By default, when you call the `save` method, the cloned data will be\n     * committed to the store BEFORE saving tot he API server. Set\n     * `:eager=\"false\"` to only update the store with the API server response.\n     */\n    eager: {\n      type: Boolean,\n      default: true\n    },\n    // Set to false to prevent re-cloning if the object updates.\n    watch: {\n      type: Boolean,\n      default: true\n    }\n  },\n  data: () => ({\n    clone: null,\n    isDirty: false\n  }),\n  computed: {\n    isNew() {\n      return (this.item && this.item.__isTemp) || false\n    }\n  },\n  watch: {\n    item: {\n      handler: 'setup',\n      immediate: true,\n      deep: true\n    }\n  },\n  methods: {\n    setup() {\n      if (this.item) {\n        this.isDirty = false\n        // Unwatch the clone to prevent running watchers during reclone\n        if (this.unwatchClone) {\n          this.unwatchClone()\n        }\n\n        this.clone = this.item.clone()\n\n        // Watch the new clone.\n        this.unwatchClone = this.$watch('clone', {\n          handler: 'markAsDirty',\n          deep: true\n        })\n      }\n    },\n    save(params) {\n      if (this.eager) {\n        this.clone.commit()\n      }\n      return this.clone.save(params).then(response => {\n        this.$emit('saved', response)\n        if (this.isNew) {\n          this.$emit('saved-new', response)\n        }\n        return response\n      })\n    },\n    reset() {\n      this.clone.reset()\n      this.isDirty = false\n      this.$emit('reset', this.item)\n    },\n    async remove() {\n      await this.item.remove()\n      this.$emit('removed', this.item)\n      return this.item\n    },\n    markAsDirty() {\n      if (!this.isDirty) {\n        this.isDirty = true\n      }\n    }\n  },\n  render() {\n    const { clone, save, reset, remove, isDirty, isNew } = this\n    return this.$scopedSlots.default({\n      clone,\n      save,\n      reset,\n      remove,\n      isDirty,\n      isNew\n    })\n  }\n}\n"
  },
  {
    "path": "src/FeathersVuexGet.ts",
    "content": "/* eslint-disable @typescript-eslint/explicit-function-return-type */\nexport default {\n  props: {\n    /**\n     * The path of the service from which to pull records.\n     */\n    service: {\n      type: String,\n      required: true\n    },\n    /**\n     * Must match the `serverAlias` that was provided in the service's configuration.\n     */\n    serverAlias: {\n      type: String,\n      default: 'api'\n    },\n    /**\n     * By default, `query` is used to get data from the Vuex store AND the API request.\n     * If you specify a `fetchQuery`, then `query` will only be used for the Vuex store.\n     */\n    query: {\n      type: Object,\n      default: null\n    },\n    /**\n     * If a separate query is desired to fetch data, use fetchQuery\n     * The watchers are automatically updated, so you don't have to write 'fetchQuery.propName'\n     */\n    fetchQuery: {\n      type: Object\n    },\n    /**\n     * Can be used in place of the `query` prop to provide more params. Only params.query is\n     * passed to the getter.\n     */\n    params: {\n      type: Object,\n      default: null\n    },\n    /**\n     * Can be used in place of the `fetchQuery` prop to provide more params. Only params.query is\n     * passed to the getter.\n     */\n    fetchParams: {\n      type: Object,\n      default: null\n    },\n    /**\n     * When `queryWhen` evaluates to false, no API request will be made.\n     */\n    queryWhen: {\n      type: [Boolean, Function],\n      default: true\n    },\n    // For get requests\n    id: {\n      type: [Number, String],\n      default: null\n    },\n    /**\n     * Specify which properties in the query to watch and re-trigger API requests.\n     */\n    watch: {\n      type: [String, Array],\n      default() {\n        return []\n      }\n    },\n    /**\n     * Set `local` to true to only requests from the Vuex data store and not make API requests.\n     */\n    local: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * This function is called by the getter and allows you to intercept the `item` in the\n     * response to pass it into the parent component's scope.  It's a dirty little cheater\n     * function (because it's called from a getter), but it actually works well  ;)\n     */\n    editScope: {\n      type: Function,\n      default(scope) {\n        return scope\n      }\n    }\n  },\n  data: () => ({\n    isFindPending: false,\n    isGetPending: false\n  }),\n  computed: {\n    item() {\n      const getArgs = this.getArgs(this.query)\n      if (this.id) {\n        if (getArgs.length === 1) {\n          return this.$store.getters[`${this.service}/get`](this.id) || null\n        } else {\n          const args = [this.id]\n          const query = getArgs[1].query\n          if (query) {\n            args.push(query)\n          }\n          return this.$store.getters[`${this.service}/get`](args) || null\n        }\n      } else {\n        return null\n      }\n    },\n    scope() {\n      const { item, isGetPending } = this\n      const defaultScope = { item, isGetPending }\n\n      return this.editScope(defaultScope) || defaultScope\n    }\n  },\n  methods: {\n    getArgs(queryToUse) {\n      const query = queryToUse || this.fetchQuery || this.query\n      const params = this.fetchParams || this.params\n\n      const getArgs = [this.id]\n      if (params) {\n        getArgs.push(params)\n      } else if (query && Object.keys(query).length > 0) {\n        getArgs.push({ query })\n      }\n      return getArgs\n    },\n    getData() {\n      const getArgs = this.getArgs()\n\n      if (\n        typeof this.queryWhen === 'function'\n          ? this.queryWhen(...getArgs)\n          : this.queryWhen\n      ) {\n        this.isGetPending = true\n\n        if (this.id) {\n          return this.$store\n            .dispatch(\n              `${this.service}/get`,\n              getArgs.length === 1 ? this.id : getArgs\n            )\n            .then(response => {\n              this.isGetPending = false\n              return response\n            })\n        }\n      }\n    },\n    fetchData() {\n      if (this.local || this.id === 'new') {\n        return\n      } else if (\n        this.fetchQuery ||\n        this.query ||\n        this.params ||\n        (this.id !== null && this.id !== undefined)\n      ) {\n        return this.getData()\n      } else {\n        // eslint-disable-next-line no-console\n        console.log(`No query and no id provided, so no data will be fetched.`)\n      }\n    }\n  },\n  created() {\n    if (!this.$FeathersVuex) {\n      throw new Error(\n        `You must first Vue.use the FeathersVuex plugin before using the 'FeathersVuexGet' component.`\n      )\n    }\n    if (!this.$store.state[this.service]) {\n      throw new Error(\n        `The '${this.service}' plugin is not registered with feathers-vuex`\n      )\n    }\n\n    const watch = Array.isArray(this.watch) ? this.watch : [this.watch]\n\n    if (\n      this.fetchQuery ||\n      this.query ||\n      this.params ||\n      (this.id !== null && this.id !== undefined)\n    ) {\n      watch.forEach(prop => {\n        if (typeof prop !== 'string') {\n          throw new Error(`Values in the 'watch' array must be strings.`)\n        }\n        if (this.fetchQuery) {\n          if (prop.startsWith('query')) {\n            prop.replace('query', 'fetchQuery')\n          }\n        }\n        this.$watch(prop, this.fetchData)\n      })\n\n      this.fetchData()\n    }\n  },\n  render() {\n    return this.$scopedSlots.default(this.scope)\n  }\n}\n"
  },
  {
    "path": "src/FeathersVuexInputWrapper.ts",
    "content": "import _debounce from 'lodash/debounce'\n\nexport default {\n  name: 'FeathersVuexInputWrapper',\n  props: {\n    item: {\n      type: Object,\n      required: true\n    },\n    prop: {\n      type: String,\n      required: true\n    },\n    debounce: {\n      type: Number,\n      default: 0\n    }\n  },\n  data: () => ({\n    clone: null\n  }),\n  computed: {\n    current() {\n      return this.clone || this.item\n    }\n  },\n  watch: {\n    debounce: {\n      handler(wait) {\n        this.debouncedHandler = _debounce(this.handler, wait)\n      },\n      immediate: true\n    }\n  },\n  methods: {\n    createClone(e) {\n      this.clone = this.item.clone()\n    },\n    cleanup() {\n      this.$nextTick(() => {\n        this.clone = null\n      })\n    },\n    handler(e, callback) {\n      if (!this.clone) {\n        this.createClone()\n      }\n      const maybePromise = callback({\n        event: e,\n        clone: this.clone,\n        prop: this.prop,\n        data: { [this.prop]: this.clone[this.prop] }\n      })\n      if (maybePromise && maybePromise.then) {\n        maybePromise.then(this.cleanup)\n      } else {\n        this.cleanup()\n      }\n    }\n  },\n  render() {\n    const { current, prop, createClone } = this\n    const handler = this.debounce ? this.debouncedHandler : this.handler\n\n    return this.$scopedSlots.default({ current, prop, createClone, handler })\n  }\n}\n"
  },
  {
    "path": "src/FeathersVuexPagination.ts",
    "content": "import {\n  h,\n  computed,\n  watch\n} from '@vue/composition-api'\n\nexport default {\n  name: 'FeathersVuexPagination',\n  props: {\n    /**\n     * An object containing { $limit, and $skip }\n     */\n    value: {\n      type: Object,\n      // eslint-disable-next-line @typescript-eslint/explicit-function-return-type\n      default: () => null\n    },\n    /**\n     * The `latestQuery` object from the useFind data\n     */\n    latestQuery: {\n      type: Object,\n      // eslint-disable-next-line @typescript-eslint/explicit-function-return-type\n      default: () => null\n    }\n  },\n  // eslint-disable-next-line\n  setup(props, context) {\n    /**\n     * The number of pages available based on the results returned in the latestQuery prop.\n     */\n    const pageCount = computed(() => {\n      const q = props.latestQuery\n      if (q && q.response) {\n        return Math.ceil(q.response.total / props.value.$limit)\n      } else {\n        return 1\n      }\n    })\n\n    /**\n     * The `currentPage` is calculated based on the $limit and $skip values provided in\n     * the v-model object.\n     *\n     * Setting `currentPage` to a new numeric value will emit the appropriate values out\n     * the v-model. (using the default `input` event)\n     */\n    const currentPage = computed({\n      set(pageNumber: number) {\n        if (pageNumber < 1) {\n          pageNumber = 1\n        } else if (pageNumber > pageCount.value) {\n          pageNumber = pageCount.value\n        }\n        const $limit = props.value.$limit\n        const $skip = $limit * (pageNumber - 1)\n\n        context.emit('input', { $limit, $skip })\n      },\n      get() {\n        const params = props.value\n        if (params) {\n          return pageCount.value === 0 ? 0 : params.$skip / params.$limit + 1\n        } else {\n          return 1\n        }\n      }\n    })\n\n    watch(\n      () => pageCount.value,\n      () => {\n        const lq = props.latestQuery\n        if (lq && lq.response && currentPage.value > pageCount.value) {\n          currentPage.value = pageCount.value\n        }\n      }\n    )\n\n    const canPrev = computed(() => {\n      return currentPage.value - 1 > 0\n    })\n    const canNext = computed(() => {\n      return currentPage.value < pageCount.value\n    })\n\n    function toStart(): void {\n      currentPage.value = 1\n    }\n    function toEnd(): void {\n      currentPage.value = pageCount.value\n    }\n    function toPage(pageNumber): void {\n      currentPage.value = pageNumber\n    }\n\n    function next(): void {\n      currentPage.value++\n    }\n    function prev(): void {\n      currentPage.value--\n    }\n\n    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type\n    return () => {\n      if (context.slots.default) {\n        return context.slots.default({\n          currentPage: currentPage.value,\n          pageCount: pageCount.value,\n          canPrev: canPrev.value,\n          canNext: canNext.value,\n          toStart,\n          toEnd,\n          toPage,\n          prev,\n          next\n        })\n      } else {\n        return h('div', {}, [\n          h('p', `FeathersVuexPagination uses the default slot:`),\n          h('p', `#default=\"{ currentPage, pageCount }\"`)\n        ])\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/auth-module/auth-module.actions.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport fastCopy from 'fast-copy'\nimport { globalModels as models } from '../service-module/global-models'\nimport { getNameFromPath } from '../utils'\n\nexport default function makeAuthActions(feathersClient) {\n  return {\n    authenticate(store, dataOrArray) {\n      const { commit, state, dispatch } = store\n      const [data, params] = Array.isArray(dataOrArray)\n        ? dataOrArray\n        : [dataOrArray]\n\n      commit('setAuthenticatePending')\n      if (state.errorOnAuthenticate) {\n        commit('clearAuthenticateError')\n      }\n      return feathersClient\n        .authenticate(data, params)\n        .then(response => {\n          return dispatch('responseHandler', response)\n        })\n        .catch(error => {\n          commit('setAuthenticateError', error)\n          commit('unsetAuthenticatePending')\n          return Promise.reject(error)\n        })\n    },\n\n    responseHandler({ commit, state, dispatch }, response) {\n      if (response.accessToken) {\n        commit('setAccessToken', response.accessToken)\n        commit('setPayload', response)\n\n        // Handle when user is returned in the authenticate response\n        let user = response[state.responseEntityField]\n\n        if (user) {\n          if (state.serverAlias && state.userService) {\n            const Model = Object.keys(models[state.serverAlias])\n              .map(modelName => models[state.serverAlias][modelName])\n              .find(model => getNameFromPath(model.servicePath) === getNameFromPath(state.userService))\n            if (Model) {\n              // Copy user object to avoid setupInstance modifying payload state\n              user = new Model(fastCopy(user))\n            }\n          }\n          commit('setUser', user)\n          commit('unsetAuthenticatePending')\n        } else if (\n          state.userService &&\n          response.hasOwnProperty(state.entityIdField)\n        ) {\n          return dispatch(\n            'populateUser',\n            response[state.entityIdField]\n          ).then(() => {\n            commit('unsetAuthenticatePending')\n            return response\n          })\n        }\n        return response\n\n        // If there was not an accessToken in the response, allow the response to pass through to handle two-factor-auth\n      } else {\n        return response\n      }\n    },\n\n    populateUser({ commit, state, dispatch }, userId) {\n      return dispatch(`${state.userService}/get`, userId, { root: true }).then(\n        user => {\n          commit('setUser', user)\n          return user\n        }\n      )\n    },\n\n    logout({ commit }) {\n      commit('setLogoutPending')\n      return feathersClient\n        .logout()\n        .then(response => {\n          commit('logout')\n          commit('unsetLogoutPending')\n          return response\n        })\n        .catch(error => {\n          return Promise.reject(error)\n        })\n    }\n  }\n}\n"
  },
  {
    "path": "src/auth-module/auth-module.getters.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nexport default function makeAuthGetters({ userService }) {\n  const getters = {}\n\n  if (userService) {\n    Object.assign(getters, {\n      // A reactive user object\n      user(state, getters, rootState) {\n        if (!state.user) {\n          return null\n        }\n        const { idField } = rootState[userService]\n        const userId = state.user[idField]\n        return rootState[userService].keyedById[userId] || null\n      },\n      isAuthenticated(state, getters) {\n        return !!getters.user\n      }\n    })\n  }\n\n  return getters\n}\n"
  },
  {
    "path": "src/auth-module/auth-module.mutations.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { serializeError } from 'serialize-error'\n\nexport default function makeAuthMutations() {\n  return {\n    setAccessToken(state, payload) {\n      state.accessToken = payload\n    },\n    setPayload(state, payload) {\n      state.payload = payload\n    },\n    setUser(state, payload) {\n      state.user = payload\n    },\n\n    setAuthenticatePending(state) {\n      state.isAuthenticatePending = true\n    },\n    unsetAuthenticatePending(state) {\n      state.isAuthenticatePending = false\n    },\n    setLogoutPending(state) {\n      state.isLogoutPending = true\n    },\n    unsetLogoutPending(state) {\n      state.isLogoutPending = false\n    },\n\n    setAuthenticateError(state, error) {\n      state.errorOnAuthenticate = Object.assign({}, serializeError(error))\n    },\n    clearAuthenticateError(state) {\n      state.errorOnAuthenticate = null\n    },\n    setLogoutError(state, error) {\n      state.errorOnLogout = Object.assign({}, serializeError(error))\n    },\n    clearLogoutError(state) {\n      state.errorOnLogout = null\n    },\n\n    logout(state) {\n      state.payload = null\n      state.accessToken = null\n      if (state.user) {\n        state.user = null\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/auth-module/auth-module.state.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { AuthState } from './types'\n\nexport default function setupAuthState({\n  userService,\n  serverAlias,\n  responseEntityField = 'user',\n  entityIdField = 'userId'\n}) {\n  const state: AuthState = {\n    accessToken: null, // The JWT\n    payload: null, // The JWT payload\n    entityIdField,\n    responseEntityField,\n\n    isAuthenticatePending: false,\n    isLogoutPending: false,\n\n    errorOnAuthenticate: null,\n    errorOnLogout: null,\n    user: null, // For a reactive user object, use the `user` getter.\n    userService: null,\n    serverAlias\n  }\n  // If a userService string was passed, add a user attribute\n  if (userService) {\n    Object.assign(state, { userService })\n  }\n  return state\n}\n"
  },
  {
    "path": "src/auth-module/make-auth-plugin.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { FeathersVuexOptions } from '../service-module/types'\nimport setupState from './auth-module.state'\nimport setupGetters from './auth-module.getters'\nimport setupMutations from './auth-module.mutations'\nimport setupActions from './auth-module.actions'\n\nconst defaults = {\n  namespace: 'auth',\n  userService: '', // Set this to automatically populate the user (using an additional request) on login success.\n  serverAlias: 'api',\n  debug: false,\n  state: {}, // for custom state\n  getters: {}, // for custom getters\n  mutations: {}, // for custom mutations\n  actions: {} // for custom actions\n}\n\nexport default function authPluginInit(\n  feathersClient,\n  globalOptions: FeathersVuexOptions\n) {\n  if (!feathersClient || !feathersClient.service) {\n    throw new Error('You must pass a Feathers Client instance to feathers-vuex')\n  }\n\n  return function makeAuthPlugin(options) {\n    options = Object.assign(\n      {},\n      defaults,\n      { serverAlias: globalOptions.serverAlias },\n      options\n    )\n\n    if (!feathersClient.authenticate) {\n      throw new Error(\n        'You must register the @feathersjs/authentication-client plugin before using the feathers-vuex auth module'\n      )\n    }\n    if (options.debug && options.userService && !options.serverAlias) {\n      console.warn(\n        'A userService was provided, but no serverAlias was provided. To make sure the user record is an instance of the User model, a serverAlias must be provided.'\n      )\n    }\n\n    const defaultState = setupState(options)\n    const defaultGetters = setupGetters(options)\n    const defaultMutations = setupMutations()\n    const defaultActions = setupActions(feathersClient)\n\n    return function setupStore(store) {\n      const { namespace } = options\n\n      store.registerModule(namespace, {\n        namespaced: true,\n        state: Object.assign({}, defaultState, options.state),\n        getters: Object.assign({}, defaultGetters, options.getters),\n        mutations: Object.assign({}, defaultMutations, options.mutations),\n        actions: Object.assign({}, defaultActions, options.actions)\n      })\n    }\n  }\n}\n"
  },
  {
    "path": "src/auth-module/types.ts",
    "content": "export interface AuthState {\n  accessToken: string\n  payload: {}\n  entityIdField: string\n  responseEntityField: string\n\n  isAuthenticatePending: boolean\n  isLogoutPending: boolean\n\n  errorOnAuthenticate: Error\n  errorOnLogout: Error\n  user: {}\n  userService: string\n  serverAlias: string\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport FeathersVuexFind from './FeathersVuexFind'\nimport FeathersVuexGet from './FeathersVuexGet'\nimport FeathersVuexFormWrapper from './FeathersVuexFormWrapper'\nimport FeathersVuexInputWrapper from './FeathersVuexInputWrapper'\nimport FeathersVuexPagination from './FeathersVuexPagination'\nimport makeFindMixin from './make-find-mixin'\nimport makeGetMixin from './make-get-mixin'\nimport { globalModels as models } from './service-module/global-models'\nimport { clients, addClient } from './service-module/global-clients'\nimport makeBaseModel from './service-module/make-base-model'\nimport prepareMakeServicePlugin from './service-module/make-service-plugin'\nimport prepareMakeAuthPlugin from './auth-module/make-auth-plugin'\nimport useFind from './useFind'\nimport useGet from './useGet'\n\nimport {\n  FeathersVuexOptions,\n  HandleEvents,\n  Model,\n  ModelStatic,\n  ModelSetupContext,\n  Id,\n  FeathersVuexStoreState,\n  FeathersVuexGlobalModels,\n  GlobalModels\n} from './service-module/types'\nimport { initAuth, hydrateApi } from './utils'\nimport { FeathersVuex } from './vue-plugin/vue-plugin'\nimport { ServiceState } from './service-module/service-module.state'\nimport { AuthState } from './auth-module/types'\nconst events = ['created', 'patched', 'updated', 'removed']\n\nconst defaults: FeathersVuexOptions = {\n  autoRemove: false, // Automatically remove records missing from responses (only use with feathers-rest)\n  addOnUpsert: false, // Add new records pushed by 'updated/patched' socketio events into store, instead of discarding them\n  enableEvents: true, // Listens to socket.io events when available\n  idField: 'id', // The field in each record that will contain the id\n  tempIdField: '__id',\n  debug: false, // Set to true to enable logging messages.\n  keepCopiesInStore: false, // Set to true to store cloned copies in the store instead of on the Model.\n  nameStyle: 'short', // Determines the source of the module name. 'short', 'path', or 'explicit'\n  paramsForServer: ['$populateParams'], // Custom query operators that are ignored in the find getter, but will pass through to the server. $populateParams is for https://feathers-graph-populate.netlify.app/\n  preferUpdate: false, // When true, calling model.save() will do an update instead of a patch.\n  replaceItems: false, // Instad of merging in changes in the store, replace the entire record.\n  serverAlias: 'api',\n  handleEvents: {} as HandleEvents,\n  skipRequestIfExists: false, // For get action, if the record already exists in store, skip the remote request\n  whitelist: [] // Custom query operators that will be allowed in the find getter.\n}\n\nexport default function feathersVuex(feathers, options: FeathersVuexOptions) {\n  if (!feathers || !feathers.service) {\n    throw new Error(\n      'The first argument to feathersVuex must be a feathers client.'\n    )\n  }\n\n  // Setup the event handlers. By default they just return the value of `options.enableEvents`\n  defaults.handleEvents = events.reduce((obj, eventName) => {\n    obj[eventName] = () => options.enableEvents || true\n    return obj\n  }, {} as HandleEvents)\n\n  options = Object.assign({}, defaults, options)\n\n  if (!options.serverAlias) {\n    throw new Error(\n      `You must provide a 'serverAlias' in the options to feathersVuex`\n    )\n  }\n\n  addClient({ client: feathers, serverAlias: options.serverAlias })\n\n  const BaseModel = makeBaseModel(options)\n  const makeServicePlugin = prepareMakeServicePlugin(options)\n  const makeAuthPlugin = prepareMakeAuthPlugin(feathers, options)\n\n  return {\n    makeServicePlugin,\n    BaseModel,\n    makeAuthPlugin,\n    FeathersVuex,\n    models: models as GlobalModels,\n    clients\n  }\n}\n\nexport {\n  initAuth,\n  hydrateApi,\n  FeathersVuexFind,\n  FeathersVuexGet,\n  FeathersVuexFormWrapper,\n  FeathersVuexInputWrapper,\n  FeathersVuexPagination,\n  FeathersVuex,\n  makeFindMixin,\n  makeGetMixin,\n  models,\n  clients,\n  useFind,\n  useGet,\n  AuthState,\n  Id,\n  Model,\n  ModelStatic,\n  ModelSetupContext,\n  ServiceState,\n  FeathersVuexGlobalModels,\n  FeathersVuexStoreState\n}\n"
  },
  {
    "path": "src/make-find-mixin.ts",
    "content": "/*\neslint\nno-console: 0,\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\n\nimport debounce from 'lodash/debounce'\nimport _get from 'lodash/get'\nimport {\n  getItemsFromQueryInfo,\n  getQueryInfo,\n  getServiceCapitalization,\n  getServicePrefix\n} from './utils'\n\nexport default function makeFindMixin(options) {\n  const {\n    service,\n    params,\n    fetchParams,\n    queryWhen = () => true,\n    local = false,\n    qid = 'default',\n    items,\n    debug\n  } = options\n  let { name, watch = [] } = options\n\n  if (typeof watch === 'string') {\n    watch = [watch]\n  } else if (typeof watch === 'boolean' && watch) {\n    watch = ['params']\n  }\n\n  if (\n    !service ||\n    (typeof service !== 'string' && typeof service !== 'function')\n  ) {\n    throw new Error(\n      `The 'service' option is required in the FeathersVuex make-find-mixin and must be a string.`\n    )\n  }\n  if (typeof service === 'function' && !name) {\n    name = 'service'\n  }\n\n  const nameToUse = (name || service).replace(/-/g, '_')\n  const prefix = getServicePrefix(nameToUse)\n  const capitalized = getServiceCapitalization(nameToUse)\n  const SERVICE_NAME = `${prefix}ServiceName`\n  let ITEMS = items || prefix\n  if (typeof service === 'function' && name === 'service' && !items) {\n    ITEMS = 'items'\n  }\n  const ITEMS_FETCHED = `${ITEMS}Fetched`\n  const IS_FIND_PENDING = `isFind${capitalized}Pending`\n  const PARAMS = `${prefix}Params`\n  const FETCH_PARAMS = `${prefix}FetchParams`\n  const WATCH = `${prefix}Watch`\n  const QUERY_WHEN = `${prefix}QueryWhen`\n  const ERROR = `${prefix}Error`\n  const FIND_ACTION = `find${capitalized}`\n  const FIND_GETTER = `find${capitalized}InStore`\n  const HAVE_ITEMS_BEEN_REQUESTED_ONCE = `have${capitalized}BeenRequestedOnce`\n  const HAVE_ITEMS_LOADED_ONCE = `have${capitalized}LoadedOnce`\n  const PAGINATION = `${prefix}PaginationData`\n  const MOST_RECENT_QUERY = `${prefix}LatestQuery`\n  const LOCAL = `${prefix}Local`\n  const QID = `${prefix}Qid`\n  const data = {\n    [IS_FIND_PENDING]: false,\n    [HAVE_ITEMS_BEEN_REQUESTED_ONCE]: false,\n    [HAVE_ITEMS_LOADED_ONCE]: false,\n    [WATCH]: watch,\n    [QID]: qid,\n    [MOST_RECENT_QUERY]: null,\n    [ERROR]: null\n  }\n  // Should only be used with actual fetching API calls.\n  const getParams = ({ providedParams, params, fetchParams }) => {\n    if (providedParams) {\n      return providedParams\n    } else {\n      // Returning null fetchParams allows the query to be skipped.\n      return fetchParams || fetchParams === null ? fetchParams : params\n    }\n  }\n\n  const mixin = {\n    data() {\n      return data\n    },\n    computed: {\n      [PAGINATION]() {\n        return this.$store.state[this[SERVICE_NAME]].pagination\n      },\n      [ITEMS]() {\n        const serviceName = this[SERVICE_NAME]\n        const serviceState = this.$store.state[serviceName]\n\n        // If both queries are provided, we're not using fall-through pagination.\n        if (\n          (this[FETCH_PARAMS] && this[PARAMS]) ||\n          (this[PARAMS] && !this[PARAMS].paginate)\n        ) {\n          return this.$store.getters[`${serviceName}/find`](this[PARAMS]).data\n        }\n\n        // User can pass `paginate: true` to force pagination.\n        const params = this[PARAMS]\n        // Check for pagination data for this query.\n        if (params) {\n          const { defaultSkip, defaultLimit } = serviceState.pagination\n          const skip = params.query.$skip || defaultSkip\n          const limit = params.query.$limit || defaultLimit\n          const pagination = this[PAGINATION][params.qid || this[QID]] || {}\n          const response = skip != null && limit != null ? { limit, skip } : {}\n          const queryInfo = getQueryInfo(params, response)\n          const items = getItemsFromQueryInfo(\n            pagination,\n            queryInfo,\n            serviceState.keyedById\n          )\n\n          if (items && items.length) {\n            return items\n          }\n        }\n\n        return []\n      },\n      // Queries the Vuex store with the exact same query that was sent to the API server.\n      [ITEMS_FETCHED]() {\n        if (this[FETCH_PARAMS]) {\n          return this[FIND_GETTER](this[FETCH_PARAMS]).data\n        } else {\n          return this[ITEMS]\n        }\n      },\n      // Exposes `findItemsInStore\n      [FIND_GETTER]() {\n        return params => {\n          const serviceName = this[SERVICE_NAME]\n          return this.$store.getters[`${serviceName}/find`](params)\n        }\n      }\n    },\n    methods: {\n      [`${FIND_ACTION}DebouncedProxy`](params) {\n        const paramsToUse = getParams({\n          providedParams: params,\n          params: this[PARAMS],\n          fetchParams: this[FETCH_PARAMS]\n        })\n        if (paramsToUse && paramsToUse.debounce) {\n          const cachedDebounceFunction = this[`${FIND_ACTION}Debounced`]\n          const mostRecentTime = this[`${FIND_ACTION}MostRecentDebounceTime`]\n\n          if (\n            !cachedDebounceFunction ||\n            mostRecentTime != paramsToUse.debounce\n          ) {\n            this[`${FIND_ACTION}MostRecentDebounceTime`] = paramsToUse.debounce\n            this[`${FIND_ACTION}Debounced`] = debounce(\n              this[FIND_ACTION],\n              paramsToUse.debounce\n            )\n          }\n          return this[`${FIND_ACTION}Debounced`](paramsToUse)\n        } else {\n          return this[FIND_ACTION](paramsToUse)\n        }\n      },\n      [FIND_ACTION](params) {\n        const serviceName = this[SERVICE_NAME]\n        const paramsToUse = getParams({\n          providedParams: params,\n          params: this[PARAMS],\n          fetchParams: this[FETCH_PARAMS]\n        })\n\n        const shouldExecuteQuery =\n          typeof this[QUERY_WHEN] === 'function'\n            ? this[QUERY_WHEN](paramsToUse)\n            : this[QUERY_WHEN]\n\n        if (shouldExecuteQuery) {\n          if (paramsToUse) {\n            // Set the qid.\n            paramsToUse.query = paramsToUse.query || {}\n            paramsToUse.qid = paramsToUse.qid || this[QID]\n            this[QID] = paramsToUse.qid\n\n            this[IS_FIND_PENDING] = true\n            this[HAVE_ITEMS_BEEN_REQUESTED_ONCE] = true\n\n            return this.$store\n              .dispatch(`${serviceName}/find`, paramsToUse)\n              .then(response => {\n                // To prevent thrashing, only clear ERROR on response, not on initial request.\n                this[ERROR] = null\n\n                this[HAVE_ITEMS_LOADED_ONCE] = true\n                const queryInfo = getQueryInfo(paramsToUse, response)\n                queryInfo.response = response\n                queryInfo.isOutdated = false\n\n                this[MOST_RECENT_QUERY] = queryInfo\n                this[IS_FIND_PENDING] = false\n                return response\n              })\n              .catch(error => {\n                this[ERROR] = error\n                return error\n              })\n          }\n        } else {\n          if (this[MOST_RECENT_QUERY]) {\n            this[MOST_RECENT_QUERY].isOutdated = true\n          }\n        }\n      },\n      getPaginationForQuery(params = {}) {\n        const pagination = this[PAGINATION]\n        const { qid, queryId, pageId } = getQueryInfo(params)\n        const queryInfo = _get(pagination, [qid, queryId], {})\n        const pageInfo = _get(pagination, [qid, queryId, pageId], {})\n\n        return { queryInfo, pageInfo }\n      }\n    },\n    // add the created hook only if the local option is falsy\n    ...(!local && {\n      created() {\n        if (debug) {\n          console.log(\n            `running 'created' hook in makeFindMixin for service \"${service}\" (using name ${nameToUse}\")`\n          )\n          console.log(PARAMS, this[PARAMS])\n          console.log(FETCH_PARAMS, this[FETCH_PARAMS])\n        }\n\n        const pType = Object.getPrototypeOf(this)\n\n        if (\n          pType.hasOwnProperty(PARAMS) ||\n          pType.hasOwnProperty(FETCH_PARAMS)\n        ) {\n          watch.forEach(prop => {\n            if (typeof prop !== 'string') {\n              throw new Error(`Values in the 'watch' array must be strings.`)\n            }\n            prop = prop.replace('params', PARAMS)\n\n            if (pType.hasOwnProperty(FETCH_PARAMS)) {\n              if (prop.startsWith(PARAMS)) {\n                prop = prop.replace(PARAMS, FETCH_PARAMS)\n              }\n            }\n            this.$watch(prop, function() {\n              // If the request is going to be debounced, set IS_FIND_PENDING to true.\n              // Without this, there's not a way to show a loading indicator during the debounce timeout.\n              const paramsToUse = getParams({\n                providedParams: null,\n                params: this[PARAMS],\n                fetchParams: this[FETCH_PARAMS]\n              })\n              if (paramsToUse && paramsToUse.debounce) {\n                this[IS_FIND_PENDING] = true\n              }\n              return this[`${FIND_ACTION}DebouncedProxy`]()\n            })\n          })\n\n          return this[FIND_ACTION]()\n        } else {\n          // TODO: Add this message to the logging:\n          //       \"Pass { local: true } to disable this warning and only do local queries.\"\n          console.log(\n            `No \"${PARAMS}\" or \"${FETCH_PARAMS}\" attribute was found in the makeFindMixin for the \"${service}\" service (using name \"${nameToUse}\").  No queries will be made.`\n          )\n        }\n      }\n    })\n  }\n\n  function hasSomeAttribute(vm, ...attributes) {\n    return attributes.some(a => {\n      return vm.hasOwnProperty(a) || Object.getPrototypeOf(vm).hasOwnProperty(a)\n    })\n  }\n\n  function setupAttribute(\n    NAME,\n    value,\n    computedOrMethods = 'computed',\n    returnTheValue = false\n  ) {\n    if (typeof value === 'boolean') {\n      data[NAME] = !!value\n    } else if (typeof value === 'string') {\n      mixin.computed[NAME] = function() {\n        // If the specified computed prop wasn't found, display an error.\n        if (!returnTheValue) {\n          if (!hasSomeAttribute(this, value, NAME)) {\n            throw new Error(\n              `Value for ${NAME} was not found on the component at '${value}'.`\n            )\n          }\n        }\n        return returnTheValue ? value : this[value]\n      }\n    } else if (typeof value === 'function') {\n      mixin[computedOrMethods][NAME] = value\n    }\n  }\n\n  setupAttribute(SERVICE_NAME, service, 'computed', true)\n  setupAttribute(PARAMS, params)\n  setupAttribute(FETCH_PARAMS, fetchParams)\n  setupAttribute(QUERY_WHEN, queryWhen, 'computed')\n  setupAttribute(LOCAL, local)\n\n  return mixin\n}\n"
  },
  {
    "path": "src/make-get-mixin.ts",
    "content": "/*\neslint\nno-console: 0,\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport inflection from 'inflection'\n\nexport default function makeFindMixin(options) {\n  const {\n    service,\n    params,\n    fetchParams,\n    queryWhen,\n    id,\n    local = false,\n    qid = 'default',\n    item,\n    debug\n  } = options\n  let { name, watch = [] } = options\n\n  if (typeof watch === 'string') {\n    watch = [watch]\n  } else if (typeof watch === 'boolean' && watch) {\n    watch = ['query']\n  }\n\n  if (\n    !service ||\n    (typeof service !== 'string' && typeof service !== 'function')\n  ) {\n    throw new Error(\n      `The 'service' option is required in the FeathersVuex make-find-mixin and must be a string.`\n    )\n  }\n  if (typeof service === 'function' && !name) {\n    name = 'service'\n  }\n\n  const nameToUse = (name || service).replace(/-/g, '_')\n  const singularized = inflection.singularize(nameToUse)\n  const prefix = inflection.camelize(singularized, true)\n  const capitalized = prefix.charAt(0).toUpperCase() + prefix.slice(1)\n  const SERVICE_NAME = `${prefix}ServiceName`\n  let ITEM = item || prefix\n  if (typeof service === 'function' && name === 'service' && !item) {\n    ITEM = 'item'\n  }\n  const IS_GET_PENDING = `isGet${capitalized}Pending`\n  const PARAMS = `${prefix}Params`\n  const FETCH_PARAMS = `${prefix}FetchParams`\n  const WATCH = `${prefix}Watch`\n  const QUERY_WHEN = `${prefix}QueryWhen`\n  const ERROR = `${prefix}Error`\n  const GET_ACTION = `get${capitalized}`\n  const GET_GETTER = `get${capitalized}FromStore`\n  const HAS_ITEM_BEEN_REQUESTED_ONCE = `has${capitalized}BeenRequestedOnce`\n  const HAS_ITEM_LOADED_ONCE = `has${capitalized}LoadedOnce`\n  const LOCAL = `${prefix}Local`\n  const QID = `${prefix}Qid`\n  const ID = `${prefix}Id`\n  const data = {\n    [IS_GET_PENDING]: false,\n    [HAS_ITEM_BEEN_REQUESTED_ONCE]: false,\n    [HAS_ITEM_LOADED_ONCE]: false,\n    [WATCH]: watch,\n    [QID]: qid,\n    [ERROR]: null\n  }\n\n  const mixin = {\n    data() {\n      return data\n    },\n    computed: {\n      [ITEM]() {\n        return this[ID]\n          ? this.$store.getters[`${this[SERVICE_NAME]}/get`](this[ID])\n          : null\n      },\n      [QUERY_WHEN]() {\n        return true\n      },\n      // Exposes `get<Item>FromStore`\n      [GET_GETTER]() {\n        return id => {\n          const serviceName = this[SERVICE_NAME]\n          return this.$store.getters[`${serviceName}/get`](id)\n        }\n      }\n    },\n    methods: {\n      [GET_ACTION](id, params) {\n        const paramsToUse = params || this[FETCH_PARAMS] || this[PARAMS]\n        const idToUse = id || this[ID]\n\n        if (this[QUERY_WHEN]) {\n          this[IS_GET_PENDING] = true\n          this[HAS_ITEM_BEEN_REQUESTED_ONCE] = true\n\n          if (idToUse != null) {\n            return this.$store\n              .dispatch(`${this[SERVICE_NAME]}/get`, [idToUse, paramsToUse])\n              .then(response => {\n                // To prevent thrashing, only clear ERROR on response, not on initial request.\n                this[ERROR] = null\n\n                this[HAS_ITEM_LOADED_ONCE] = true\n                this[IS_GET_PENDING] = false\n                return response\n              })\n              .catch(error => {\n                this[ERROR] = error\n                return error\n              })\n          }\n        }\n      }\n    },\n    // add the created lifecycle hook only if local option is falsy\n    ...(!local && {\n      created() {\n        if (debug) {\n          console.log(\n            `running 'created' hook in makeGetMixin for service \"${service}\" (using name ${nameToUse}\")`\n          )\n          console.log(ID, this[ID])\n          console.log(PARAMS, this[PARAMS])\n          console.log(FETCH_PARAMS, this[FETCH_PARAMS])\n        }\n\n        const pType = Object.getPrototypeOf(this)\n\n        if (\n          this.hasOwnProperty(ID) ||\n          pType.hasOwnProperty(ID) ||\n          pType.hasOwnProperty(PARAMS) ||\n          pType.hasOwnProperty(FETCH_PARAMS)\n        ) {\n          if (!watch.includes(ID)) {\n            watch.push(ID)\n          }\n\n          watch.forEach(prop => {\n            if (typeof prop !== 'string') {\n              throw new Error(`Values in the 'watch' array must be strings.`)\n            }\n            prop = prop.replace('query', PARAMS)\n\n            if (pType.hasOwnProperty(FETCH_PARAMS)) {\n              if (prop.startsWith(PARAMS)) {\n                prop.replace(PARAMS, FETCH_PARAMS)\n              }\n            }\n            this.$watch(prop, function() {\n              return this[GET_ACTION]()\n            })\n          })\n\n          return this[GET_ACTION]()\n        } else {\n          console.log(\n            `No \"${ID}\", \"${PARAMS}\" or \"${FETCH_PARAMS}\" attribute was found in the makeGetMixin for the \"${service}\" service (using name \"${nameToUse}\").  No queries will be made.`\n          )\n        }\n      }\n    })\n  }\n\n  function hasSomeAttribute(vm, ...attributes) {\n    return attributes.some(a => {\n      return vm.hasOwnProperty(a) || Object.getPrototypeOf(vm).hasOwnProperty(a)\n    })\n  }\n\n  function setupAttribute(\n    NAME,\n    value,\n    computedOrMethods = 'computed',\n    returnTheValue = false\n  ) {\n    if (typeof value === 'boolean') {\n      data[NAME] = !!value\n    } else if (typeof value === 'string') {\n      mixin.computed[NAME] = function() {\n        // If the specified computed prop wasn't found, display an error.\n        if (!returnTheValue) {\n          if (!hasSomeAttribute(this, value, NAME)) {\n            throw new Error(\n              `Value for ${NAME} was not found on the component at '${value}'.`\n            )\n          }\n        }\n        return returnTheValue ? value : this[value]\n      }\n    } else if (typeof value === 'function') {\n      mixin[computedOrMethods][NAME] = value\n    }\n  }\n\n  setupAttribute(SERVICE_NAME, service, 'computed', true)\n  setupAttribute(ID, id)\n  setupAttribute(PARAMS, params)\n  setupAttribute(FETCH_PARAMS, fetchParams)\n  setupAttribute(QUERY_WHEN, queryWhen, 'computed')\n  setupAttribute(LOCAL, local)\n\n  return mixin\n}\n"
  },
  {
    "path": "src/service-module/global-clients.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport _get from 'lodash/get'\n\n/**\n * A global object that holds references to all Model Classes in the application.\n */\nexport const clients: { [k: string]: any } = {\n  byAlias: {},\n  byHost: {}\n}\n\n/**\n * prepareAddModel wraps options in a closure around addModel\n * @param options\n */\nexport function addClient({ client, serverAlias }) {\n  // Save reference to the clients by host uri, if it was available.\n  let uri = ''\n  if (client.io) {\n    uri = _get(client, 'io.io.uri')\n  }\n  if (uri) {\n    clients.byHost[uri] = client\n  }\n  // Save reference to clients by serverAlias.\n  clients.byAlias[serverAlias] = client\n}\n\nexport function clearClients() {\n  function deleteKeys(path) {\n    Object.keys(clients[path]).forEach(key => {\n      delete clients[path][key]\n    })\n  }\n  deleteKeys('byAlias')\n  deleteKeys('byHost')\n}\n"
  },
  {
    "path": "src/service-module/global-models.ts",
    "content": "/*\neslint\nno-console: 0,\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { FeathersVuexOptions } from './types'\n\n/**\n * A global object that holds references to all Model Classes in the application.\n */\nexport const globalModels: { [k: string]: any } = {}\n\n/**\n * prepareAddModel wraps options in a closure around addModel\n * @param options\n */\nexport function prepareAddModel(options: FeathersVuexOptions) {\n  const { serverAlias } = options\n\n  return function addModel(Model) {\n    globalModels[serverAlias] = globalModels[serverAlias] || {\n      byServicePath: {}\n    }\n    const name = Model.modelName || Model.name\n    if (globalModels[serverAlias][name] && options.debug) {\n      // eslint-disable-next-line no-console\n      console.error(`Overwriting Model: models[${serverAlias}][${name}].`)\n    }\n    globalModels[serverAlias][name] = Model\n    globalModels[serverAlias].byServicePath[Model.servicePath] = Model\n  }\n}\n\nexport function clearModels() {\n  Object.keys(globalModels).forEach(key => {\n    const serverAliasObj = globalModels[key]\n\n    Object.keys(serverAliasObj).forEach(key => {\n      delete globalModels[key]\n    })\n\n    delete globalModels[key]\n  })\n}\n"
  },
  {
    "path": "src/service-module/make-base-model.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport {\n  FeathersVuexOptions,\n  Id,\n  ModelInstanceOptions,\n  Model,\n  ModelStatic,\n  GlobalModels,\n  StoreState,\n  AnyData,\n  PatchParams\n} from './types'\nimport { globalModels, prepareAddModel } from './global-models'\nimport { mergeWithAccessors, checkNamespace, getId, Params } from '../utils'\nimport _merge from 'lodash/merge'\nimport _get from 'lodash/get'\nimport { EventEmitter } from 'events'\nimport { ModelSetupContext } from './types'\nimport { Store } from 'vuex'\nimport { GetterName } from './service-module.getters'\n\nconst defaultOptions = {\n  clone: false,\n  commit: true,\n  merge: true\n}\n\n/** Ensures value has EventEmitter instance props */\nfunction assertIsEventEmitter(val: unknown): asserts val is EventEmitter {\n  if (\n    !Object.keys(EventEmitter.prototype).every((eeKey) =>\n      Object.prototype.hasOwnProperty.call(val, eeKey)\n    )\n  ) {\n    throw new Error(`Expected EventEmitter, but got ${val}`)\n  }\n}\n\n/**\n *\n * @param options\n */\nexport default function makeBaseModel(options: FeathersVuexOptions) {\n  const addModel = prepareAddModel(options)\n  const { serverAlias } = options\n\n  // If this serverAlias already has a BaseModel, return it\n  const ExistingBaseModel = _get(globalModels, [serverAlias, 'BaseModel'])\n  if (ExistingBaseModel) {\n    return ExistingBaseModel as ModelStatic\n  }\n\n  abstract class BaseModel implements Model {\n    // Think of these as abstract static properties\n    public static servicePath: string\n    public static namespace: string\n    public static keepCopiesInStore = options.keepCopiesInStore\n    // eslint-disable-next-line\n    public static instanceDefaults(data: AnyData, ctx: ModelSetupContext) {\n      return data\n    }\n    // eslint-disable-next-line\n    public static setupInstance(data: AnyData, ctx: ModelSetupContext) {\n      return data\n    }\n    public static diffOnPatch(data: AnyData) {\n      return data\n    }\n\n    // Monkey patched onto the Model class in `makeServicePlugin()`\n    public static store: Store<StoreState>\n\n    public static idField: string = options.idField\n    public static tempIdField: string = options.tempIdField\n    public static preferUpdate: boolean = options.preferUpdate\n    public static serverAlias: string = options.serverAlias\n\n    public static readonly models = globalModels as GlobalModels // Can access other Models here\n\n    public static readonly copiesById: {\n      [key: string]: Model | undefined\n      [key: number]: Model | undefined\n    } = {}\n\n    public __id: string\n    public __isClone: boolean\n    public __isTemp: boolean\n\n    public static merge = mergeWithAccessors\n    public static modelName = 'BaseModel'\n\n    public constructor(data: AnyData, options: ModelInstanceOptions) {\n      // You have to pass at least an empty object to get a tempId.\n      data = data || {}\n      options = Object.assign({}, defaultOptions, options)\n\n      const {\n        store,\n        keepCopiesInStore,\n        copiesById: copiesByIdOnModel,\n        models,\n        instanceDefaults,\n        idField,\n        tempIdField,\n        setupInstance,\n        getFromStore,\n        namespace,\n        _commit\n      } = this.constructor as typeof BaseModel\n      const id = getId(data, idField)\n      const hasValidId = id !== null && id !== undefined\n      const tempId =\n        data && data.hasOwnProperty(tempIdField) ? data[tempIdField] : undefined\n      const hasValidTempId = tempId !== null && tempId !== undefined\n      const copiesById = keepCopiesInStore\n        ? store?.state[namespace].copiesById\n        : copiesByIdOnModel\n\n      if (store?.state?.[namespace]?.replaceItems !== true) {\n        const existingItem =\n          hasValidId && !options.clone\n            ? getFromStore.call(this.constructor, id)\n            : null\n\n        // If it already exists, update the original and return\n        if (existingItem) {\n          data = setupInstance.call(this, data, { models, store }) || data\n          _commit.call(this.constructor, 'mergeInstance', data)\n          return existingItem\n        }\n      }\n\n      // If cloning and a clone already exists, update and return the original clone. Only one clone is allowed.\n      const existingClone =\n        (hasValidId || hasValidTempId) && options.clone\n          ? copiesById[id] || copiesById[tempId]\n          : null\n      if (existingClone) {\n        // This must be done in a mutation to avoid Vuex errors.\n        _commit.call(this.constructor, 'merge', {\n          dest: existingClone,\n          source: data\n        })\n        return existingClone\n      }\n\n      // Mark as a clone\n      if (options.clone) {\n        Object.defineProperty(this, '__isClone', {\n          value: true,\n          enumerable: false\n        })\n      }\n\n      // Setup instanceDefaults\n      if (instanceDefaults && typeof instanceDefaults === 'function') {\n        const defaults =\n          instanceDefaults.call(this, data, { models, store }) || data\n        mergeWithAccessors(this, defaults)\n      }\n\n      // Handles Vue objects or regular ones. We can't simply assign or return\n      // the data due to how Vue wraps everything into an accessor.\n      if (options.merge !== false) {\n        mergeWithAccessors(\n          this,\n          setupInstance.call(this, data, { models, store }) || data\n        )\n      }\n\n      // Add the item to the store\n      if (!options.clone && options.commit !== false && store) {\n        _commit.call(this.constructor, 'addItem', this)\n      }\n      return this\n    }\n\n    /**\n     * Calls `getter`, passing this model's ID as the parameter\n     * @param getter name of getter to call\n     */\n    private getGetterWithId(getter: GetterName): unknown {\n      const { _getters, idField, tempIdField } = this\n        .constructor as typeof BaseModel\n      const id =\n        getId(this, idField) != null ? getId(this, idField) : this[tempIdField]\n      return _getters.call(this.constructor, getter, id)\n    }\n\n    get isCreatePending(): boolean {\n      return this.getGetterWithId('isCreatePendingById') as boolean\n    }\n    get isUpdatePending(): boolean {\n      return this.getGetterWithId('isUpdatePendingById') as boolean\n    }\n    get isPatchPending(): boolean {\n      return this.getGetterWithId('isPatchPendingById') as boolean\n    }\n    get isRemovePending(): boolean {\n      return this.getGetterWithId('isRemovePendingById') as boolean\n    }\n    get isSavePending(): boolean {\n      return this.getGetterWithId('isSavePendingById') as boolean\n    }\n    get isPending(): boolean {\n      return this.getGetterWithId('isPendingById') as boolean\n    }\n\n    public static getId(record: Record<string, any>): string {\n      const { idField } = this.constructor as typeof BaseModel\n      return getId(record, idField)\n    }\n\n    public static find(params?: Params) {\n      return this._dispatch('find', params)\n    }\n\n    public static findInStore(params?: Params) {\n      return this._getters('find', params)\n    }\n\n    public static count(params?: Params) {\n      return this._dispatch('count', params)\n    }\n\n    public static countInStore(params?: Params) {\n      return this._getters('count', params)\n    }\n\n    public static get(id: Id, params?: Params) {\n      if (params) {\n        return this._dispatch('get', [id, params])\n      } else {\n        return this._dispatch('get', id)\n      }\n    }\n\n    public static getFromStore(id: Id, params?: Params) {\n      return this._getters('get', id, params)\n    }\n\n    /**\n     * An alias for store.getters. Can only call function-based getters, since\n     * it's meant for only `find` and `get`.\n     * @param method the vuex getter name without the namespace\n     * @param payload if provided, the getter will be called as a function\n     */\n    public static _getters(name: GetterName, idOrParams?: any, params?: any) {\n      const { namespace, store } = this\n\n      if (checkNamespace(namespace, this, options.debug)) {\n        if (!store.getters.hasOwnProperty(`${namespace}/${name}`)) {\n          throw new Error(`Could not find getter named ${namespace}/${name}`)\n        }\n        return store.getters[`${namespace}/${name}`](idOrParams, params)\n      }\n    }\n    /**\n     * An alias for store.commit\n     * @param method the vuex mutation name without the namespace\n     * @param payload the payload for the mutation\n     */\n    public static _commit(method: string, payload: any): void {\n      const { namespace, store } = this\n\n      if (checkNamespace(namespace, this, options.debug)) {\n        store.commit(`${namespace}/${method}`, payload)\n      }\n    }\n    /**\n     * An alias for store.dispatch\n     * @param method the vuex action name without the namespace\n     * @param payload the payload for the action\n     */\n    public static _dispatch(method: string, payload: any) {\n      const { namespace, store } = this\n\n      if (checkNamespace(namespace, this, options.debug)) {\n        return store.dispatch(`${namespace}/${method}`, payload)\n      }\n    }\n\n    /**\n     * make the server side documents hydrated on client a FeathersVuexModel\n     */\n    public static hydrateAll() {\n      const { namespace, store } = this\n      const state = store.state[namespace]\n      const commit = store.commit\n      // Replace each plain object with a model instance.\n      Object.keys(state.keyedById).forEach((id) => {\n        const record = state.keyedById[id]\n        commit(`${namespace}/removeItem`, record)\n        commit(`${namespace}/addItem`, record)\n      })\n    }\n\n    /**\n     * clone the current record using the `createCopy` mutation\n     */\n    public clone(data: AnyData): this {\n      const { idField, tempIdField } = this.constructor as typeof BaseModel\n      if (this.__isClone) {\n        throw new Error('You cannot clone a copy')\n      }\n      const id =\n        getId(this, idField) != null ? getId(this, idField) : this[tempIdField]\n      return this._clone(id, data)\n    }\n\n    private _clone(id, data = {}) {\n      const { store, namespace, _commit, _getters } = this\n        .constructor as typeof BaseModel\n      const { keepCopiesInStore } = store.state[namespace]\n\n      _commit.call(this.constructor, `createCopy`, id)\n\n      if (keepCopiesInStore) {\n        return Object.assign(\n          _getters.call(this.constructor, 'getCopyById', id),\n          data\n        )\n      } else {\n        // const { copiesById } = this.constructor as typeof BaseModel\n        return Object.assign(\n          (this.constructor as typeof BaseModel).copiesById[id],\n          data\n        )\n      }\n    }\n    /**\n     * Reset a clone to match the instance in the store.\n     */\n    public reset(): this {\n      const { idField, tempIdField, _commit } = this\n        .constructor as typeof BaseModel\n\n      if (this.__isClone) {\n        const id =\n          getId(this, idField) != null\n            ? getId(this, idField)\n            : this[tempIdField]\n        _commit.call(this.constructor, 'resetCopy', id)\n        return this\n      } else {\n        throw new Error('You cannot reset a non-copy')\n      }\n    }\n\n    /**\n     * Update a store instance to match a clone.\n     */\n    public commit(): this {\n      const { idField, tempIdField, _commit, _getters } = this\n        .constructor as typeof BaseModel\n      if (this.__isClone) {\n        const id =\n          getId(this, idField) != null\n            ? getId(this, idField)\n            : this[tempIdField]\n        _commit.call(this.constructor, 'commitCopy', id)\n\n        return _getters.call(this.constructor, 'get', id)\n      } else {\n        throw new Error('You cannot call commit on a non-copy')\n      }\n    }\n\n    /**\n     * A shortcut to either call create or patch/update\n     * @param params\n     */\n    public save(params?: Params): Promise<this> {\n      const { idField, preferUpdate } = this.constructor as typeof BaseModel\n      const id = getId(this, idField)\n      if (id != null) {\n        return preferUpdate ? this.update(params) : this.patch(params)\n      } else {\n        return this.create(params)\n      }\n    }\n    /**\n     * Calls service create with the current instance data\n     * @param params\n     */\n    public create(params?: Params): Promise<this> {\n      const { _dispatch } = this.constructor as typeof BaseModel\n      const data = Object.assign({}, this)\n      if (data[options.idField] === null) {\n        delete data[options.idField]\n      }\n      return _dispatch.call(this.constructor, 'create', [data, params])\n    }\n\n    /**\n     * Calls service patch with the current instance data\n     * @param params\n     */\n    public patch<D extends {} = AnyData>(\n      params?: PatchParams<D>\n    ): Promise<this> {\n      const { idField, _dispatch } = this.constructor as typeof BaseModel\n      const id = getId(this, idField)\n\n      if (id == null) {\n        const error = new Error(\n          `Missing ${idField} property. You must create the data before you can patch with this data`\n        )\n        return Promise.reject(error)\n      }\n      return _dispatch.call(this.constructor, 'patch', [id, this, params])\n    }\n\n    /**\n     * Calls service update with the current instance data\n     * @param params\n     */\n    public update(params?: Params): Promise<this> {\n      const { idField, _dispatch } = this.constructor as typeof BaseModel\n      const id = getId(this, idField)\n\n      if (id !== 0 && !id) {\n        const error = new Error(\n          `Missing ${idField} property. You must create the data before you can update with this data`\n        )\n        return Promise.reject(error)\n      }\n      return _dispatch.call(this.constructor, 'update', [id, this, params])\n    }\n\n    /**\n     * Calls service remove with the current instance id\n     * @param params\n     */\n    public remove(params?: Params): Promise<this> {\n      const { idField, tempIdField, _dispatch, _commit } = this\n        .constructor as typeof BaseModel\n      const id = getId(this, idField)\n\n      if (id != null) {\n        if (params && params.eager) {\n          _commit.call(this.constructor, 'removeItem', id)\n        }\n        return _dispatch.call(this.constructor, 'remove', [id, params])\n      } else {\n        // is temp\n        _commit.call(this.constructor, 'removeTemps', [this[tempIdField]])\n        _commit.call(this.constructor, 'clearCopy', [this[tempIdField]])\n        return Promise.resolve(this)\n      }\n    }\n\n    public toJSON() {\n      return _merge({}, this)\n    }\n  }\n  for (const n in EventEmitter.prototype) {\n    BaseModel[n] = EventEmitter.prototype[n]\n  }\n\n  addModel(BaseModel)\n\n  const BaseModelEventEmitter = BaseModel\n  assertIsEventEmitter(BaseModelEventEmitter)\n  return BaseModelEventEmitter as ModelStatic\n}\n"
  },
  {
    "path": "src/service-module/make-service-module.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport _pick from 'lodash/pick'\nimport _merge from 'lodash/merge'\nimport makeDefaultState from './service-module.state'\nimport makeGetters from './service-module.getters'\nimport makeMutations from './service-module.mutations'\nimport makeActions from './service-module.actions'\nimport { Service } from '@feathersjs/feathers'\nimport { MakeServicePluginOptions } from './types'\nimport { Store } from 'vuex'\n\nexport default function makeServiceModule(\n  service: Service<any>,\n  options: MakeServicePluginOptions,\n  store: Store<any>\n) {\n  const defaults = {\n    namespaced: true,\n    state: makeDefaultState(options),\n    getters: makeGetters(),\n    mutations: makeMutations(),\n    actions: makeActions({service, options})\n  }\n  const fromOptions = _pick(options, [\n    'state',\n    'getters',\n    'mutations',\n    'actions'\n  ])\n  const merged = _merge({}, defaults, fromOptions)\n  const extended = options.extend({ store, module: merged })\n  const finalModule = _merge({}, merged, extended)\n\n  return finalModule\n}\n"
  },
  {
    "path": "src/service-module/make-service-plugin.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport {\n  FeathersVuexOptions,\n  MakeServicePluginOptions,\n  ServicePluginExtendOptions\n} from './types'\n\nimport makeServiceModule from './make-service-module'\nimport { globalModels, prepareAddModel } from './global-models'\nimport enableServiceEvents from './service-module.events'\nimport { makeNamespace, getServicePath, assignIfNotPresent } from '../utils'\nimport _get from 'lodash/get'\n\ninterface ServiceOptionsDefaults {\n  servicePath: string\n  namespace: string\n  extend: (\n    options: ServicePluginExtendOptions\n  ) => {\n    state: any\n    getters: any\n    mutations: any\n    actions: any\n  }\n  state: {}\n  getters: {}\n  mutations: {}\n  actions: {}\n  instanceDefaults: () => {}\n  setupInstance: (instance: {}) => {}\n  debounceEventsMaxWait: number\n}\n\nconst defaults: ServiceOptionsDefaults = {\n  namespace: '', // The namespace for the Vuex module. Will generally be derived from the service.path, service.name, when available. Otherwise, it must be provided here, explicitly.\n  servicePath: '',\n  extend: ({ module }) => module, // for custom plugin (replaces state, getters, mutations, and actions)\n  state: {}, // for custom state\n  getters: {}, // for custom getters\n  mutations: {}, // for custom mutations\n  actions: {}, // for custom actions\n  instanceDefaults: () => ({}), // Default instanceDefaults returns an empty object\n  setupInstance: instance => instance, // Default setupInstance returns the instance\n  debounceEventsMaxWait: 1000\n}\nconst events = ['created', 'patched', 'updated', 'removed']\n\n/**\n * prepare only wraps the makeServicePlugin to provide the globalOptions.\n * @param globalOptions\n */\nexport default function prepareMakeServicePlugin(\n  globalOptions: FeathersVuexOptions\n) {\n  const addModel = prepareAddModel(globalOptions)\n  /**\n   * (1) Make a Vuex plugin for the provided service.\n   * (2a) Attach the vuex store to the BaseModel.\n   * (2b) If the Model does not extend the BaseModel, monkey patch it, too\n   * (3) Setup real-time events\n   */\n  return function makeServicePlugin(config: MakeServicePluginOptions) {\n    if (!config.service) {\n      throw new Error(\n        'No service was provided. If you passed one in, check that you have configured a transport plugin on the Feathers Client. Make sure you use the client version of the transport.'\n      )\n    }\n    const options = Object.assign({}, defaults, globalOptions, config)\n    const {\n      Model,\n      service,\n      namespace,\n      nameStyle,\n      instanceDefaults,\n      setupInstance,\n      preferUpdate\n    } = options\n\n    if (globalOptions.handleEvents && options.handleEvents) {\n      options.handleEvents = Object.assign(\n        {},\n        globalOptions.handleEvents,\n        options.handleEvents\n      )\n    }\n\n    events.forEach(eventName => {\n      if (!options.handleEvents[eventName])\n        options.handleEvents[eventName] = () => options.enableEvents || true\n    })\n\n    // Make sure we get a service path from either the service or the options\n    let { servicePath } = options\n    if (!servicePath) {\n      servicePath = getServicePath(service, Model)\n    }\n    options.servicePath = servicePath\n\n    service.FeathersVuexModel = Model\n\n    return store => {\n      // (1^) Create and register the Vuex module\n      options.namespace = makeNamespace(namespace, servicePath, nameStyle)\n      const module = makeServiceModule(service, options, store)\n      // Don't preserve state if reinitialized (prevents state pollution in SSR)\n      store.registerModule(options.namespace, module, { preserveState: false })\n\n      // (2a^) Monkey patch the BaseModel in globalModels\n      const BaseModel = _get(globalModels, [options.serverAlias, 'BaseModel'])\n      if (BaseModel && !BaseModel.store) {\n        Object.assign(BaseModel, {\n          store\n        })\n      }\n      // (2b^) Monkey patch the Model(s) and add to globalModels\n      assignIfNotPresent(Model, {\n        namespace: options.namespace,\n        servicePath,\n        instanceDefaults,\n        setupInstance,\n        preferUpdate\n      })\n      // As per 1^, don't preserve state on the model either (prevents state pollution in SSR)\n      Object.assign(Model, {\n        store\n      })\n      if (!Model.modelName || Model.modelName === 'BaseModel') {\n        throw new Error(\n          'The modelName property is required for Feathers-Vuex Models'\n        )\n      }\n      addModel(Model)\n\n      // (3^) Setup real-time events\n      if (options.enableEvents) {\n        enableServiceEvents({ service, Model, store, options })\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/service-module/service-module.actions.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport fastCopy from 'fast-copy'\nimport { getId } from '../utils'\nimport { Service } from '@feathersjs/feathers'\nimport { MakeServicePluginOptions } from './types'\n\ninterface serviceAndOptions {\n  service: Service<any>\n  options: MakeServicePluginOptions\n}\n\nexport default function makeServiceActions({service, options}: serviceAndOptions) {\n  const serviceActions = {\n    find({ commit, dispatch }, params) {\n      params = params || {}\n      params = fastCopy(params)\n\n      // For working with client-side services, paginate.default must be truthy.\n      if (params.paginate === true) {\n        params.paginate = { default: true }\n      }\n\n      commit('setPending', 'find')\n\n      return service\n        .find(params)\n        .then(response => dispatch('handleFindResponse', { params, response }))\n        .catch(error => dispatch('handleFindError', { params, error }))\n    },\n\n    // Two query syntaxes are supported, since actions only receive one argument.\n    //   1. Just pass the id: `get(1)`\n    //   2. Pass arguments as an array: `get([null, params])`\n    get({ state, getters, commit, dispatch }, args) {\n      let id\n      let params\n      let skipRequestIfExists\n\n      if (Array.isArray(args)) {\n        id = args[0]\n        params = args[1] || {}\n      } else {\n        id = args\n        params = {}\n      }\n\n      params = fastCopy(params)\n\n      if ('skipRequestIfExists' in params) {\n        skipRequestIfExists = params.skipRequestIfExists\n        delete params.skipRequestIfExists\n      } else {\n        skipRequestIfExists = state.skipRequestIfExists\n      }\n\n      function getFromRemote() {\n        commit('setPending', 'get')\n        return service\n          .get(id, params)\n          .then(async function (item) {\n            dispatch('addOrUpdate', item)\n            commit('unsetPending', 'get')\n            return state.keyedById[id]\n          })\n          .catch(error => {\n            commit('setError', { method: 'get', error })\n            commit('unsetPending', 'get')\n            return Promise.reject(error)\n          })\n      }\n\n      // If the records is already in store, return it\n      const existingItem = getters.get(id, params)\n      if (existingItem && skipRequestIfExists) {\n        return Promise.resolve(existingItem)\n      }\n      return getFromRemote()\n    },\n\n    create({ commit, dispatch, state }, dataOrArray) {\n      const { idField, tempIdField } = state\n      let data\n      let params\n      let tempIds\n\n      if (Array.isArray(dataOrArray)) {\n        data = dataOrArray[0]\n        params = dataOrArray[1]\n      } else {\n        data = dataOrArray\n      }\n\n      params = fastCopy(params)\n\n      if (Array.isArray(data)) {\n        tempIds = data.map(i => i[tempIdField])\n      } else {\n        tempIds = [data[tempIdField]] // Array of tempIds\n      }\n\n      params = params || {}\n\n      commit('setPending', 'create')\n      commit('setIdPending', { method: 'create', id: tempIds })\n\n      return service\n        .create(data, params)\n        .then(async response => {\n          if (Array.isArray(response)) {\n            dispatch('addOrUpdateList', response)\n            response = response.map(item => {\n              const id = getId(item, idField)\n\n              return state.keyedById[id]\n            })\n          } else {\n            const id = getId(response, idField)\n            const tempId = tempIds[0]\n\n            if (id != null && tempId != null) {\n              commit('updateTemp', { id, tempId })\n            }\n            response = dispatch('addOrUpdate', response)\n\n            // response = state.keyedById[id]\n          }\n          commit('removeTemps', tempIds)\n          return response\n        })\n        .catch(error => {\n          commit('setError', { method: 'create', error })\n          return Promise.reject(error)\n        })\n        .finally(() => {\n          commit('unsetPending', 'create')\n          commit('unsetIdPending', { method: 'create', id: tempIds })\n        })\n    },\n\n    update({ commit, dispatch, state }, [id, data, params]) {\n      commit('setPending', 'update')\n      commit('setIdPending', { method: 'update', id })\n\n      params = fastCopy(params)\n\n      return service\n        .update(id, data, params)\n        .then(async function (item) {\n          dispatch('addOrUpdate', item)\n          return state.keyedById[id]\n        })\n        .catch(error => {\n          commit('setError', { method: 'update', error })\n          return Promise.reject(error)\n        })\n        .finally(() => {\n          commit('unsetPending', 'update')\n          commit('unsetIdPending', { method: 'update', id })\n        })\n    },\n\n    /**\n     * If params.data is provided, it will be passed as the patch data (instead of the `data` arg).\n     * This provides a simple way to patch with partial data.\n     */\n    patch({ commit, dispatch, state }, [id, data, params]) {\n      commit('setPending', 'patch')\n      commit('setIdPending', { method: 'patch', id })\n\n      params = fastCopy(params)\n\n      if (options.Model && (!params || !params.data)) {\n        data = options.Model.diffOnPatch(data)\n      }\n      if (params && params.data) {\n        data = params.data\n      }\n\n      return service\n        .patch(id, data, params)\n        .then(async function (item) {\n          dispatch('addOrUpdate', item)\n          return state.keyedById[id]\n        })\n        .catch(error => {\n          commit('setError', { method: 'patch', error })\n          return Promise.reject(error)\n        })\n        .finally(() => {\n          commit('unsetPending', 'patch')\n          commit('unsetIdPending', { method: 'patch', id })\n        })\n    },\n\n    remove({ commit }, idOrArray) {\n      let id\n      let params\n\n      if (Array.isArray(idOrArray)) {\n        id = idOrArray[0]\n        params = idOrArray[1]\n      } else {\n        id = idOrArray\n      }\n\n      params = params || {}\n      params = fastCopy(params)\n\n      commit('setPending', 'remove')\n      commit('setIdPending', { method: 'remove', id })\n\n      return service\n        .remove(id, params)\n        .then(item => {\n          commit('removeItem', id)\n          return item\n        })\n        .catch(error => {\n          commit('setError', { method: 'remove', error })\n          return Promise.reject(error)\n        })\n        .finally(() => {\n          commit('unsetPending', 'remove')\n          commit('unsetIdPending', { method: 'remove', id })\n        })\n    }\n  }\n\n  const actions = {\n    count({ dispatch }, params) {\n      params = params || {}\n      params = fastCopy(params)\n\n      if (!params.query) {\n        throw 'params must contain a query-object'\n      }\n\n      params.query.$limit = 0 // <- limit 0 in feathers is a fast count query\n\n      return dispatch('find', params)\n        .then(response => {\n          return response.total || response.length\n        })\n        .catch(error => dispatch('handleFindError', { params, error }))\n    },\n    /**\n     * Handle the response from the find action.\n     *\n     * @param payload consists of the following two params\n     *   @param params - Remember that these params aren't what was sent to the\n     *         Feathers client.  The client modifies the params object.\n     *   @param response\n     */\n    async handleFindResponse(\n      { state, commit, dispatch },\n      { params, response }\n    ) {\n      const { qid = 'default', query } = params\n      const { idField } = state\n\n      dispatch('addOrUpdateList', response)\n      commit('unsetPending', 'find')\n\n      const mapItemFromState = item => {\n        const id = getId(item, idField)\n\n        return state.keyedById[id]\n      }\n\n      // The pagination data will be under `pagination.default` or whatever qid is passed.\n      response.data &&\n        commit('updatePaginationForQuery', { qid, response, query })\n\n      // Swap out the response records for their Vue-observable store versions\n      const data = response.data || response\n      const mappedFromState = data.map(mapItemFromState)\n      if (mappedFromState[0] !== undefined) {\n        response.data\n          ? (response.data = mappedFromState)\n          : (response = mappedFromState)\n      }\n\n      response = await dispatch('afterFind', response)\n\n      return response\n    },\n\n    async handleFindError({ commit }, { params, error }) {\n      commit('setError', { method: 'find', params, error })\n      commit('unsetPending', 'find')\n      return Promise.reject(error)\n    },\n\n    async afterFind({}, response) {\n      return response\n    },\n\n    addOrUpdateList({ state, commit }, response) {\n      const list = response.data || response\n      const isPaginated = response.hasOwnProperty('total')\n      const toAdd = []\n      const toUpdate = []\n      const toRemove = []\n      const { idField, autoRemove } = state\n\n      const disableRemove = response.disableRemove || !autoRemove\n\n      list.forEach(item => {\n        const id = getId(item, idField)\n        const existingItem = state.keyedById[id]\n\n        if (id !== null && id !== undefined) {\n          existingItem ? toUpdate.push(item) : toAdd.push(item)\n        }\n      })\n\n      if (!isPaginated && !disableRemove) {\n        // Find IDs from the state which are not in the list\n        state.ids.forEach(id => {\n          if (!list.some(item => getId(item, idField) === id)) {\n            toRemove.push(state.keyedById[id])\n          }\n        })\n        commit('removeItems', toRemove) // commit removal\n      }\n\n      if (options.Model) {\n        toAdd.forEach((item, index) => {\n          toAdd[index] = new options.Model(item, { commit: false })\n        })\n      }\n\n      commit('addItems', toAdd)\n      commit('updateItems', toUpdate)\n\n      return response\n    },\n\n    /**\n     * Adds or updates an item. If a matching temp record is found in the store,\n     * the temp record will completely replace the existingItem. This is to work\n     * around the common scenario where the realtime `created` event arrives before\n     * the `create` response returns to create the record. The reference to the\n     * original temporary record must be maintained in order to preserve reactivity.\n     */\n    addOrUpdate({ state, commit }, item) {\n      const { idField } = state\n      const id = getId(item, idField)\n\n      const isIdOk = id !== null && id !== undefined\n\n      if (\n        options.Model &&\n        !(item instanceof options.Model)\n      ) {\n        item = new options.Model(item, { commit: false })\n      }\n\n      if (isIdOk) {\n        if (state.keyedById[id]) {\n          commit('updateItem', item)\n        } else {\n          commit('addItem', item)\n        }\n      }\n      return item\n    }\n  }\n  /**\n   * Only add a method to the store if the service actually has that same method.\n   */\n  Object.keys(serviceActions).map(method => {\n    if (service[method] && typeof service[method] === 'function') {\n      actions[method] = serviceActions[method]\n    }\n  })\n  return actions\n}\n"
  },
  {
    "path": "src/service-module/service-module.events.ts",
    "content": "import { getId } from '../utils'\nimport _debounce from 'lodash/debounce'\nimport { globalModels } from './global-models'\n\nexport interface ServiceEventsDebouncedQueue {\n  addOrUpdateById: {}\n  removeItemById: {}\n  enqueueAddOrUpdate(item: any): void\n  enqueueRemoval(item: any): void\n  flushAddOrUpdateQueue(): void\n  flushRemoveItemQueue(): void\n}\n\nexport default function enableServiceEvents({\n  service,\n  Model,\n  store,\n  options\n}): ServiceEventsDebouncedQueue {\n  const debouncedQueue: ServiceEventsDebouncedQueue = {\n    addOrUpdateById: {},\n    removeItemById: {},\n    enqueueAddOrUpdate(item): void {\n      const id = getId(item, options.idField)\n      this.addOrUpdateById[id] = item\n      if (this.removeItemById.hasOwnProperty(id)) {\n        delete this.removeItemById[id]\n      }\n      this.flushAddOrUpdateQueue()\n    },\n    enqueueRemoval(item): void {\n      const id = getId(item, options.idField)\n      this.removeItemById[id] = item\n      if (this.addOrUpdateById.hasOwnProperty(id)) {\n        delete this.addOrUpdateById[id]\n      }\n      this.flushRemoveItemQueue()\n    },\n    flushAddOrUpdateQueue: _debounce(\n      async function () {\n        const values = Object.values(this.addOrUpdateById)\n        if (values.length === 0) return\n        await store.dispatch(`${options.namespace}/addOrUpdateList`, {\n          data: values,\n          disableRemove: true\n        })\n        this.addOrUpdateById = {}\n      },\n      options.debounceEventsTime || 20,\n      { maxWait: options.debounceEventsMaxWait }\n    ),\n    flushRemoveItemQueue: _debounce(\n      function () {\n        const values = Object.values(this.removeItemById)\n        if (values.length === 0) return\n        store.commit(`${options.namespace}/removeItems`, values)\n        this.removeItemById = {}\n      },\n      options.debounceEventsTime || 20,\n      { maxWait: options.debounceEventsMaxWait }\n    )\n  }\n\n  const handleEvent = (eventName, item, mutationName): void => {\n    const handler = options.handleEvents[eventName]\n    const confirmOrArray = handler(item, {\n      model: Model,\n      models: globalModels\n    })\n    const [affectsStore, modified = item] = Array.isArray(confirmOrArray)\n      ? confirmOrArray\n      : [confirmOrArray]\n    if (affectsStore) {\n      if (!options.debounceEventsTime) {\n        eventName === 'removed'\n          ? store.commit(`${options.namespace}/removeItem`, modified)\n          : store.dispatch(`${options.namespace}/${mutationName}`, modified)\n      } else {\n        eventName === 'removed'\n          ? debouncedQueue.enqueueRemoval(item)\n          : debouncedQueue.enqueueAddOrUpdate(item)\n      }\n    }\n  }\n\n  // Listen to socket events when available.\n  service.on('created', item => {\n    handleEvent('created', item, 'addOrUpdate')\n    Model.emit && Model.emit('created', item)\n  })\n  service.on('updated', item => {\n    handleEvent('updated', item, 'addOrUpdate')\n    Model.emit && Model.emit('updated', item)\n  })\n  service.on('patched', item => {\n    handleEvent('patched', item, 'addOrUpdate')\n    Model.emit && Model.emit('patched', item)\n  })\n  service.on('removed', item => {\n    handleEvent('removed', item, 'removeItem')\n    Model.emit && Model.emit('removed', item)\n  })\n\n  return debouncedQueue\n}\n"
  },
  {
    "path": "src/service-module/service-module.getters.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport sift from 'sift'\nimport { filterQuery, sorter, select } from '@feathersjs/adapter-commons'\nimport { globalModels as models } from './global-models'\nimport _omit from 'lodash/omit'\nimport { unref } from '@vue/composition-api'\nimport { ServiceState } from '..'\nimport { Id } from '@feathersjs/feathers'\n\nconst FILTERS = ['$sort', '$limit', '$skip', '$select']\nconst additionalOperators = ['$elemMatch']\n\nconst getCopiesById = ({\n  keepCopiesInStore,\n  servicePath,\n  serverAlias,\n  copiesById\n}) => {\n  if (keepCopiesInStore) {\n    return copiesById\n  } else {\n    const Model = models[serverAlias].byServicePath[servicePath]\n\n    return Model.copiesById\n  }\n}\n\nexport default function makeServiceGetters() {\n  return {\n    list: state => Object.values(state.keyedById),\n    find: state => _params => {\n      const params = unref(_params) || {}\n\n      const {\n        paramsForServer,\n        whitelist,\n        keyedById,\n        idField,\n        tempsById\n      } = state\n      const q = _omit(params.query || {}, paramsForServer)\n\n      const { query, filters } = filterQuery(q, {\n        operators: additionalOperators.concat(whitelist)\n      })\n\n      let values = Object.values(keyedById) as any\n\n      if (params.temps) {\n        values.push(...(Object.values(tempsById) as any))\n      }\n\n      values = values.filter(sift(query))\n\n      if (params.copies) {\n        const copiesById = getCopiesById(state)\n        // replace keyedById value with existing clone value\n        values = values.map(value => copiesById[value[idField]] || value)\n      }\n\n      const total = values.length\n\n      if (filters.$sort !== undefined) {\n        values.sort(sorter(filters.$sort))\n      }\n\n      if (filters.$skip !== undefined && filters.$limit !== undefined) {\n        values = values.slice(filters.$skip, filters.$limit + filters.$skip)\n      } else if (filters.$skip !== undefined || filters.$limit !== undefined) {\n        values = values.slice(filters.$skip, filters.$limit)\n      }\n\n      if (filters.$select) {\n        values = select(params)(values)\n      }\n\n      return {\n        total,\n        limit: filters.$limit || 0,\n        skip: filters.$skip || 0,\n        data: values\n      }\n    },\n    count: (state, getters) => _params => {\n      const params = unref(_params) || {}\n\n      const cleanQuery = _omit(params.query, FILTERS)\n      params.query = cleanQuery\n\n      return getters.find(params).total\n    },\n    get: ({ keyedById, tempsById, idField, tempIdField }) => (\n      _id,\n      _params = {}\n    ) => {\n      const id = unref(_id)\n      const params = unref(_params)\n\n      const record = keyedById[id] && select(params, idField)(keyedById[id])\n      if (record) {\n        return record\n      }\n      const tempRecord =\n        tempsById[id] && select(params, tempIdField)(tempsById[id])\n\n      return tempRecord || null\n    },\n    getCopyById: state => id => {\n      const copiesById = getCopiesById(state)\n      return copiesById[id]\n    },\n\n    isCreatePendingById: ({ isIdCreatePending }: ServiceState) => (id: Id) =>\n      isIdCreatePending.includes(id),\n    isUpdatePendingById: ({ isIdUpdatePending }: ServiceState) => (id: Id) =>\n      isIdUpdatePending.includes(id),\n    isPatchPendingById: ({ isIdPatchPending }: ServiceState) => (id: Id) =>\n      isIdPatchPending.includes(id),\n    isRemovePendingById: ({ isIdRemovePending }: ServiceState) => (id: Id) =>\n      isIdRemovePending.includes(id),\n    isSavePendingById: (state: ServiceState, getters) => (id: Id) =>\n      getters.isCreatePendingById(id) ||\n      getters.isUpdatePendingById(id) ||\n      getters.isPatchPendingById(id),\n    isPendingById: (state: ServiceState, getters) => (id: Id) =>\n      getters.isSavePendingById(id) || getters.isRemovePendingById(id)\n  }\n}\n\nexport type GetterName = keyof ReturnType<typeof makeServiceGetters>\n"
  },
  {
    "path": "src/service-module/service-module.mutations.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0,\nno-var: 0\n*/\nimport Vue from 'vue'\nimport { serializeError } from 'serialize-error'\nimport {\n  updateOriginal,\n  mergeWithAccessors,\n  assignTempId,\n  getId,\n  getQueryInfo\n} from '../utils'\nimport { globalModels as models } from './global-models'\nimport _omit from 'lodash/omit'\nimport _get from 'lodash/get'\nimport _isObject from 'lodash/isObject'\nimport { Id } from '@feathersjs/feathers'\nimport { ServiceState } from '..'\n\nexport type PendingServiceMethodName =\n  | 'find'\n  | 'get'\n  | 'create'\n  | 'update'\n  | 'patch'\n  | 'remove'\nexport type PendingIdServiceMethodName = Exclude<\n  PendingServiceMethodName,\n  'find' | 'get'\n>\n\nexport default function makeServiceMutations() {\n  function addItems(state, items) {\n    const { serverAlias, idField, tempIdField, modelName } = state\n    const Model = _get(models, [serverAlias, modelName])\n    const BaseModel = _get(models, [serverAlias, 'BaseModel'])\n\n    for (let item of items) {\n      const id = getId(item, idField)\n      const isTemp = id === null || id === undefined\n\n      // If the response contains a real id, remove isTemp\n      if (id != null) {\n        delete item.__isTemp\n      }\n\n      if (Model && !(item instanceof BaseModel) && !(item instanceof Model)) {\n        item = new Model(item, { commit: false })\n      }\n\n      if (isTemp) {\n        let tempId = item[tempIdField]\n        if (tempId == null) {\n          tempId = assignTempId(state, item)\n        }\n        item.__isTemp = true\n        Vue.set(state.tempsById, tempId, item)\n      } else {\n        // Only add the id if it's not already in the `ids` list.\n        if (!state.ids.includes(id)) {\n          state.ids.push(id)\n        }\n        Vue.set(state.keyedById, id, item)\n      }\n    }\n  }\n\n  function updateItems(state, items) {\n    const { idField, replaceItems, addOnUpsert, serverAlias, modelName } = state\n    const Model = _get(models, [serverAlias, modelName])\n    const BaseModel = _get(models, [state.serverAlias, 'BaseModel'])\n\n    for (let item of items) {\n      const id = getId(item, idField)\n\n      // If the response contains a real id, remove isTemp\n      if (id != null) {\n        delete item.__isTemp\n      }\n\n      // Update the record\n      if (id !== null && id !== undefined) {\n        if (state.ids.includes(id)) {\n          // Completely replace the item\n          if (replaceItems) {\n            if (Model && !(item instanceof Model)) {\n              item = new Model(item)\n            }\n            Vue.set(state.keyedById, id, item)\n            // Merge in changes\n          } else {\n            /**\n             * If we have a Model class, calling new Model(incomingData) will call update\n             * the original record with the accessors and setupInstance data.\n             * This means that date objects and relationships will be preserved.\n             *\n             * If there's no Model class, just call updateOriginal on the incoming data.\n             */\n            if (\n              Model &&\n              !(item instanceof BaseModel) &&\n              !(item instanceof Model)\n            ) {\n              item = new Model(item)\n            } else {\n              const original = state.keyedById[id]\n              updateOriginal(original, item)\n            }\n          }\n\n          // if addOnUpsert then add the record into the state, else discard it.\n        } else if (addOnUpsert) {\n          state.ids.push(id)\n          Vue.set(state.keyedById, id, item)\n        }\n        continue\n      }\n    }\n  }\n\n  function mergeInstance(state, item) {\n    const { idField } = state\n    const id = getId(item, idField)\n    const existingItem = state.keyedById[id]\n    if (existingItem) {\n      mergeWithAccessors(existingItem, item)\n    }\n  }\n\n  function merge(state, { dest, source }) {\n    mergeWithAccessors(dest, source)\n  }\n\n  return {\n    mergeInstance,\n    merge,\n    addItem(state, item) {\n      addItems(state, [item])\n    },\n    addItems,\n    updateItem(state, item) {\n      updateItems(state, [item])\n    },\n    updateItems(state, items) {\n      if (!Array.isArray(items)) {\n        throw new Error(\n          'You must provide an array to the `updateItems` mutation.'\n        )\n      }\n      updateItems(state, items)\n    },\n\n    // Promotes temp to \"real\" item:\n    // - adds _id to temp\n    // - removes __isTemp flag\n    // - migrates temp from tempsById to keyedById\n    updateTemp(state, { id, tempId }) {\n      const temp = state.tempsById[tempId]\n      if (temp) {\n        temp[state.idField] = id\n        Vue.delete(temp, '__isTemp')\n        Vue.delete(state.tempsById, tempId)\n        // If an item already exists in the store from the `created` event firing\n        // it will be replaced here\n        Vue.set(state.keyedById, id, temp)\n        // Only add the id if it's not already in the `ids` list.\n        if (!state.ids.includes(id)) {\n          state.ids.push(id)\n        }\n      }\n\n      // Add _id to temp's clone as well if it exists\n      const Model = _get(models, [state.serverAlias, state.modelName])\n      const tempClone = Model && Model.copiesById && Model.copiesById[tempId]\n      if (tempClone) {\n        tempClone[state.idField] = id\n        Model.copiesById[id] = tempClone\n        Vue.delete(tempClone, '__isTemp')\n      }\n    },\n\n    removeItem(state, item) {\n      const { idField } = state\n      const idToBeRemoved = _isObject(item) ? getId(item, idField) : item\n      const isIdOk = idToBeRemoved !== null && idToBeRemoved !== undefined\n      const index = state.ids.findIndex(i => i === idToBeRemoved)\n\n      const Model = _get(models, `[${state.serverAlias}][${state.modelName}]`)\n      const copiesById = state.keepCopiesInStore\n        ? state.copiesById\n        : Model.copiesById\n\n      if (isIdOk && index !== null && index !== undefined) {\n        Vue.delete(state.ids, index)\n        Vue.delete(state.keyedById, idToBeRemoved)\n        if (copiesById.hasOwnProperty(idToBeRemoved)) {\n          Vue.delete(copiesById, idToBeRemoved)\n        }\n      }\n    },\n\n    // Removes temp records\n    removeTemps(state, tempIds) {\n      tempIds.forEach(id => {\n        const temp = state.tempsById[id]\n        if (temp) {\n          if (temp[state.idField]) {\n            // Removes __isTemp if created\n            delete temp.__isTemp\n            Vue.delete(temp, '__isTemp')\n          }\n        }\n      })\n      state.tempsById = _omit(state.tempsById, tempIds)\n    },\n\n    removeItems(state, items) {\n      const { idField } = state\n\n      if (!Array.isArray(items)) {\n        throw new Error(\n          'You must provide an array to the `removeItems` mutation.'\n        )\n      }\n      // Make sure we have an array of ids. Assume all are the same.\n      const containsObjects = items[0] && _isObject(items[0])\n      const idsToRemove = containsObjects\n        ? items.map(item => getId(item, idField))\n        : items\n      const mapOfIdsToRemove = idsToRemove.reduce((map, id) => {\n        map[id] = true\n        return map\n      }, {})\n\n      const Model = _get(models, [\n        state.serverAlias,\n        'byServicePath',\n        state.servicePath\n      ])\n      const copiesById = state.keepCopiesInStore\n        ? state.copiesById\n        : Model.copiesById\n\n      idsToRemove.forEach(id => {\n        Vue.delete(state.keyedById, id)\n        if (copiesById.hasOwnProperty(id)) {\n          Vue.delete(copiesById, id)\n        }\n      })\n\n      // Get indexes to remove from the ids array.\n      const mapOfIndexesToRemove = state.ids.reduce((map, id, index) => {\n        if (mapOfIdsToRemove[id]) {\n          map[index] = true\n        }\n        return map\n      }, {})\n      // Remove highest indexes first, so the indexes don't change\n      const indexesInReverseOrder = Object.keys(mapOfIndexesToRemove).sort(\n        (a, b) => {\n          if (a < b) {\n            return 1\n          } else if (a > b) {\n            return -1\n          } else {\n            return 0\n          }\n        }\n      )\n      indexesInReverseOrder.forEach(indexInIdsArray => {\n        Vue.delete(state.ids, indexInIdsArray)\n      })\n    },\n\n    clearAll(state) {\n      state.ids = []\n      state.keyedById = {}\n\n      if (state.keepCopiesInStore) {\n        state.copiesById = {}\n      } else {\n        const Model = _get(models, [\n          state.serverAlias,\n          'byServicePath',\n          state.servicePath\n        ])\n        Object.keys(Model.copiesById).forEach(k =>\n          Vue.delete(Model.copiesById, k)\n        )\n      }\n    },\n\n    // Creates a copy of the record with the passed-in id, stores it in copiesById\n    createCopy(state, id) {\n      const { servicePath, keepCopiesInStore, serverAlias } = state\n      const current = state.keyedById[id] || state.tempsById[id]\n      const Model = _get(models, [serverAlias, 'byServicePath', servicePath])\n\n      let item\n\n      if (Model) {\n        item = new Model(current, { clone: true })\n      } else {\n        const existingClone = state.copiesById[id]\n\n        item = existingClone\n          ? mergeWithAccessors(existingClone, current)\n          : mergeWithAccessors({}, current)\n      }\n\n      if (keepCopiesInStore) {\n        state.copiesById[id] = item\n      } else {\n        // Since it won't be added to the store, make it a Vue object\n        if (!item.hasOwnProperty('__ob__')) {\n          item = Vue.observable(item)\n        }\n        if (!Model.hasOwnProperty('copiesById')) {\n          Object.defineProperty(Model, 'copiesById', { value: {} })\n        }\n        Model.copiesById[id] = item\n      }\n    },\n\n    // Resets the copy to match the original record, locally\n    resetCopy(state, id) {\n      const { servicePath, keepCopiesInStore } = state\n      const Model = _get(models, [\n        state.serverAlias,\n        'byServicePath',\n        servicePath\n      ])\n      const copy = keepCopiesInStore\n        ? state.copiesById[id]\n        : Model && _get(Model, ['copiesById', id])\n\n      if (copy) {\n        const original =\n          copy[state.idField] != null\n            ? state.keyedById[id]\n            : state.tempsById[id]\n        mergeWithAccessors(copy, original)\n      }\n    },\n\n    // Deep assigns copy to original record, locally\n    commitCopy(state, id) {\n      const { servicePath, keepCopiesInStore } = state\n      const Model = _get(models, [\n        state.serverAlias,\n        'byServicePath',\n        servicePath\n      ])\n      const copy = keepCopiesInStore\n        ? state.copiesById[id]\n        : Model && _get(Model, ['copiesById', id])\n\n      if (copy) {\n        const original =\n          copy[state.idField] != null\n            ? state.keyedById[id]\n            : state.tempsById[id]\n        mergeWithAccessors(original, copy)\n      }\n    },\n\n    // Removes the copy from copiesById\n    clearCopy(state, id) {\n      const { keepCopiesInStore } = state\n      const Model = _get(models, [\n        state.serverAlias,\n        'byServicePath',\n        state.servicePath\n      ])\n\n      const copiesById = keepCopiesInStore ? state.copiesById : Model.copiesById\n\n      if (copiesById[id]) {\n        Vue.delete(copiesById, id)\n      }\n    },\n\n    /**\n     * Stores pagination data on state.pagination based on the query identifier\n     * (qid) The qid must be manually assigned to `params.qid`\n     */\n    updatePaginationForQuery(state, { qid, response, query = {} }) {\n      const { data, total } = response\n      const { idField } = state\n      const ids = data.map(i => i[idField])\n      const queriedAt = new Date().getTime()\n      const { queryId, queryParams, pageId, pageParams } = getQueryInfo(\n        { qid, query },\n        response\n      )\n\n      if (!state.pagination[qid]) {\n        Vue.set(state.pagination, qid, {})\n      }\n      if (!query.hasOwnProperty('$limit') && response.hasOwnProperty('limit')) {\n        Vue.set(state.pagination, 'defaultLimit', response.limit)\n      }\n      if (!query.hasOwnProperty('$skip') && response.hasOwnProperty('skip')) {\n        Vue.set(state.pagination, 'defaultSkip', response.skip)\n      }\n\n      const mostRecent = {\n        query,\n        queryId,\n        queryParams,\n        pageId,\n        pageParams,\n        queriedAt,\n        total\n      }\n\n      const qidData = state.pagination[qid] || {}\n      Object.assign(qidData, { mostRecent })\n      qidData[queryId] = qidData[queryId] || {}\n      const queryData = {\n        total,\n        queryParams\n      }\n      Object.assign(qidData[queryId], queryData)\n\n      const pageData = {\n        [pageId]: { pageParams, ids, queriedAt }\n      }\n      Object.assign(qidData[queryId], pageData)\n\n      const newState = Object.assign({}, state.pagination[qid], qidData)\n\n      Vue.set(state.pagination, qid, newState)\n    },\n\n    setPending(state, method: PendingServiceMethodName): void {\n      const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n      state[`is${uppercaseMethod}Pending`] = true\n    },\n    unsetPending(state, method: PendingServiceMethodName): void {\n      const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n      state[`is${uppercaseMethod}Pending`] = false\n    },\n\n    setIdPending(\n      state,\n      payload: { method: PendingIdServiceMethodName; id: Id | Id[] }\n    ): void {\n      const { method, id } = payload\n      const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n      const isIdMethodPending = state[\n        `isId${uppercaseMethod}Pending`\n      ] as ServiceState['isIdCreatePending']\n      // if `id` is an array, ensure it doesn't have duplicates\n      const ids = Array.isArray(id) ? [...new Set(id)] : [id]\n      ids.forEach(id => {\n        if (typeof id === 'number' || typeof id === 'string') {\n          isIdMethodPending.push(id)\n        }\n      })\n    },\n    unsetIdPending(\n      state,\n      payload: { method: PendingIdServiceMethodName; id: Id | Id[] }\n    ): void {\n      const { method, id } = payload\n      const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n      const isIdMethodPending = state[\n        `isId${uppercaseMethod}Pending`\n      ] as ServiceState['isIdCreatePending']\n      // if `id` is an array, ensure it doesn't have duplicates\n      const ids = Array.isArray(id) ? [...new Set(id)] : [id]\n      ids.forEach(id => {\n        const idx = isIdMethodPending.indexOf(id)\n        if (idx >= 0) {\n          Vue.delete(isIdMethodPending, idx)\n        }\n      })\n    },\n\n    setError(\n      state,\n      payload: { method: PendingServiceMethodName; error: Error }\n    ): void {\n      const { method, error } = payload\n      const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n      state[`errorOn${uppercaseMethod}`] = Object.assign(\n        {},\n        serializeError(error)\n      )\n    },\n    clearError(state, method: PendingServiceMethodName): void {\n      const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n      state[`errorOn${uppercaseMethod}`] = null\n    }\n  }\n}\n"
  },
  {
    "path": "src/service-module/service-module.state.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\n\nimport _omit from 'lodash/omit'\n\nimport { MakeServicePluginOptions, Model } from './types'\nimport { Id } from '@feathersjs/feathers'\n\nexport interface ServiceStateExclusiveDefaults {\n  ids: string[]\n\n  errorOnFind: any\n  errorOnGet: any\n  errorOnCreate: any\n  errorOnPatch: any\n  errorOnUpdate: any\n  errorOnRemove: any\n\n  isFindPending: boolean\n  isGetPending: boolean\n  isCreatePending: boolean\n  isPatchPending: boolean\n  isUpdatePending: boolean\n  isRemovePending: boolean\n\n  keyedById: {}\n  tempsById: {}\n  copiesById: {}\n  namespace?: string\n  pagination?: {\n    defaultLimit: number\n    defaultSkip: number\n    default?: PaginationState\n  }\n  paramsForServer: string[]\n  modelName?: string\n  debounceEventsTime: number\n  isIdCreatePending: Id[]\n  isIdUpdatePending: Id[]\n  isIdPatchPending: Id[]\n  isIdRemovePending: Id[]\n}\n\nexport interface ServiceState<M extends Model = Model> {\n  options: {}\n  ids: string[]\n  autoRemove: boolean\n  errorOnFind: any\n  errorOnGet: any\n  errorOnCreate: any\n  errorOnPatch: any\n  errorOnUpdate: any\n  errorOnRemove: any\n  isFindPending: boolean\n  isGetPending: boolean\n  isCreatePending: boolean\n  isPatchPending: boolean\n  isUpdatePending: boolean\n  isRemovePending: boolean\n  idField: string\n  tempIdField: string\n  keyedById: {\n    [k: string]: M\n    [k: number]: M\n  }\n  tempsById: {\n    [k: string]: M\n    [k: number]: M\n  }\n  copiesById: {\n    [k: string]: M\n  }\n  whitelist: string[]\n  paramsForServer: string[]\n  namespace: string\n  nameStyle: string // Should be enum of 'short' or 'path'\n  pagination?: {\n    defaultLimit: number\n    defaultSkip: number\n    default?: PaginationState\n  }\n  modelName?: string\n  debounceEventsTime: number\n  debounceEventsMaxWait: number\n  isIdCreatePending: Id[]\n  isIdUpdatePending: Id[]\n  isIdPatchPending: Id[]\n  isIdRemovePending: Id[]\n}\n\nexport interface PaginationState {\n  ids: any\n  limit: number\n  skip: number\n  ip: number\n  total: number\n  mostRecent: any\n}\n\nexport default function makeDefaultState(options: MakeServicePluginOptions) {\n  const nonStateProps = [\n    'Model',\n    'service',\n    'instanceDefaults',\n    'setupInstance',\n    'handleEvents',\n    'extend',\n    'state',\n    'getters',\n    'mutations',\n    'actions'\n  ]\n\n  const state: ServiceStateExclusiveDefaults = {\n    ids: [],\n    keyedById: {},\n    copiesById: {},\n    tempsById: {}, // Really should be called tempsByTempId\n    pagination: {\n      defaultLimit: null,\n      defaultSkip: null\n    },\n    paramsForServer: ['$populateParams'],\n    debounceEventsTime: null,\n\n    isFindPending: false,\n    isGetPending: false,\n    isCreatePending: false,\n    isUpdatePending: false,\n    isPatchPending: false,\n    isRemovePending: false,\n\n    errorOnFind: null,\n    errorOnGet: null,\n    errorOnCreate: null,\n    errorOnUpdate: null,\n    errorOnPatch: null,\n    errorOnRemove: null,\n\n    isIdCreatePending: [],\n    isIdUpdatePending: [],\n    isIdPatchPending: [],\n    isIdRemovePending: []\n  }\n\n  if (options.Model) {\n    state.modelName = options.Model.modelName\n  }\n\n  const startingState = _omit(options, nonStateProps)\n\n  return Object.assign({}, state, startingState)\n}\n"
  },
  {
    "path": "src/service-module/types.ts",
    "content": "import { Service, Id } from '@feathersjs/feathers'\nimport { Params, Paginated } from '../utils'\nimport { EventEmitter } from 'events'\nimport { Store } from 'vuex'\nimport { Ref } from '@vue/composition-api'\n\nexport { Id } from '@feathersjs/feathers'\n\n/*\neslint\n@typescript-eslint/no-explicit-any: 0\n*/\nexport interface FeathersVuexOptions {\n  serverAlias: string\n  addOnUpsert?: boolean\n  autoRemove?: boolean\n  debug?: boolean\n  enableEvents?: boolean\n  handleEvents?: HandleEvents\n  idField?: string\n  tempIdField?: string\n  keepCopiesInStore?: boolean\n  debounceEventsTime?: number\n  debounceEventsMaxWait?: number\n  nameStyle?: string\n  paramsForServer?: string[]\n  preferUpdate?: boolean\n  replaceItems?: boolean\n  skipRequestIfExists?: boolean\n  whitelist?: string[]\n}\n\nexport interface HandleEvents {\n  created?: Function\n  patched?: Function\n  updated?: Function\n  removed?: Function\n}\n\nexport interface ServicePluginExtendOptions {\n  store: Store<any>\n  module: any\n}\n\nexport interface MakeServicePluginOptions {\n  Model: any\n  service: Service<any>\n\n  idField?: string\n  tempIdField?: string\n\n  addOnUpsert?: boolean\n  autoRemove?: boolean\n  debug?: boolean\n  enableEvents?: boolean\n  preferUpdate?: boolean\n  replaceItems?: boolean\n  skipRequestIfExists?: boolean\n  nameStyle?: string\n  keepCopiesInStore?: boolean\n  debounceEventsTime?: number\n  debounceEventsMaxWait?: number\n\n  servicePath?: string\n  namespace?: string\n\n  whitelist?: string[]\n  paramsForServer?: string[]\n\n  instanceDefaults?: () => {}\n  setupInstance?: (data: any, { models, store }) => {}\n  handleEvents?: HandleEvents\n\n  extend?: (\n    options: ServicePluginExtendOptions\n  ) => {\n    state?: any\n    getters?: any\n    mutations?: any\n    actions?: any\n  }\n  state?: {}\n  getters?: {}\n  mutations?: {}\n  actions?: {}\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface FeathersVuexStoreState {\n  /** Allow clients to augment store state */\n}\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface FeathersVuexGlobalModels {\n  /** Allow clients to augment Global models */\n}\n\n// Alias and default to any if user doesn't augment interfaces\nexport type StoreState = keyof FeathersVuexStoreState extends never\n  ? any\n  : FeathersVuexStoreState\nexport type GlobalModels = keyof FeathersVuexGlobalModels extends never\n  ? any\n  : FeathersVuexGlobalModels\n\nexport interface PatchParams<D extends {} = AnyData> extends Params {\n  data?: Partial<D>\n}\n\nexport interface ModelSetupContext {\n  /**\n   * The global Vuex store\n   */\n  store: StoreState\n  /**\n   * The global `models` object\n   */\n  models: GlobalModels\n}\n\nexport interface ModelInstanceOptions {\n  /**\n   * Creating clone?\n   *\n   * Default: `false`\n   */\n  clone?: boolean\n  /**\n   * Add to store\n   *\n   * Default: `true`\n   */\n  commit?: boolean\n  /**\n   * Merge with existing?\n   *\n   * Default: `true`\n   */\n  merge?: boolean\n}\n\nexport type AnyData = { [key: string]: any }\n\n/** Static Model interface */\nexport interface ModelStatic extends EventEmitter {\n  /**\n   * The path passed to `FeathersClient.service()` to create the service\n   */\n  servicePath: string\n  /**\n   * Holds the value that was used to register the module with Vuex.\n   * This will match the servicePath unless you've provided a custom\n   * namespace in the Service Module plugin options.\n   */\n  namespace: string\n  /**\n   * The global Vuex store\n   */\n  readonly store: Store<StoreState>\n  /**\n   * The field in each record that will contain the ID\n   */\n  idField: string\n  /**\n   * The field in each temporary record that contains the temporary ID\n   */\n  tempIdField: string\n  /**\n   *  If `true`, calling `model.save()` will do an `update` instead of a `patch`.\n   */\n  preferUpdate: boolean\n  /**\n   * Server alias in the global `models` object\n   */\n  serverAlias: string\n  /**\n   * Model name used to circumvent Babel transpilation errors\n   */\n  modelName: string\n  /**\n   * The global `models` object\n   */\n  readonly models: GlobalModels\n  /**\n   * All model copies created using `model.clone()`\n   */\n  readonly copiesById: {\n    [key: string]: Model | undefined\n    [key: number]: Model | undefined\n  }\n\n  /**\n   * The BaseModel constructor calls mergeWithAccessors(this, newData).\n   * This utility function correctly copies data between both regular\n   * objects and Vue.observable instances. If you create a class where\n   * you need to do your own merging, you probably don't want\n   * mergeWithAccessors to run twice. In this case, you can use the\n   * `merge: false` BaseModel instance option to prevent the internal\n   * merge. You can then access the mergeWithAccessors method by calling\n   * this method like MyModel.merge(this, newData).\n   * @param dest destination object\n   * @param source source object\n   * @param blacklist keys to ignore when merging\n   * @example\n   * class Todo extends BaseModel {\n   *   public constructor(data, options?) {\n   *   options.merge = false // Prevent the internal merge\n   *   super(data, options)\n   *   // ... your custom constructor logic happens here.\n   *   // Call the static merge method to do your own merging.\n   *   Todo.merge(this, data)\n   *   }\n   * }\n   */\n  merge(dest: unknown, source: unknown, blacklist?: string[]): void\n\n  /**\n   * Create new Model\n   * @param data partial model data\n   * @param options model instance options\n   */\n  new (data?: AnyData, options?: ModelInstanceOptions): Model\n  prototype: Model\n\n  /**\n   * The instanceDefaults API was created in version 1.7 to prevent\n   * requiring to specify data for new instances created throughout\n   * the app. Depending on the complexity of the service's \"business\n   * logic\", it can save a lot of boilerplate. Notice that it is\n   * similar to the setupInstance method added in 2.0. The instanceDefaults\n   * method should ONLY be used to return default values for a new\n   * instance. Use setupInstance to handle other transformations on\n   * the data.\n   * @param data the instance data\n   * @param ctx setup context\n   */\n  instanceDefaults(data: AnyData, ctx: ModelSetupContext): AnyData\n\n  /**\n   * A new setupInstance class method is now available in version 2.0.\n   * This method allows you to transform the data and setup the final\n   * instance based on incoming data. For example, you can access the\n   * models object to reference other service Model classes and create\n   * data associations.\n   * @param data the instance data\n   * @param ctx setup context\n   */\n  setupInstance(data: AnyData, ctx: ModelSetupContext): AnyData\n\n  /**\n   * Gets called just before sending the data to the API server. It gets\n   * called with the data and must return the diffed data.\n   *\n   * Default: `data => data`\n   * @param data the instance data\n   */\n  diffOnPatch(data: AnyData): AnyData\n\n  /**\n   * A proxy for the `find` action\n   * @param params Find params\n   */\n  find<M extends Model = Model>(params?: Params): Promise<M[] | Paginated<M>>\n  /**\n   * A proxy for the `find` getter\n   * @param params Find params\n   */\n  findInStore<M extends Model = Model>(\n    params?: Params | Ref<Params>\n  ): Paginated<M>\n\n  /**\n   * A proxy for the `count` action\n   * @param params Find params\n   */\n  count(params?: Params): Promise<number>\n  /**\n   * A proxy for the `count` getter\n   * @param params Find params\n   */\n  countInStore(params?: Params | Ref<Params>): number\n\n  /**\n   * A proxy for the `get` action\n   * @param id ID of record to retrieve\n   * @param params Get params\n   */\n  get<M extends Model = Model>(id: Id, params?: Params): Promise<M | undefined>\n  /**\n   * A proxy for the `get` getter\n   * @param id ID of record to retrieve\n   * @param params Get params\n   */\n  getFromStore<M extends Model = Model>(\n    id: Id | Ref<Id>,\n    params?: Params | Ref<Params>\n  ): M | undefined\n}\n\n/** Model instance interface */\nexport interface Model {\n  [key: string]: any\n  /**\n   * model's temporary ID\n   */\n  readonly __id?: string\n  /**\n   * model is temporary?\n   */\n  readonly __isTemp?: boolean\n  /**\n   * model is a clone?\n   */\n  readonly __isClone?: boolean\n\n  /**\n   * `Create` is currently pending on this model\n   */\n  readonly isCreatePending: boolean\n  /**\n   * `Update` is currently pending on this model\n   */\n  readonly isUpdatePending: boolean\n  /**\n   * `Patch` is currently pending on this model\n   */\n  readonly isPatchPending: boolean\n  /**\n   * `Remove` is currently pending on this model\n   */\n  readonly isRemovePending: boolean\n  /**\n   * Any of `create`, `update` or `patch` is currently pending on this model\n   */\n  readonly isSavePending: boolean\n  /**\n   * Any method is currently pending on this model\n   */\n  readonly isPending: boolean\n\n  /**\n   * Creates a deep copy of the record and stores it on\n   * `Model.copiesById`. This allows you to make changes\n   * to the clone and not update visible data until you\n   * commit or save the data.\n   * @param data Properties to modify on the cloned instance\n   */\n  clone(data?: AnyData): this\n  /**\n   * The create method calls the create action (service method)\n   * using the instance data.\n   * @param params Params passed to the Feathers client request\n   */\n  create(params?: Params): Promise<this>\n  /**\n   * The patch method calls the patch action (service method)\n   * using the instance data. The instance's id field is used\n   * for the patch id.\n   *\n   * You can provide an object as `params.data`, and Feathers-Vuex\n   * will use `params.data` as the patch data. This allows patching\n   * with partial data.\n   * @param params Params passed to the Feathers client request\n   */\n  patch<D extends {} = AnyData>(params?: PatchParams<D>): Promise<this>\n  /**\n   * The remove method calls the remove action (service method)\n   * using the instance data. The instance's id field is used\n   * for the remove id.\n   * @param params Params passed to the Feathers client request\n   */\n  remove(params?: Params): Promise<this>\n  /**\n   * The update method calls the update action (service method)\n   * using the instance data. The instance's id field is used for\n   * the update id.\n   * @param params Params passed to the Feathers client request\n   */\n  update(params?: Params): Promise<this>\n  /**\n   * The save method is a convenience wrapper for the create/patch\n   * methods, by default. If the records has no _id, the\n   * instance.create() method will be used.\n   * @param params Params passed to the Feathers client request\n   */\n  save(params?: Params): Promise<this>\n\n  /**\n   * Commit changes from clone to original\n   */\n  commit(): this\n\n  /**\n   * Discards changes made on this clone and syncs with the original\n   */\n  reset(): this\n}\n"
  },
  {
    "path": "src/useFind.ts",
    "content": "/*\neslint\n@typescript-eslint/no-explicit-any: 0\n*/\nimport {\n  computed,\n  isRef,\n  reactive,\n  Ref,\n  toRefs,\n  watch\n} from '@vue/composition-api'\nimport debounce from 'lodash/debounce'\nimport { getItemsFromQueryInfo, getQueryInfo, Params, Paginated } from './utils'\nimport { ModelStatic, Model } from './service-module/types'\n\ninterface UseFindOptions {\n  model: ModelStatic\n  params: Params | Ref<Params>\n  fetchParams?: Params | Ref<Params>\n  queryWhen?: Ref<boolean>\n  qid?: string\n  local?: boolean\n  immediate?: boolean\n}\ninterface UseFindState {\n  debounceTime: null | number\n  qid: string\n  isPending: boolean\n  haveBeenRequested: boolean\n  haveLoaded: boolean\n  error: null | Error\n  latestQuery: null | object\n  isLocal: boolean\n}\ninterface UseFindData<M> {\n  items: Ref<Readonly<M[]>>\n  servicePath: Ref<string>\n  isPending: Ref<boolean>\n  haveBeenRequested: Ref<boolean>\n  haveLoaded: Ref<boolean>\n  isLocal: Ref<boolean>\n  qid: Ref<string>\n  debounceTime: Ref<number>\n  latestQuery: Ref<object>\n  paginationData: Ref<object>\n  error: Ref<Error>\n  find(params?: Params | Ref<Params>): Promise<M[] | Paginated<M>>\n}\n\nconst unwrapParams = (params: Params | Ref<Params>): Params =>\n  isRef(params) ? params.value : params\n\nexport default function find<M extends Model = Model>(options: UseFindOptions): UseFindData<M> {\n  const defaults: UseFindOptions = {\n    model: null,\n    params: null,\n    qid: 'default',\n    queryWhen: computed((): boolean => true),\n    local: false,\n    immediate: true\n  }\n  const { model, params, queryWhen, qid, local, immediate } = Object.assign(\n    {},\n    defaults,\n    options\n  )\n\n  if (!model) {\n    throw new Error(\n      `No model provided for useFind(). Did you define and register it with FeathersVuex?`\n    )\n  }\n\n  const getFetchParams = (providedParams?: Params | Ref<Params>): Params => {\n    const provided = unwrapParams(providedParams)\n\n    if (provided) {\n      return provided\n    }\n\n    const fetchParams = unwrapParams(options.fetchParams)\n    // Returning null fetchParams allows the query to be skipped.\n    if (fetchParams || fetchParams === null) {\n      return fetchParams\n    }\n\n    const params = unwrapParams(options.params)\n    return params\n  }\n\n  const state = reactive<UseFindState>({\n    qid,\n    isPending: false,\n    haveBeenRequested: false,\n    haveLoaded: local,\n    error: null,\n    debounceTime: null,\n    latestQuery: null,\n    isLocal: local\n  })\n  const computes = {\n    // The find getter\n    items: computed<M[]>(() => {\n      const getterParams = unwrapParams(params)\n\n      if (getterParams) {\n        if (getterParams.paginate) {\n          const serviceState = model.store.state[model.servicePath]\n          const { defaultSkip, defaultLimit } = serviceState.pagination\n          const skip = getterParams.query.$skip || defaultSkip\n          const limit = getterParams.query.$limit || defaultLimit\n          const pagination =\n            computes.paginationData.value[getterParams.qid || state.qid] || {}\n          const response = skip != null && limit != null ? { limit, skip } : {}\n          const queryInfo = getQueryInfo(getterParams, response)\n          const items = getItemsFromQueryInfo(\n            pagination,\n            queryInfo,\n            serviceState.keyedById\n          )\n          return items\n        } else {\n          return model.findInStore(getterParams).data\n        }\n      } else {\n        return []\n      }\n    }),\n    paginationData: computed(() => {\n      return model.store.state[model.servicePath].pagination\n    }),\n    servicePath: computed<string>(() => model.servicePath)\n  }\n\n  function find(params?: Params | Ref<Params>): Promise<M[] | Paginated<M>> {\n    params = unwrapParams(params)\n    if (queryWhen.value && !state.isLocal) {\n      state.isPending = true\n      state.haveBeenRequested = true\n\n      return model.find<M>(params).then(response => {\n        // To prevent thrashing, only clear error on response, not on initial request.\n        state.error = null\n        state.haveLoaded = true\n        if(!Array.isArray(response)) {\n          const queryInfo = getQueryInfo(params, response)\n          queryInfo.response = response\n          queryInfo.isOutdated = false\n          state.latestQuery = queryInfo\n        }\n        state.isPending = false\n        return response\n      })\n    }\n  }\n  const methods = {\n    findDebounced(params?: Params) {\n      return find(params)\n    }\n  }\n  function findProxy(params?: Params | Ref<Params>) {\n    const paramsToUse = getFetchParams(params)\n\n    if (paramsToUse && paramsToUse.debounce) {\n      if (paramsToUse.debounce !== state.debounceTime) {\n        methods.findDebounced = debounce(find, paramsToUse.debounce)\n        state.debounceTime = paramsToUse.debounce\n      }\n      return methods.findDebounced(paramsToUse)\n    } else if (paramsToUse) {\n      return find(paramsToUse)\n    } else {\n      // Set error\n    }\n  }\n\n  watch(\n    () => getFetchParams(),\n    () => {\n      findProxy()\n    },\n    { immediate }\n  )\n\n  return {\n    ...computes,\n    ...toRefs(state),\n    find\n  }\n}\n"
  },
  {
    "path": "src/useGet.ts",
    "content": "/*\neslint\n@typescript-eslint/no-explicit-any: 0\n*/\nimport {\n  reactive,\n  computed,\n  toRefs,\n  isRef,\n  watch,\n  Ref\n} from '@vue/composition-api'\nimport { Params } from './utils'\nimport { ModelStatic, Model, Id } from './service-module/types'\n\ninterface UseGetOptions {\n  model: ModelStatic\n  id: null | string | number | Ref<null> | Ref<string> | Ref<number>\n  params?: Params | Ref<Params>\n  queryWhen?: Ref<boolean>\n  local?: boolean\n  immediate?: boolean\n}\ninterface UseGetState {\n  isPending: boolean\n  hasBeenRequested: boolean\n  hasLoaded: boolean\n  error: null | Error\n  isLocal: boolean\n}\ninterface UseGetData<M> {\n  item: Ref<Readonly<M | null>>\n  servicePath: Ref<string>\n  isPending: Ref<boolean>\n  hasBeenRequested: Ref<boolean>\n  hasLoaded: Ref<boolean>\n  isLocal: Ref<boolean>\n  error: Ref<Error>\n  get(id: Id, params?: Params): Promise<M | undefined>\n}\n\nexport default function get<M extends Model = Model>(options: UseGetOptions): UseGetData<M> {\n  const defaults: UseGetOptions = {\n    model: null,\n    id: null,\n    params: null,\n    queryWhen: computed((): boolean => true),\n    local: false,\n    immediate: true\n  }\n  const { model, id, params, queryWhen, local, immediate } = Object.assign(\n    {},\n    defaults,\n    options\n  )\n\n  if (!model) {\n    throw new Error(\n      `No model provided for useGet(). Did you define and register it with FeathersVuex?`\n    )\n  }\n\n  function getId(): null | string | number {\n    return isRef(id) ? id.value : id || null\n  }\n  function getParams(): Params {\n    return isRef(params) ? params.value : params\n  }\n\n  const state = reactive<UseGetState>({\n    isPending: false,\n    hasBeenRequested: false,\n    hasLoaded: false,\n    error: null,\n    isLocal: local\n  })\n\n  const computes = {\n    item: computed(() => {\n      const getterId = isRef(id) ? id.value : id\n      const getterParams = isRef(params)\n        ? Object.assign({}, params.value)\n        : params == null\n        ? params\n        : { ...params }\n      if (getterParams != null) {\n        return model.getFromStore<M>(getterId, getterParams) || null\n      } else {\n        return model.getFromStore<M>(getterId) || null\n      }\n    }),\n    servicePath: computed(() => model.servicePath)\n  }\n\n\n\n  function get(id: Id, params?: Params): Promise<M | undefined> {\n    const idToUse = isRef<Id>(id) ? id.value : id\n    const paramsToUse = isRef(params) ? params.value : params\n\n    if (idToUse != null && queryWhen.value && !state.isLocal) {\n      state.isPending = true\n      state.hasBeenRequested = true\n\n      const promise =\n        paramsToUse != null\n          ? model.get(idToUse, paramsToUse)\n          : model.get(idToUse)\n\n      return promise\n        .then(response => {\n          state.isPending = false\n          state.hasLoaded = true\n          return response\n        })\n        .catch(error => {\n          state.isPending = false\n          state.error = error\n          return error\n        })\n    } else {\n      return Promise.resolve(undefined)\n    }\n  }\n\n  watch(\n    [() => getId(), () => getParams()],\n    ([id, params]) => {\n      get(id as string | number, params as Params)\n    },\n    { immediate }\n  )\n\n  return {\n    ...toRefs(state),\n    ...computes,\n    get\n  }\n}\n"
  },
  {
    "path": "src/utils.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport decode from 'jwt-decode'\nimport inflection from 'inflection'\nimport Vue from 'vue'\nimport fastCopy from 'fast-copy'\nimport _isObject from 'lodash/isObject'\nimport _trim from 'lodash/trim'\nimport _omit from 'lodash/omit'\nimport ObjectID from 'bson-objectid'\nimport { globalModels as models } from './service-module/global-models'\nimport stringify from 'fast-json-stable-stringify'\nimport { Service } from '@feathersjs/feathers'\n\ninterface Query {\n  [key: string]: any\n}\ninterface PaginationOptions {\n  default: number\n  max: number\n}\ninterface Params {\n  query?: Query\n  paginate?: false | Pick<PaginationOptions, 'max'>\n  provider?: string\n  route?: { [key: string]: string }\n  headers?: { [key: string]: any }\n  temps?: boolean\n  copies?: boolean\n\n  [key: string]: any // (JL) not sure if we want this\n}\ninterface Paginated<T> {\n  total: number\n  limit: number\n  skip: number\n  data: T[]\n}\n\nexport { Query, PaginationOptions, Params, Paginated }\n\nexport function stripSlashes(location: string) {\n  return _trim(location, '/')\n}\n\nexport function setByDot(obj, path, value, ifDelete?) {\n  if (ifDelete) {\n    // eslint-disable-next-line no-console\n    console.log(\n      'DEPRECATED. Use deleteByDot instead of setByDot(obj,path,value,true). (setByDot)'\n    )\n  }\n\n  if (path.indexOf('.') === -1) {\n    obj[path] = value\n\n    if (value === undefined && ifDelete) {\n      delete obj[path]\n    }\n\n    return\n  }\n\n  const parts = path.split('.')\n  const lastIndex = parts.length - 1\n  return parts.reduce((obj1, part, i) => {\n    if (i !== lastIndex) {\n      if (!obj1.hasOwnProperty(part) || typeof obj1[part] !== 'object') {\n        obj1[part] = {}\n      }\n      return obj1[part]\n    }\n\n    obj1[part] = value\n    if (value === undefined && ifDelete) {\n      delete obj1[part]\n    }\n    return obj1\n  }, obj)\n}\n\nexport function upperCaseFirst(string) {\n  return string.charAt(0).toUpperCase() + string.slice(1)\n}\n\nexport function getShortName(service) {\n  let namespace = stripSlashes(service)\n  if (Array.isArray(namespace)) {\n    namespace = namespace.slice(-1)\n  } else if (namespace.includes('/')) {\n    namespace = namespace.slice(namespace.lastIndexOf('/') + 1)\n  }\n  return namespace\n}\n\nexport function getNameFromPath(service) {\n  return stripSlashes(service)\n}\n\n// Reads and returns the contents of a cookie with the provided name.\nexport function readCookie(cookies, name) {\n  if (!cookies) {\n    return undefined\n  }\n  const nameEQ = name + '='\n  const ca = cookies.split(';')\n  for (let i = 0; i < ca.length; i++) {\n    let c = ca[i]\n    while (c.charAt(0) === ' ') {\n      c = c.substring(1, c.length)\n    }\n    if (c.indexOf(nameEQ) === 0) {\n      return c.substring(nameEQ.length, c.length)\n    }\n  }\n  return null\n}\n\n// Pass a decoded payload and it will return a boolean based on if it hasn't expired.\nexport function payloadIsValid(payload) {\n  return payload && payload.exp * 1000 > new Date().getTime()\n}\n\n// from https://github.com/iliakan/detect-node\nexport const isNode =\n  Object.prototype.toString.call(\n    typeof process !== 'undefined' ? process : 0\n  ) === '[object process]'\n\nexport const isBrowser = !isNode\n\nconst authDefaults = {\n  commit: undefined,\n  req: undefined,\n  moduleName: 'auth',\n  cookieName: 'feathers-jwt'\n}\n\nexport function getValidPayloadFromToken(token) {\n  if (token) {\n    try {\n      const payload = decode(token)\n      return payloadIsValid(payload) ? payload : undefined\n    } catch (error) {\n      return undefined\n    }\n  }\n  return undefined\n}\n\nexport const initAuth = function initAuth(options) {\n  const { commit, req, moduleName, cookieName, feathersClient } = Object.assign(\n    {},\n    authDefaults,\n    options\n  )\n\n  if (typeof commit !== 'function') {\n    throw new Error(\n      'You must pass the `commit` function in the `initAuth` function options.'\n    )\n  }\n  let cookies\n  if (req) {\n    cookies = req.headers.cookie\n  } else if (document && document.cookie) {\n    cookies = document.cookie\n  } else {\n    throw new Error(\n      'You must pass the `req` object in the `initAuth` function options.'\n    )\n  }\n\n  const accessToken = readCookie(cookies, cookieName)\n  const payload = getValidPayloadFromToken(accessToken)\n\n  if (payload) {\n    commit(`${moduleName}/setAccessToken`, accessToken)\n    commit(`${moduleName}/setPayload`, payload)\n    if (feathersClient) {\n      return feathersClient.authentication\n        .setAccessToken(accessToken)\n        .then(() => payload)\n    }\n  }\n  return Promise.resolve(payload)\n}\n\n/**\n * run de BaseModel hydration on client for each api\n */\nexport const hydrateApi = function hydrateApi({ api }) {\n  Object.keys(api).forEach(modelName => {\n    if (!['byServicePath', 'BaseModel'].includes(modelName)) {\n      const Model = api[modelName]\n      Model.hydrateAll()\n    }\n  })\n}\n\n/**\n * Generate a new tempId and mark the record as a temp\n * @param state\n * @param item\n */\nexport function assignTempId(state, item) {\n  const { debug, tempIdField } = state\n  if (debug) {\n    // eslint-disable-next-line no-console\n    console.info('assigning temporary id to item', item)\n  }\n  const newId = new ObjectID().toHexString()\n  item[tempIdField] = newId\n  return newId\n}\n\nfunction stringifyIfObject(val): string | any {\n  if (typeof val === 'object' && val != null) {\n    return val.toString()\n  }\n  return val\n}\n\n/**\n * Get the id from a record in this order:\n *   1. the `idField`\n *   2. id\n *   3. _id\n * @param item\n * @param idField\n */\nexport function getId(item, idField?) {\n  if (!item) {\n    return\n  }\n  if (item[idField] != null || item.hasOwnProperty(idField)) {\n    return stringifyIfObject(item[idField])\n  }\n  if (item.id != null || item.hasOwnProperty('id')) {\n    return stringifyIfObject(item.id)\n  }\n  if (item._id != null || item.hasOwnProperty('_id')) {\n    return stringifyIfObject(item._id)\n  }\n}\n\n// Creates a Model class name from the last part of the servicePath\nexport function getModelName(Model) {\n  // If the Model.name has been customized, use it.\n  if (Model.modelName) {\n    return Model.modelName\n  }\n\n  // Otherwise, use an inflection of the last bit of the servicePath\n  const parts = Model.servicePath.split('/')\n  let name = parts[parts.length - 1]\n  name = inflection.titleize(name)\n  name = name.split('-').join('')\n  name = inflection.singularize(name)\n  return name\n}\n\nexport function registerModel(Model, globalModels, apiPrefix, servicePath) {\n  const modelName = getModelName(Model)\n  const path = apiPrefix ? `${apiPrefix}.${modelName}` : modelName\n\n  setByDot(globalModels, path, Model)\n  globalModels.byServicePath[servicePath] = Model\n  return {\n    path,\n    name: modelName\n  }\n}\n\nexport function getServicePrefix(servicePath) {\n  const parts = servicePath.split('/')\n  let name = parts[parts.length - 1]\n  // name = inflection.underscore(name)\n  name = name.replace(/-/g, '_')\n  name = inflection.camelize(name, true)\n  return name\n}\n\nexport function getServiceCapitalization(servicePath) {\n  const parts = servicePath.split('/')\n  let name = parts[parts.length - 1]\n  // name = inflection.underscore(name)\n  name = name.replace(/-/g, '_')\n  name = inflection.camelize(name)\n  return name\n}\n\nexport function updateOriginal(original, newData) {\n  Object.keys(newData).forEach(key => {\n    const newProp = newData[key]\n    const oldProp = original[key]\n    let shouldCopyProp = false\n\n    if (newProp === oldProp) {\n      return\n    }\n\n    // If the old item doesn't already have this property, update it\n    if (!original.hasOwnProperty(key)) {\n      shouldCopyProp = true\n      // If the old prop is null or undefined, and the new prop is neither\n    } else if (\n      (oldProp === null || oldProp === undefined) &&\n      newProp !== null &&\n      newProp !== undefined\n    ) {\n      shouldCopyProp = true\n      // If both old and new are arrays\n    } else if (Array.isArray(oldProp) && Array.isArray(newProp)) {\n      shouldCopyProp = true\n    } else if (_isObject(oldProp)) {\n      shouldCopyProp = true\n    } else if (\n      oldProp !== newProp &&\n      !Array.isArray(oldProp) &&\n      !Array.isArray(newProp)\n    ) {\n      shouldCopyProp = true\n    }\n\n    if (shouldCopyProp) {\n      if (original.hasOwnProperty(key)) {\n        original[key] = newProp\n      } else {\n        Vue.set(original, key, newProp)\n      }\n    }\n  })\n}\n\nexport function getQueryInfo(\n  params: Params = {},\n  response: Partial<Pick<Paginated<any>, 'limit' | 'skip'>> = {}\n) {\n  const query = params.query || {}\n  const qid: string = params.qid || 'default'\n  const $limit =\n    response.limit !== null && response.limit !== undefined\n      ? response.limit\n      : query.$limit\n  const $skip =\n    response.skip !== null && response.skip !== undefined\n      ? response.skip\n      : query.$skip\n\n  const queryParams = _omit(query, ['$limit', '$skip'])\n  const queryId = stringify(queryParams)\n  const pageParams = $limit !== undefined ? { $limit, $skip } : undefined\n  const pageId = pageParams ? stringify(pageParams) : undefined\n\n  return {\n    qid,\n    query,\n    queryId,\n    queryParams,\n    pageParams,\n    pageId,\n    response: undefined,\n    isOutdated: undefined as boolean | undefined\n  }\n}\n\nexport function getItemsFromQueryInfo(pagination, queryInfo, keyedById) {\n  const { queryId, pageId } = queryInfo\n  const queryLevel = pagination[queryId]\n  const pageLevel = queryLevel && queryLevel[pageId]\n  const ids = pageLevel && pageLevel.ids\n\n  if (ids && ids.length) {\n    return ids.map(id => keyedById[id])\n  } else {\n    return []\n  }\n}\n\nexport function makeNamespace(namespace, servicePath, nameStyle) {\n  const nameStyles = {\n    short: getShortName,\n    path: getNameFromPath\n  }\n  return namespace || nameStyles[nameStyle](servicePath)\n}\n\n/**\n * Gets the service path or name from the service.  The modelname is provided\n * to allow easier discovery if there's a problem.\n * @param service\n * @param modelName\n */\nexport function getServicePath(service: Service<any>, Model: any) {\n  if (!service.name && !service.path && !Model.servicePath) {\n    throw new Error(\n      `Service for model named ${Model.name} is missing a path or name property. The feathers adapter needs to be updated with a PR to expose this property. You can work around this by adding a static servicePath =  passing a 'servicePath' attribute in the options: makeServicePlugin({servicePath: '/path/to/my/service'})`\n    )\n  }\n\n  return service.path || service.name || Model.servicePath\n}\n\nexport function randomString(length) {\n  let text = ''\n  const possible =\n    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\n\n  for (let i = 0; i < length; i++) {\n    text += possible.charAt(Math.floor(Math.random() * possible.length))\n  }\n\n  return text\n}\n\nexport function createRelatedInstance({ item, Model, idField, store }) {\n  // Create store instances (if data contains an idField)\n  const model = new Model(item)\n  const id = getId(model, idField)\n  const storedModel = store.state[model.constructor.namespace].keyedById[id]\n\n  return { model, storedModel }\n}\n\nexport function isBaseModelInstance(item) {\n  const baseModels = Object.keys(models).map(alias => models[alias].BaseModel)\n  return !!baseModels.find(BaseModel => {\n    return item instanceof BaseModel\n  })\n}\n\nexport function mergeWithAccessors(\n  dest,\n  source,\n  blacklist = ['__isClone', '__ob__']\n) {\n  const sourceProps = Object.getOwnPropertyNames(source)\n  const destProps = Object.getOwnPropertyNames(dest)\n  const sourceIsVueObservable = sourceProps.includes('__ob__')\n  const destIsVueObservable = destProps.includes('__ob__')\n  sourceProps.forEach(key => {\n    const sourceDesc = Object.getOwnPropertyDescriptor(source, key)\n    const destDesc = Object.getOwnPropertyDescriptor(dest, key)\n\n    // if (Array.isArray(source[key]) && source[key].find(i => i.__ob__)) {\n    //   sourceIsVueObservable = true\n    // }\n    // if (Array.isArray(dest[key]) && dest[key].find(i => i.__ob__)) {\n    //   destIsVueObservable = true\n    // }\n\n    // This might have to be uncommented, but we'll try it this way, for now.\n    // if (!sourceDesc.enumerable) {\n    //   return\n    // }\n\n    // If the destination is not writable, return. Also ignore blacklisted keys.\n    // Must explicitly check if writable is false\n    if ((destDesc && destDesc.writable === false) || blacklist.includes(key)) {\n      return\n    }\n\n    // Handle Vue observable objects\n    if (destIsVueObservable || sourceIsVueObservable) {\n      const isObject = _isObject(source[key])\n      const isFeathersVuexInstance =\n        isObject &&\n        !!(\n          source[key].constructor.modelName || source[key].constructor.namespace\n        )\n      // Do not use fastCopy directly on a feathers-vuex BaseModel instance to keep from breaking reactivity.\n      if (isObject && !isFeathersVuexInstance) {\n        try {\n          dest[key] = fastCopy(source[key])\n        } catch (err) {\n          if (!err.message.includes('getter')) {\n            throw err\n          }\n        }\n      } else {\n        try {\n          dest[key] = source[key]\n        } catch (err) {\n          if (!err.message.includes('getter')) {\n            throw err\n          }\n        }\n      }\n      return\n    }\n\n    // Handle defining accessors\n    if (\n      typeof sourceDesc.get === 'function' ||\n      typeof sourceDesc.set === 'function'\n    ) {\n      Object.defineProperty(dest, key, sourceDesc)\n      return\n    }\n\n    // Do not attempt to overwrite a getter in the dest object\n    if (destDesc && typeof destDesc.get === 'function') {\n      return\n    }\n\n    // Assign values\n    // Do not allow sharing of deeply-nested objects between instances\n    // Potentially breaks accessors on nested data. Needs recursion if this is an issue\n    let value\n    if (_isObject(sourceDesc.value) && !isBaseModelInstance(sourceDesc.value)) {\n      value = fastCopy(sourceDesc.value)\n    }\n    dest[key] = value || sourceDesc.value\n  })\n  return dest\n}\n\nexport function checkNamespace(namespace, item, debug) {\n  if (!namespace && debug) {\n    // eslint-disable-next-line no-console\n    console.error(\n      'A `namespace` was not available on the Model for this item:',\n      item,\n      'this can be caused by not passing the Model into the makeServicePlugin function'\n    )\n  }\n  return namespace !== null && namespace !== undefined\n}\n\nexport function assignIfNotPresent(Model, props): void {\n  for (const key in props) {\n    if (!Model.hasOwnProperty(key)) {\n      Model[key] = props[key]\n    }\n  }\n}\n"
  },
  {
    "path": "src/vue-plugin/vue-plugin.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport FeathersVuexFind from '../FeathersVuexFind'\nimport FeathersVuexGet from '../FeathersVuexGet'\nimport FeathersVuexFormWrapper from '../FeathersVuexFormWrapper'\nimport FeathersVuexInputWrapper from '../FeathersVuexInputWrapper'\nimport FeathersVuexPagination from '../FeathersVuexPagination'\nimport FeathersVuexCount from '../FeathersVuexCount'\nimport { globalModels } from '../service-module/global-models'\nimport { GlobalModels } from '../service-module/types'\n\n// Augment global models onto VueConstructor and instance\ndeclare module 'vue/types/vue' {\n  interface VueConstructor {\n    $FeathersVuex: GlobalModels\n  }\n  interface Vue {\n    $FeathersVuex: GlobalModels\n  }\n}\n\nexport const FeathersVuex = {\n  install(Vue, options = { components: true }) {\n    const shouldSetupComponents = options.components !== false\n\n    Vue.$FeathersVuex = globalModels\n    Vue.prototype.$FeathersVuex = globalModels\n\n    if (shouldSetupComponents) {\n      Vue.component('FeathersVuexFind', FeathersVuexFind)\n      Vue.component('FeathersVuexGet', FeathersVuexGet)\n      Vue.component('FeathersVuexFormWrapper', FeathersVuexFormWrapper)\n      Vue.component('FeathersVuexInputWrapper', FeathersVuexInputWrapper)\n      Vue.component('FeathersVuexPagination', FeathersVuexPagination)\n      Vue.component('FeathersVuexCount', FeathersVuexCount)\n    }\n  }\n}\n"
  },
  {
    "path": "stories/.npmignore",
    "content": "*.stories.js"
  },
  {
    "path": "stories/FeathersVuexFormWrapper.stories.js",
    "content": "/* eslint-disable @typescript-eslint/explicit-function-return-type */\nimport '../../assets/styles/tailwind.postcss'\n\nimport FeathersVuexFormWrapper from '../src/FeathersVuexFormWrapper'\nimport Readme from './README.md'\n\nimport store from '../../store/store.dev'\nimport { models } from 'feathers-vuex'\n\nexport default {\n  title: 'FeathersVuexFormWrapper',\n  parameters: {\n    component: FeathersVuexFormWrapper,\n    readme: {\n      sidebar: Readme\n    }\n  }\n}\n\nexport const Basic = () => ({\n  components: { FeathersVuexFormWrapper },\n  data: () => ({\n    date: null,\n    UserModel: models.api.User\n  }),\n  store,\n  template: `<div class=\"p-3\">\n  <FeathersVuexFormWrapper\n    :id=\"'new'\"\n    :model=\"UserModel\"\n  >\n    <template v-slot=\"{ clone, isNew, isDirty, save, reset, remove }\">\n      <FormComponent\n        :item=\"clone\"\n        :is-new=\"isNew\"\n        :is-dirty=\"isDirty\"\n        @save=\"save\"\n        @reset=\"reset\"\n        @remove=\"remove\"\n      >\n      </FormComponent>\n    </template>\n  </FeathersVuexFormWrapper>\n</div>`\n})\n"
  },
  {
    "path": "stories/FeathersVuexInputWrapper.stories.js",
    "content": "/* eslint-disable @typescript-eslint/explicit-function-return-type */\nimport FeathersVuexInputWrapper from '../src/FeathersVuexInputWrapper.vue'\nimport { makeModel } from '@rovit/test-model'\n\nconst User = makeModel()\n\nconst user = new User({\n  _id: 1,\n  email: 'marshall@rovit.com',\n  carColor: '#FFF'\n})\n\nexport default {\n  title: 'FeathersVuexInputWrapper',\n  component: FeathersVuexInputWrapper\n}\n\nexport const basic = () => ({\n  components: {\n    FeathersVuexInputWrapper\n  },\n  data: () => ({\n    user\n  }),\n  methods: {\n    save({ clone, data }) {\n      const user = clone.commit()\n      user.patch(data)\n    }\n  },\n  template: `\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @focus=\"createClone\"\n          @blur=\"e => handler(e, save)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n  `\n})\n\nexport const handlerAsPromise = () => ({\n  components: {\n    FeathersVuexInputWrapper\n  },\n  data: () => ({\n    user\n  }),\n  methods: {\n    async save({ clone, data }) {\n      const user = clone.commit()\n      return user.patch(data)\n    }\n  },\n  template: `\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @focus=\"createClone\"\n          @blur=\"e => handler(e, save)\"\n          class=\"bg-gray-200 rounded\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n  `\n})\n\nexport const multipleOnDistinctProperties = () => ({\n  components: {\n    FeathersVuexInputWrapper\n  },\n  data: () => ({\n    user\n  }),\n  methods: {\n    async save({ event, clone, prop, data }) {\n      const user = clone.commit()\n      return user.patch(data)\n    }\n  },\n  template: `\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"text\"\n          @focus=\"createClone\"\n          @blur=\"e => handler(e, save)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"carColor\">\n      <template #default=\"{ current, prop, createClone, handler }\">\n        <input\n          v-model=\"current[prop]\"\n          type=\"color\"\n          @click=\"createClone\"\n          @change=\"e => handler(e, save)\"\n        />\n      </template>\n    </FeathersVuexInputWrapper>\n\n    <pre class=\"bg-black text-white text-xs mt-2 p-1\">{{user}}</pre>\n  </div>\n  `\n})\n\nexport const noInputInSlot = () => ({\n  components: {\n    FeathersVuexInputWrapper\n  },\n  data: () => ({\n    user\n  }),\n  methods: {\n    async save({ clone, data }) {\n      const user = clone.commit()\n      user.patch(data)\n    }\n  },\n  template: `\n  <div class=\"p-3\">\n    <FeathersVuexInputWrapper :item=\"user\" prop=\"email\" />\n  </div>\n  `\n})\n"
  },
  {
    "path": "test/auth-module/actions.test.js",
    "content": "import assert from 'chai/chai'\nimport setupVuexAuth from '~/src/auth-module/auth-module'\nimport setupVuexService from '~/src/service-module/service-module'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport Vuex, { mapActions } from 'vuex'\nimport memory from 'feathers-memory'\n\nconst options = {}\nconst globalModels = {}\n\nconst auth = setupVuexAuth(feathersClient, options, globalModels)\nconst service = setupVuexService(feathersClient, options, globalModels)\n\nconst accessToken =\n  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjAsImV4cCI6OTk5OTk5OTk5OTk5OX0.zmvEm8w142xGI7CbUsnvVGZk_hrVE1KEjzDt80LSW50'\n\ndescribe('Auth Module - Actions', () => {\n  it('Authenticate', done => {\n    const store = new Vuex.Store({\n      plugins: [auth()]\n    })\n    feathersClient.use('authentication', {\n      create(data) {\n        return Promise.resolve({ accessToken })\n      }\n    })\n\n    const authState = store.state.auth\n    const actions = mapActions('auth', ['authenticate'])\n\n    assert(authState.accessToken === null)\n    assert(authState.errorOnAuthenticate === null)\n    assert(authState.errorOnLogout === null)\n    assert(authState.isAuthenticatePending === false)\n    assert(authState.isLogoutPending === false)\n    assert(authState.payload === null)\n\n    const request = { strategy: 'local', email: 'test', password: 'test' }\n    actions.authenticate.call({ $store: store }, request).then(response => {\n      assert(authState.accessToken === response.accessToken)\n      assert(authState.errorOnAuthenticate === null)\n      assert(authState.errorOnLogout === null)\n      assert(authState.isAuthenticatePending === false)\n      assert(authState.isLogoutPending === false)\n      const expectedPayload = {\n        userId: 0,\n        exp: 9999999999999\n      }\n      assert.deepEqual(authState.payload, expectedPayload)\n      done()\n    })\n\n    // Make sure proper state changes occurred before response\n    assert(authState.accessToken === null)\n    assert(authState.errorOnAuthenticate === null)\n    assert(authState.errorOnLogout === null)\n    assert(authState.isAuthenticatePending === true)\n    assert(authState.isLogoutPending === false)\n    assert(authState.payload === null)\n  })\n\n  it('Logout', done => {\n    const store = new Vuex.Store({\n      plugins: [auth()]\n    })\n    feathersClient.use('authentication', {\n      create(data) {\n        return Promise.resolve({ accessToken })\n      }\n    })\n\n    const authState = store.state.auth\n    const actions = mapActions('auth', ['authenticate', 'logout'])\n    const request = { strategy: 'local', email: 'test', password: 'test' }\n\n    actions.authenticate.call({ $store: store }, request).then(authResponse => {\n      actions.logout.call({ $store: store }).then(response => {\n        assert(authState.accessToken === null)\n        assert(authState.errorOnAuthenticate === null)\n        assert(authState.errorOnLogout === null)\n        assert(authState.isAuthenticatePending === false)\n        assert(authState.isLogoutPending === false)\n        assert(authState.payload === null)\n        done()\n      })\n    })\n  })\n\n  it('Authenticate with userService config option', done => {\n    feathersClient.use('authentication', {\n      create(data) {\n        return Promise.resolve({ accessToken })\n      }\n    })\n    feathersClient.use(\n      'users',\n      memory({ store: { 0: { id: 0, email: 'test@test.com' } } })\n    )\n    const store = new Vuex.Store({\n      plugins: [auth({ userService: 'users' }), service('users')]\n    })\n\n    const authState = store.state.auth\n    const actions = mapActions('auth', ['authenticate'])\n\n    assert(authState.user === null)\n\n    const request = { strategy: 'local', email: 'test', password: 'test' }\n    actions.authenticate\n      .call({ $store: store }, request)\n      .then(response => {\n        const expectedUser = {\n          id: 0,\n          email: 'test@test.com'\n        }\n        assert.deepEqual(authState.user, expectedUser)\n        done()\n      })\n      .catch(error => {\n        assert(!error, error)\n        done()\n      })\n  })\n})\n"
  },
  {
    "path": "test/auth-module/auth-module.test.ts",
    "content": "/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { assert } from 'chai'\nimport feathersVuex from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport Vuex from 'vuex'\nimport { isEmpty } from 'lodash'\n\nconst { makeAuthPlugin, makeServicePlugin, BaseModel } = feathersVuex(\n  feathersClient,\n  {\n    serverAlias: 'api'\n  }\n)\ninterface CustomStore {\n  state: any\n  auth: any\n  authentication?: any\n  users?: any\n}\n\nfunction makeContext() {\n  class User extends BaseModel {\n    constructor(data, options) {\n      super(data, options)\n    }\n    static modelName = 'User'\n    static instanceDefaults() {\n      return {\n        email: '',\n        password: ''\n      }\n    }\n  }\n  const servicePath = 'users'\n  const usersPlugin = makeServicePlugin({\n    Model: User,\n    service: feathersClient.service(servicePath),\n    servicePath\n  })\n\n  const authPlugin = makeAuthPlugin({ userService: 'users' })\n\n  const store = new Vuex.Store<CustomStore>({\n    plugins: [authPlugin, usersPlugin]\n  })\n\n  return { User, usersPlugin, authPlugin, BaseModel, store }\n}\n\ndescribe('Auth Module', () => {\n  describe('Configuration', () => {\n    it('has default auth namespace', () => {\n      const { store } = makeContext()\n      const authState = Object.assign({}, store.state.auth)\n      const expectedCustomStore = {\n        accessToken: null,\n        entityIdField: 'userId',\n        errorOnAuthenticate: null,\n        errorOnLogout: null,\n        isAuthenticatePending: false,\n        isLogoutPending: false,\n        payload: null,\n        responseEntityField: 'user',\n        serverAlias: 'api',\n        user: null,\n        userService: 'users'\n      }\n\n      assert.deepEqual(authState, expectedCustomStore, 'has the default state')\n    })\n\n    it('can customize the namespace', function() {\n      const store = new Vuex.Store<CustomStore>({\n        plugins: [makeAuthPlugin({ namespace: 'authentication' })]\n      })\n\n      assert(store.state.authentication, 'the custom namespace was used')\n    })\n  })\n\n  describe('Customizing Auth Store', function() {\n    it('allows adding custom state', function() {\n      const customState = {\n        test: true,\n        test2: {\n          test: true\n        }\n      }\n      const store = new Vuex.Store<CustomStore>({\n        plugins: [makeAuthPlugin({ state: customState })]\n      })\n\n      assert(store.state.auth.test === true, 'added custom state')\n      assert(store.state.auth.test2.test === true, 'added custom state')\n    })\n\n    it('allows custom mutations', function() {\n      const state = { test: true }\n      const customMutations = {\n        setTestToFalse(state) {\n          state.test = false\n        }\n      }\n      const store = new Vuex.Store<CustomStore>({\n        plugins: [makeAuthPlugin({ state, mutations: customMutations })]\n      })\n\n      store.commit('auth/setTestToFalse')\n      assert(\n        store.state.auth.test === false,\n        'the custom state was modified by the custom mutation'\n      )\n    })\n\n    it('has a user && isAuthenticated getter when there is a userService attribute', function() {\n      const store = new Vuex.Store<CustomStore>({\n        state: {\n          state: {},\n          auth: {},\n          users: {\n            idField: 'id',\n            keyedById: {\n              1: {\n                id: 1,\n                name: 'Marshall'\n              }\n            }\n          }\n        },\n        plugins: [\n          makeAuthPlugin({\n            state: {\n              user: {\n                id: 1\n              }\n            },\n            userService: 'users'\n          })\n        ]\n      })\n      const user = store.getters['auth/user']\n      const isAuthenticated = store.getters['auth/isAuthenticated']\n\n      assert(user.name === 'Marshall', 'Got the user from the users store.')\n      assert(isAuthenticated, 'isAuthenticated')\n    })\n\n    it('getters show not authenticated when there is no user', function() {\n      const store = new Vuex.Store<CustomStore>({\n        state: {\n          state: {},\n          auth: {},\n          users: {\n            idField: 'id',\n            keyedById: {}\n          }\n        },\n        plugins: [\n          makeAuthPlugin({\n            state: {},\n            userService: 'users'\n          })\n        ]\n      })\n      const user = store.getters['auth/user']\n      const isAuthenticated = store.getters['auth/isAuthenticated']\n\n      assert(user === null, 'user getter returned null as expected')\n      assert(!isAuthenticated, 'not authenticated')\n    })\n\n    it('allows custom getters', function() {\n      const customGetters = {\n        oneTwoThree() {\n          return 123\n        }\n      }\n      const store = new Vuex.Store<CustomStore>({\n        plugins: [makeAuthPlugin({ getters: customGetters })]\n      })\n\n      assert(\n        store.getters['auth/oneTwoThree'] === 123,\n        'the custom getter was available'\n      )\n    })\n\n    it('allows adding custom actions', function() {\n      const config = {\n        state: {\n          isTrue: false\n        },\n        mutations: {\n          setToTrue(state) {\n            state.isTrue = true\n          }\n        },\n        actions: {\n          trigger(context) {\n            context.commit('setToTrue')\n          }\n        }\n      }\n      const store = new Vuex.Store<CustomStore>({\n        plugins: [makeAuthPlugin(config)]\n      })\n\n      store.dispatch('auth/trigger')\n      assert(store.state.auth.isTrue === true, 'the custom action was run')\n    })\n  })\n\n  it('Calls auth service without params', async function() {\n    let receivedData = null\n    let receivedParams = null\n    feathersClient.use('authentication', {\n      create(data, params) {\n        receivedData = data\n        receivedParams = params\n        return Promise.resolve({ accessToken: 'jg54jh2gj6fgh734j5h4j25jbh' })\n      }\n    })\n\n    const { store } = makeContext()\n\n    const request = { strategy: 'local', email: 'test', password: 'test' }\n    await store.dispatch('auth/authenticate', request)\n    assert(receivedData, 'got data')\n    assert(receivedData.strategy === 'local', 'got strategy')\n    assert(receivedData.email === 'test', 'got email')\n    assert(receivedData.password === 'test', 'got password')\n    assert(receivedParams && isEmpty(receivedParams), 'empty params')\n  })\n\n  it('Calls auth service with params', async function() {\n    let receivedParams = null\n    feathersClient.use('authentication', {\n      create(data, params) {\n        receivedParams = params\n        return Promise.resolve({ accessToken: 'jg54jh2gj6fgh734j5h4j25jbh' })\n      }\n    })\n\n    const { store } = makeContext()\n\n    const request = { strategy: 'local', email: 'test', password: 'test' }\n    const customParams = { theAnswer: 42 }\n    await store.dispatch('auth/authenticate', [request, customParams])\n    assert(receivedParams && receivedParams.theAnswer === 42, 'got params')\n  })\n})\n"
  },
  {
    "path": "test/auth.test.js",
    "content": "import { assert } from 'chai'\nimport feathersVuexAuth, { reducer } from '../src/auth'\nimport * as actionTypes from '../src/action-types'\nimport './server'\nimport { makeFeathersRestClient } from './feathers-client'\n\ndescribe('feathers-vuex:auth', () => {\n  it('is CommonJS compatible', () => {\n    assert(typeof require('../lib/auth').default === 'function')\n  })\n\n  it('basic functionality', () => {\n    assert(typeof feathersVuexAuth === 'function', 'It worked')\n  })\n\n  it('throws an error if the auth plugin is missing', () => {\n    const app = {}\n    const store = {}\n    const plugin = feathersVuexAuth(store).bind(app)\n    assert.throws(\n      plugin,\n      'You must first register the @feathersjs/authentication-client plugin'\n    )\n  })\n\n  it('returns the app, is chainable', () => {\n    const app = {\n      authenticate() {}\n    }\n    const store = {}\n    const returnValue = feathersVuexAuth(store).bind(app)()\n    assert(returnValue === app)\n  })\n\n  it('replaces the original authenticate function', () => {\n    const feathersClient = makeFeathersRestClient()\n    const oldAuthenticate = feathersClient.authenticate\n    const store = {}\n    feathersClient.configure(feathersVuexAuth(store))\n    assert(oldAuthenticate !== feathersClient.authenticate)\n  })\n\n  it('dispatches actions to the store.', done => {\n    const feathersClient = makeFeathersRestClient()\n    const fakeStore = {\n      dispatch(action) {\n        switch (action.type) {\n          case actionTypes.FEATHERS_AUTH_REQUEST:\n            assert(action.payload.test || action.payload.accessToken)\n            break\n          case actionTypes.FEATHERS_AUTH_SUCCESS:\n            assert(action.data)\n            break\n          case actionTypes.FEATHERS_AUTH_FAILURE:\n            assert(action.error)\n            done()\n            break\n          case actionTypes.FEATHERS_AUTH_LOGOUT:\n            assert(action)\n            break\n        }\n      }\n    }\n\n    feathersClient.configure(feathersVuexAuth(fakeStore))\n\n    try {\n      feathersClient\n        .authenticate({ test: true })\n        .then(response => {\n          feathersClient.logout()\n          return response\n        })\n        .catch(error => {\n          assert(error.className === 'not-authenticated')\n        })\n    } catch (err) {}\n    try {\n      feathersClient.authenticate({\n        strategy: 'jwt',\n        accessToken: 'q34twershtdyfhgmj'\n      })\n    } catch (err) {\n      // eslint-disable-next-line no-console\n      console.log(err)\n    }\n  })\n})\n\ndescribe('feathers-vuex:auth - Reducer', () => {\n  it('Has defaults', () => {\n    const state = undefined\n    const defaultState = {\n      isPending: false,\n      isError: false,\n      isSignedIn: false,\n      accessToken: null,\n      error: undefined\n    }\n    const newState = reducer(state, {})\n    assert.deepEqual(newState, defaultState)\n  })\n\n  it(`Responds to ${actionTypes.FEATHERS_AUTH_REQUEST}`, () => {\n    const state = undefined\n    const action = {\n      type: actionTypes.FEATHERS_AUTH_REQUEST,\n      payload: {\n        strategy: 'jwt',\n        accessToken: 'evh8vq2pj'\n      }\n    }\n    const expectedState = {\n      isPending: true,\n      isError: false,\n      isSignedIn: false,\n      accessToken: null,\n      error: undefined\n    }\n    const newState = reducer(state, action)\n    assert.deepEqual(newState, expectedState)\n  })\n\n  it(`Responds to ${actionTypes.FEATHERS_AUTH_SUCCESS}`, () => {\n    const state = undefined\n    const accessToken = 'evh8vq2pj'\n    const action = {\n      type: actionTypes.FEATHERS_AUTH_SUCCESS,\n      data: { accessToken }\n    }\n    const expectedState = {\n      isPending: false,\n      isError: false,\n      isSignedIn: true,\n      accessToken: accessToken,\n      error: undefined\n    }\n    const newState = reducer(state, action)\n    assert.deepEqual(newState, expectedState)\n  })\n\n  it(`Responds to ${actionTypes.FEATHERS_AUTH_FAILURE}`, () => {\n    const state = undefined\n    const error = 'Unauthorized'\n    const action = {\n      type: actionTypes.FEATHERS_AUTH_FAILURE,\n      error\n    }\n    const expectedState = {\n      isPending: false,\n      isError: true,\n      isSignedIn: false,\n      accessToken: null,\n      error\n    }\n    const newState = reducer(state, action)\n    assert.deepEqual(newState, expectedState)\n  })\n\n  it(`Responds to ${actionTypes.FEATHERS_AUTH_LOGOUT}`, () => {\n    const state = undefined\n    const action = {\n      type: actionTypes.FEATHERS_AUTH_LOGOUT\n    }\n    const expectedState = {\n      isPending: false,\n      isError: false,\n      isSignedIn: false,\n      accessToken: null,\n      error: undefined\n    }\n    const newState = reducer(state, action)\n    assert.deepEqual(newState, expectedState)\n  })\n})\n"
  },
  {
    "path": "test/fixtures/fake-data.js",
    "content": "export default {\n  users: [\n    {\n      email: 'Richie_Cartwright97@hotmail.com',\n      password: '$2a$13$7BBrmdiWTtm3GbD/KHdBOOjTqricfqPI06j/Wg/rsDqpnEza00bHG',\n      _id: '5c6cafbf1babb758d2975407'\n    },\n    {\n      email: 'Kristofer25@hotmail.com',\n      password: '$2a$13$d5f0aRKwHwK9NFbJfYG4Ke4gZU39dOa5jGqzbFtuWpBll4mLs/Ewu',\n      _id: '5c6cafbf1babb758d2975408'\n    },\n    {\n      email: 'Margarett.Kozey@hotmail.com',\n      password: '$2a$13$t6cmWh4zjpfVoTqGUtyB1O/zLZe99uYhNNEQQi7cMEpTtyzABeIt.',\n      _id: '5c6cafbf1babb758d2975409'\n    },\n    {\n      email: 'Pinkie_Braun98@yahoo.com',\n      password: '$2a$13$.HrrqdURnxWoBljdifveveC4eF6RXbP4qb2WEK40TIJ5D8FxSNe6O',\n      _id: '5c6cafbf1babb758d297540a'\n    },\n    {\n      email: 'Isabelle26@yahoo.com',\n      password: '$2a$13$J0x0WhGjGQVyOs15zGmOluN5oj681xze4ARaKD8I05hVEwvO8KUfi',\n      _id: '5c6cafbf1babb758d297540b'\n    },\n    {\n      email: 'Kay92@yahoo.com',\n      password: '$2a$13$jmPxZxZKW0E3XgEyUEqyEeY5S.0PlvHnbTPR/T8WKbm192macrpSq',\n      _id: '5c6cafbf1babb758d297540c'\n    }\n  ],\n  transactions: [\n    {\n      _id: '5c6cafbf1babb758d297540d',\n      name: 'Hahn, Dare and Turner',\n      type: 'deposit',\n      amount: 589.57,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b3'\n    },\n    {\n      _id: '5c6cafbf1babb758d297540e',\n      name: 'Schulist, Abbott and McClure',\n      type: 'invoice',\n      amount: 206,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297540f',\n      name: 'Simonis, Waters and Turcotte',\n      type: 'deposit',\n      amount: 384.59,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754ba'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975410',\n      name: 'Buckridge - Steuber',\n      type: 'invoice',\n      amount: 445.16,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754ba'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975411',\n      name: 'Reilly, Hahn and Murray',\n      type: 'invoice',\n      amount: 104.58,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975412',\n      name: 'Heaney LLC',\n      type: 'invoice',\n      amount: 551.98,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975413',\n      name: 'Harris LLC',\n      type: 'payment',\n      amount: 797.64,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975414',\n      name: 'Ruecker and Sons',\n      type: 'deposit',\n      amount: 104.17,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975415',\n      name: 'Walter, DuBuque and Sipes',\n      type: 'withdrawal',\n      amount: 724.42,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975416',\n      name: 'Tremblay LLC',\n      type: 'payment',\n      amount: 721.65,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975417',\n      name: 'Quitzon, Hoppe and Bayer',\n      type: 'withdrawal',\n      amount: 929.01,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975418',\n      name: 'Okuneva - McClure',\n      type: 'withdrawal',\n      amount: 178.02,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975419',\n      name: 'Berge LLC',\n      type: 'payment',\n      amount: 365.29,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d297541a',\n      name: 'Bode - McLaughlin',\n      type: 'deposit',\n      amount: 682.29,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d297541b',\n      name: 'Labadie - Kub',\n      type: 'deposit',\n      amount: 557.18,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d297541c',\n      name: 'Torphy LLC',\n      type: 'withdrawal',\n      amount: 899.76,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d297541d',\n      name: 'Torp LLC',\n      type: 'deposit',\n      amount: 326.11,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d297541e',\n      name: 'Jenkins, Moen and Jast',\n      type: 'invoice',\n      amount: 143.48,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754c4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297541f',\n      name: 'Towne, Conn and Swaniawski',\n      type: 'deposit',\n      amount: 404.22,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975420',\n      name: 'Weissnat Group',\n      type: 'payment',\n      amount: 226.77,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975421',\n      name: 'Boehm, Stehr and Rolfson',\n      type: 'withdrawal',\n      amount: 862.48,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c3'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975422',\n      name: 'Schumm, Bruen and Upton',\n      type: 'withdrawal',\n      amount: 191.31,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754ad'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975423',\n      name: 'Hilpert - Bogan',\n      type: 'invoice',\n      amount: 415.73,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975424',\n      name: 'Ondricka, Koch and Adams',\n      type: 'deposit',\n      amount: 131.94,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bb'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975425',\n      name: 'Lockman LLC',\n      type: 'deposit',\n      amount: 965.39,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975426',\n      name: 'Tremblay LLC',\n      type: 'deposit',\n      amount: 43.32,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b4'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975427',\n      name: 'Block Group',\n      type: 'withdrawal',\n      amount: 935.48,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754c3'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975428',\n      name: 'Marquardt Inc',\n      type: 'deposit',\n      amount: 279.73,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975429',\n      name: 'Littel LLC',\n      type: 'payment',\n      amount: 366.58,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d297542a',\n      name: 'Kuvalis LLC',\n      type: 'deposit',\n      amount: 641.81,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d297542b',\n      name: 'Howell - Jakubowski',\n      type: 'payment',\n      amount: 239.4,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b3'\n    },\n    {\n      _id: '5c6cafbf1babb758d297542c',\n      name: 'Quigley - Mann',\n      type: 'invoice',\n      amount: 948.68,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c3'\n    },\n    {\n      _id: '5c6cafbf1babb758d297542d',\n      name: \"O'Reilly - O'Hara\",\n      type: 'payment',\n      amount: 42.22,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b1'\n    },\n    {\n      _id: '5c6cafbf1babb758d297542e',\n      name: 'Schuster, Heller and Jenkins',\n      type: 'invoice',\n      amount: 660.17,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b9'\n    },\n    {\n      _id: '5c6cafbf1babb758d297542f',\n      name: 'Skiles - Wolff',\n      type: 'payment',\n      amount: 492.12,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b4'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975430',\n      name: 'Bauch - Leffler',\n      type: 'deposit',\n      amount: 851.64,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754bc'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975431',\n      name: 'Pacocha - Morissette',\n      type: 'deposit',\n      amount: 817.33,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975432',\n      name: 'Rutherford Group',\n      type: 'payment',\n      amount: 536.97,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975433',\n      name: 'Labadie, Schaefer and Dietrich',\n      type: 'deposit',\n      amount: 470.89,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754af'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975434',\n      name: 'Weber - Schimmel',\n      type: 'withdrawal',\n      amount: 598.67,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c3'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975435',\n      name: 'Ritchie, Dach and Oberbrunner',\n      type: 'payment',\n      amount: 193.49,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975436',\n      name: 'Strosin Group',\n      type: 'invoice',\n      amount: 957.26,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975437',\n      name: 'Stark - Rogahn',\n      type: 'payment',\n      amount: 634.26,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b9'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975438',\n      name: 'Emard and Sons',\n      type: 'payment',\n      amount: 985.95,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975439',\n      name: 'Nienow - Kuphal',\n      type: 'withdrawal',\n      amount: 994.74,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d297543a',\n      name: 'Hartmann and Sons',\n      type: 'payment',\n      amount: 719.73,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d297543b',\n      name: 'Weimann - Gleichner',\n      type: 'payment',\n      amount: 209.88,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d297543c',\n      name: 'Hirthe Inc',\n      type: 'deposit',\n      amount: 371.33,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d297543d',\n      name: 'Schaden - Wisoky',\n      type: 'payment',\n      amount: 48.39,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c3'\n    },\n    {\n      _id: '5c6cafbf1babb758d297543e',\n      name: 'Stroman Inc',\n      type: 'invoice',\n      amount: 306.6,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d297543f',\n      name: 'Robel Group',\n      type: 'invoice',\n      amount: 815.09,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975440',\n      name: 'Dach Inc',\n      type: 'payment',\n      amount: 644.27,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b9'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975441',\n      name: 'VonRueden and Sons',\n      type: 'payment',\n      amount: 917.19,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b9'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975442',\n      name: 'Nikolaus, Abernathy and Jakubowski',\n      type: 'withdrawal',\n      amount: 299.81,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754bb'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975443',\n      name: 'Turcotte and Sons',\n      type: 'payment',\n      amount: 574.29,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975444',\n      name: 'Okuneva - Walker',\n      type: 'withdrawal',\n      amount: 590.38,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754bc'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975445',\n      name: 'Abernathy, Wilkinson and Watsica',\n      type: 'payment',\n      amount: 866.38,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975446',\n      name: 'Williamson, Lemke and Blanda',\n      type: 'invoice',\n      amount: 584.63,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975447',\n      name: 'Metz Inc',\n      type: 'deposit',\n      amount: 54.24,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754bd'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975448',\n      name: 'Block LLC',\n      type: 'payment',\n      amount: 491.09,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975449',\n      name: 'Grady, Schultz and Padberg',\n      type: 'deposit',\n      amount: 551.38,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297544a',\n      name: 'Bradtke - Botsford',\n      type: 'payment',\n      amount: 674.32,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d297544b',\n      name: 'Schultz, Graham and Herzog',\n      type: 'payment',\n      amount: 79.16,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d297544c',\n      name: 'Beer, Boyer and Bergstrom',\n      type: 'payment',\n      amount: 34.12,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d297544d',\n      name: 'Monahan - Crona',\n      type: 'deposit',\n      amount: 470.31,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754af'\n    },\n    {\n      _id: '5c6cafbf1babb758d297544e',\n      name: 'Morar and Sons',\n      type: 'deposit',\n      amount: 518.19,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297544f',\n      name: 'Stamm, Steuber and Doyle',\n      type: 'payment',\n      amount: 790.31,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975450',\n      name: 'Gottlieb, Harber and Wehner',\n      type: 'invoice',\n      amount: 153.32,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975451',\n      name: 'Kuhic - Paucek',\n      type: 'payment',\n      amount: 587.14,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975452',\n      name: \"O'Keefe, Rice and Crooks\",\n      type: 'deposit',\n      amount: 549.83,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975453',\n      name: 'Reilly LLC',\n      type: 'payment',\n      amount: 10.12,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975454',\n      name: 'Walsh Group',\n      type: 'payment',\n      amount: 118.07,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b3'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975455',\n      name: 'Thompson Group',\n      type: 'payment',\n      amount: 452.88,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975456',\n      name: 'Abbott - Conn',\n      type: 'withdrawal',\n      amount: 930.52,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975457',\n      name: 'Walker, Boehm and Pouros',\n      type: 'withdrawal',\n      amount: 325.91,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975458',\n      name: 'Collier - Schamberger',\n      type: 'withdrawal',\n      amount: 579.84,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975459',\n      name: 'Tremblay - Auer',\n      type: 'invoice',\n      amount: 245.28,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bd'\n    },\n    {\n      _id: '5c6cafbf1babb758d297545a',\n      name: 'Luettgen, Moore and Schroeder',\n      type: 'invoice',\n      amount: 22.18,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297545b',\n      name: 'Herzog Inc',\n      type: 'withdrawal',\n      amount: 294.14,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d297545c',\n      name: 'Kutch, Reynolds and Ankunding',\n      type: 'invoice',\n      amount: 858.23,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c0'\n    },\n    {\n      _id: '5c6cafbf1babb758d297545d',\n      name: 'Hagenes - Thiel',\n      type: 'invoice',\n      amount: 213.87,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d297545e',\n      name: 'Hoppe - Raynor',\n      type: 'invoice',\n      amount: 733.43,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d297545f',\n      name: 'Lesch, Little and Nicolas',\n      type: 'deposit',\n      amount: 154.18,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bd'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975460',\n      name: 'Hintz LLC',\n      type: 'withdrawal',\n      amount: 173.48,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975461',\n      name: 'Stamm and Sons',\n      type: 'withdrawal',\n      amount: 343.16,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975462',\n      name: 'Labadie - Weimann',\n      type: 'withdrawal',\n      amount: 812.54,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754c0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975463',\n      name: 'Hegmann Inc',\n      type: 'withdrawal',\n      amount: 843.29,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975464',\n      name: 'Walker - Bruen',\n      type: 'invoice',\n      amount: 122.99,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975465',\n      name: 'Erdman - Streich',\n      type: 'withdrawal',\n      amount: 810.5,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975466',\n      name: 'Swift - Wisozk',\n      type: 'withdrawal',\n      amount: 356.07,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754bc'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975467',\n      name: 'Schroeder, Abernathy and Miller',\n      type: 'payment',\n      amount: 588.23,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bb'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975468',\n      name: 'Hansen Inc',\n      type: 'payment',\n      amount: 978.32,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975469',\n      name: 'Reilly, Kautzer and Bode',\n      type: 'invoice',\n      amount: 874.7,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d297546a',\n      name: 'Pagac, Kohler and Johnston',\n      type: 'invoice',\n      amount: 579.61,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d297546b',\n      name: 'Stehr Group',\n      type: 'invoice',\n      amount: 673.81,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d297546c',\n      name: 'McCullough - Harvey',\n      type: 'invoice',\n      amount: 506.83,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b3'\n    },\n    {\n      _id: '5c6cafbf1babb758d297546d',\n      name: 'Brekke, Davis and Russel',\n      type: 'invoice',\n      amount: 363.19,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d297546e',\n      name: 'Kutch Group',\n      type: 'payment',\n      amount: 266.73,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297546f',\n      name: 'Funk, McLaughlin and Hartmann',\n      type: 'invoice',\n      amount: 610.16,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b9'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975470',\n      name: 'Pouros - Lind',\n      type: 'payment',\n      amount: 673.94,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975471',\n      name: 'Towne - Schmitt',\n      type: 'withdrawal',\n      amount: 285.58,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bc'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975472',\n      name: 'Prohaska - Schroeder',\n      type: 'withdrawal',\n      amount: 331.94,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975473',\n      name: 'Harber and Sons',\n      type: 'payment',\n      amount: 526.65,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975474',\n      name: 'Doyle - Huel',\n      type: 'payment',\n      amount: 582.6,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975475',\n      name: 'Goyette, Collins and Greenholt',\n      type: 'payment',\n      amount: 961.47,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975476',\n      name: 'Purdy - Corkery',\n      type: 'withdrawal',\n      amount: 742.07,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754bb'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975477',\n      name: 'Baumbach, Gutkowski and Hauck',\n      type: 'payment',\n      amount: 67.98,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975478',\n      name: 'Schuster - Jast',\n      type: 'invoice',\n      amount: 952.12,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975479',\n      name: 'Cassin LLC',\n      type: 'withdrawal',\n      amount: 491.26,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d297547a',\n      name: 'Koelpin - Rice',\n      type: 'deposit',\n      amount: 148.37,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d297547b',\n      name: 'Moen LLC',\n      type: 'deposit',\n      amount: 666.99,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d297547c',\n      name: 'Wiza LLC',\n      type: 'deposit',\n      amount: 492.08,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754ba'\n    },\n    {\n      _id: '5c6cafbf1babb758d297547d',\n      name: 'Sanford, Auer and Lueilwitz',\n      type: 'invoice',\n      amount: 677.3,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d297547e',\n      name: 'Schroeder LLC',\n      type: 'withdrawal',\n      amount: 842.7,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d297547f',\n      name: 'Carter - Schoen',\n      type: 'invoice',\n      amount: 987.15,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bb'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975480',\n      name: 'Hills Inc',\n      type: 'invoice',\n      amount: 70.08,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975481',\n      name: 'Carter - Morar',\n      type: 'invoice',\n      amount: 837.2,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bd'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975482',\n      name: 'Lubowitz, Powlowski and Leffler',\n      type: 'invoice',\n      amount: 323.43,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975483',\n      name: 'Haag, Larkin and Corwin',\n      type: 'deposit',\n      amount: 830.43,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975484',\n      name: 'Dibbert, Gerlach and Schneider',\n      type: 'invoice',\n      amount: 366.91,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754af'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975485',\n      name: 'Purdy LLC',\n      type: 'invoice',\n      amount: 872.18,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975486',\n      name: 'Pagac, Wyman and Stanton',\n      type: 'withdrawal',\n      amount: 545.68,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975487',\n      name: 'Quigley - Littel',\n      type: 'deposit',\n      amount: 256.81,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754c0'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975488',\n      name: 'Gleason - Goldner',\n      type: 'deposit',\n      amount: 906.86,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754c4'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975489',\n      name: 'Daugherty Inc',\n      type: 'invoice',\n      amount: 78.18,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b0'\n    },\n    {\n      _id: '5c6cafbf1babb758d297548a',\n      name: 'Ratke - Thiel',\n      type: 'invoice',\n      amount: 735.66,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d297548b',\n      name: 'Fisher and Sons',\n      type: 'deposit',\n      amount: 260.56,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d297548c',\n      name: 'Aufderhar - Ernser',\n      type: 'payment',\n      amount: 325.62,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d297548d',\n      name: 'Bayer - Jacobs',\n      type: 'withdrawal',\n      amount: 364.59,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b5'\n    },\n    {\n      _id: '5c6cafbf1babb758d297548e',\n      name: 'Streich - Tremblay',\n      type: 'withdrawal',\n      amount: 929.34,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754bf'\n    },\n    {\n      _id: '5c6cafbf1babb758d297548f',\n      name: 'Satterfield - Kuhlman',\n      type: 'deposit',\n      amount: 476.89,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975490',\n      name: 'Schneider Group',\n      type: 'invoice',\n      amount: 555.79,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975491',\n      name: 'Hane LLC',\n      type: 'invoice',\n      amount: 193.13,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c1'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975492',\n      name: 'Leffler - Herzog',\n      type: 'invoice',\n      amount: 590.05,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754c5'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975493',\n      name: 'Luettgen LLC',\n      type: 'invoice',\n      amount: 258.9,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975494',\n      name: 'Mosciski, Welch and Pfeffer',\n      type: 'invoice',\n      amount: 407.29,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b3'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975495',\n      name: 'DuBuque and Sons',\n      type: 'invoice',\n      amount: 444.08,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975496',\n      name: 'Tromp, Harber and Reichel',\n      type: 'deposit',\n      amount: 591.37,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bb'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975497',\n      name: 'Mayer and Sons',\n      type: 'payment',\n      amount: 2.32,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b8'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975498',\n      name: 'Larson, Schmeler and Oberbrunner',\n      type: 'payment',\n      amount: 896.93,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d2975499',\n      name: 'Schaden - Becker',\n      type: 'deposit',\n      amount: 510.06,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754ae'\n    },\n    {\n      _id: '5c6cafbf1babb758d297549a',\n      name: 'Bailey, Kshlerin and Powlowski',\n      type: 'withdrawal',\n      amount: 97.7,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b6'\n    },\n    {\n      _id: '5c6cafbf1babb758d297549b',\n      name: 'Wisoky LLC',\n      type: 'deposit',\n      amount: 931.21,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754bc'\n    },\n    {\n      _id: '5c6cafbf1babb758d297549c',\n      name: 'Mayert - Morissette',\n      type: 'withdrawal',\n      amount: 610.77,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754bc'\n    },\n    {\n      _id: '5c6cafbf1babb758d297549d',\n      name: 'Hammes Inc',\n      type: 'withdrawal',\n      amount: 74.11,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b4'\n    },\n    {\n      _id: '5c6cafbf1babb758d297549e',\n      name: 'Gleichner and Sons',\n      type: 'withdrawal',\n      amount: 983.35,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d297549f',\n      name: \"Champlin - O'Hara\",\n      type: 'withdrawal',\n      amount: 247.82,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754bd'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a0',\n      name: 'Legros - Schuster',\n      type: 'deposit',\n      amount: 821.05,\n      userId: '5c6cafbf1babb758d297540b',\n      accountId: '5c6cafbf1babb758d29754be'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a1',\n      name: 'Ritchie - Treutel',\n      type: 'invoice',\n      amount: 157.82,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754bd'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a2',\n      name: 'Hyatt, Kuphal and Stanton',\n      type: 'deposit',\n      amount: 711.48,\n      userId: '5c6cafbf1babb758d297540c',\n      accountId: '5c6cafbf1babb758d29754c3'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a3',\n      name: 'Stamm - Leffler',\n      type: 'invoice',\n      amount: 89.05,\n      userId: '5c6cafbf1babb758d2975407',\n      accountId: '5c6cafbf1babb758d29754b9'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a4',\n      name: 'Bailey, VonRueden and Lesch',\n      type: 'withdrawal',\n      amount: 378.25,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754ba'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a5',\n      name: 'Osinski, Gleason and Predovic',\n      type: 'withdrawal',\n      amount: 582.79,\n      userId: '5c6cafbf1babb758d297540a',\n      accountId: '5c6cafbf1babb758d29754b2'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a6',\n      name: 'Purdy LLC',\n      type: 'invoice',\n      amount: 633.06,\n      userId: '5c6cafbf1babb758d2975408',\n      accountId: '5c6cafbf1babb758d29754b7'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754a7',\n      name: 'Lowe and Sons',\n      type: 'withdrawal',\n      amount: 632.33,\n      userId: '5c6cafbf1babb758d2975409',\n      accountId: '5c6cafbf1babb758d29754b9'\n    }\n  ],\n  config: [\n    {\n      label: 'ipsum ea elit',\n      description: 'exercitation ut incididunt irure',\n      tags: [\n        'veniam consequat commodo',\n        'dolor in officia Duis sint',\n        'eu consectetur non cupidatat',\n        'ut deserunt'\n      ],\n      name: 'commodo consectetur ad sunt',\n      value: 'nulla',\n      _id: '5c6cafbf1babb758d29754a8'\n    },\n    {\n      label: 'labore velit Ut adipisic',\n      description: 'aute non',\n      tags: [\n        'est voluptate deserunt',\n        'minim nisi fugiat in elit',\n        'dolor in',\n        'nostrud'\n      ],\n      name: 'Excepteur veniam velit enim Ut',\n      value: 'exercitation',\n      _id: '5c6cafbf1babb758d29754a9'\n    },\n    {\n      label: 'labore officia',\n      description: 'quis aliqua',\n      tags: ['mollit labore magna ad', 'minim sit magna'],\n      name: 'oc',\n      value: 'dolor',\n      _id: '5c6cafbf1babb758d29754aa'\n    },\n    {\n      label: 'Duis fug',\n      description: 'Duis eiusmod non deserunt',\n      tags: [\n        'nostrud Lorem',\n        'laborum ut labore esse',\n        'voluptate',\n        'occaecat tempor',\n        'laboris irure culpa'\n      ],\n      name: 'nulla ut dolor in',\n      value: 'amet in enim consequat',\n      _id: '5c6cafbf1babb758d29754ab'\n    },\n    {\n      label: 'culpa',\n      description: 'velit enim incididunt',\n      tags: ['tempor nisi fugiat proident', 'Ut pariatur'],\n      name: 'irure incididunt dolore ipsum',\n      value: 'consectetur elit ea Ut',\n      _id: '5c6cafbf1babb758d29754ac'\n    }\n  ],\n  accounts: [\n    {\n      _id: '5c6cafbf1babb758d29754ad',\n      name: 'Credit Card Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'XFU',\n      currencySymbol: 'B/.'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754ae',\n      name: 'Money Market Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'BZD',\n      currencySymbol: '₮'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754af',\n      name: 'Auto Loan Account',\n      userId: '5c6cafbf1babb758d2975408',\n      currencyCode: 'PKR',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b0',\n      name: 'Personal Loan Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'PKR',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b1',\n      name: 'Home Loan Account',\n      userId: '5c6cafbf1babb758d2975408',\n      currencyCode: 'XCD',\n      currencySymbol: '₨'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b2',\n      name: 'Credit Card Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'BWP',\n      currencySymbol: 'kr'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b3',\n      name: 'Personal Loan Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'NIO',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b4',\n      name: 'Auto Loan Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'SDG',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b5',\n      name: 'Money Market Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'MNT',\n      currencySymbol: '₨'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b6',\n      name: 'Auto Loan Account',\n      userId: '5c6cafbf1babb758d297540b',\n      currencyCode: 'BOB BOV',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b7',\n      name: 'Personal Loan Account',\n      userId: '5c6cafbf1babb758d297540a',\n      currencyCode: 'ANG',\n      currencySymbol: 'Lt'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b8',\n      name: 'Personal Loan Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'HTG USD',\n      currencySymbol: '﷼'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754b9',\n      name: 'Investment Account',\n      userId: '5c6cafbf1babb758d297540a',\n      currencyCode: 'SDG',\n      currencySymbol: 'L'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754ba',\n      name: 'Credit Card Account',\n      userId: '5c6cafbf1babb758d297540a',\n      currencyCode: 'BYR',\n      currencySymbol: 'лв'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754bb',\n      name: 'Checking Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'XDR',\n      currencySymbol: 'Ls'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754bc',\n      name: 'Savings Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'PHP',\n      currencySymbol: '£'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754bd',\n      name: 'Investment Account',\n      userId: '5c6cafbf1babb758d2975407',\n      currencyCode: 'ZWL',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754be',\n      name: 'Credit Card Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'DKK',\n      currencySymbol: 'TT$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754bf',\n      name: 'Auto Loan Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'RSD',\n      currencySymbol: 'L'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754c0',\n      name: 'Checking Account',\n      userId: '5c6cafbf1babb758d297540b',\n      currencyCode: 'CHF',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754c1',\n      name: 'Credit Card Account',\n      userId: '5c6cafbf1babb758d2975407',\n      currencyCode: 'ILS',\n      currencySymbol: '﷼'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754c2',\n      name: 'Credit Card Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'BMD',\n      currencySymbol: '﷼'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754c3',\n      name: 'Auto Loan Account',\n      userId: '5c6cafbf1babb758d2975409',\n      currencyCode: 'DOP',\n      currencySymbol: '$'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754c4',\n      name: 'Savings Account',\n      userId: '5c6cafbf1babb758d297540c',\n      currencyCode: 'LVL',\n      currencySymbol: 'Php'\n    },\n    {\n      _id: '5c6cafbf1babb758d29754c5',\n      name: 'Home Loan Account',\n      userId: '5c6cafbf1babb758d297540b',\n      currencyCode: 'XOF',\n      currencySymbol: '₨'\n    }\n  ]\n}\n"
  },
  {
    "path": "test/fixtures/feathers-client.js",
    "content": "import feathers from '@feathersjs/feathers'\nimport socketio from '@feathersjs/socketio-client'\nimport rest from '@feathersjs/rest-client'\nimport axios from 'axios'\nimport auth from '@feathersjs/authentication-client'\nimport io from 'socket.io-client/dist/socket.io'\nimport fixtureSocket from 'can-fixture-socket'\n\nconst mockServer = new fixtureSocket.Server(io)\nconst baseUrl = 'http://localhost:3030'\n\n// These are fixtures used in the service-modulet.test.js under socket events.\nlet id = 0\nmockServer.on('things::create', function (data, params, cb) {\n  data.id = id\n  id++\n  mockServer.emit('things created', data)\n  cb(null, data)\n})\nmockServer.on('things::patch', function (id, data, params, cb) {\n  Object.assign(data, { id, test: true })\n  mockServer.emit('things patched', data)\n  cb(null, data)\n})\nmockServer.on('things::update', function (id, data, params, cb) {\n  Object.assign(data, { id, test: true })\n  mockServer.emit('things updated', data)\n  cb(null, data)\n})\nmockServer.on('things::remove', function (id, obj, cb) {\n  const response = { id, test: true }\n  mockServer.emit('things removed', response)\n  cb(null, response)\n})\n\nlet idDebounce = 0\n\nmockServer.on('things-debounced::create', function (data, obj, cb) {\n  data.id = idDebounce\n  idDebounce++\n  mockServer.emit('things-debounced created', data)\n  cb(null, data)\n})\nmockServer.on('things-debounced::patch', function (id, data, params, cb) {\n  Object.assign(data, { id, test: true })\n  mockServer.emit('things-debounced patched', data)\n  cb(null, data)\n})\nmockServer.on('things-debounced::update', function (id, data, params, cb) {\n  Object.assign(data, { id, test: true })\n  mockServer.emit('things-debounced updated', data)\n  cb(null, data)\n})\nmockServer.on('things-debounced::remove', function (id, params, cb) {\n  const response = { id, test: true }\n  mockServer.emit('things-debounced removed', response)\n  cb(null, response)\n})\n\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nexport function makeFeathersSocketClient(baseUrl) {\n  const socket = io(baseUrl)\n\n  return feathers().configure(socketio(socket)).configure(auth())\n}\n\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nexport function makeFeathersRestClient(baseUrl) {\n  return feathers().configure(rest(baseUrl).axios(axios)).configure(auth())\n}\n\nconst sock = io(baseUrl)\n\nexport const feathersSocketioClient = feathers()\n  .configure(socketio(sock))\n  .configure(auth())\n\nexport const feathersRestClient = feathers()\n  .configure(rest(baseUrl).axios(axios))\n  .configure(auth())\n"
  },
  {
    "path": "test/fixtures/server.js",
    "content": "import feathers from '@feathersjs/feathers'\nimport rest from '@feathersjs/express/rest'\nimport socketio from '@feathersjs/socketio'\nimport bodyParser from 'body-parser'\nimport auth from '@feathersjs/authentication'\nimport jwt from '@feathersjs/authentication-jwt'\nimport memory from 'feathers-memory'\n\nconst app = feathers()\n  .use(bodyParser.json())\n  .use(bodyParser.urlencoded({ extended: true }))\n  .configure(rest())\n  .configure(socketio())\n  .use('/users', memory())\n  .use('/todos', memory())\n  .use('/errors', memory())\n  .configure(\n    auth({\n      secret: 'test',\n      service: '/users'\n    })\n  )\n  .configure(jwt())\n\napp.service('/errors').hooks({\n  before: {\n    all: [\n      hook => {\n        throw new Error(`${hook.method} Denied!`)\n      }\n    ]\n  }\n})\n\nconst port = 3030\nconst server = app.listen(port)\n\nprocess.on('unhandledRejection', (reason, p) =>\n  console.log('Unhandled Rejection at: Promise ', p, reason)\n)\n\nserver.on('listening', () => {\n  console.log(`Feathers application started on localhost:${port}`)\n\n  setTimeout(function() {\n    server.close()\n  }, 50000)\n})\n"
  },
  {
    "path": "test/fixtures/store.js",
    "content": "import Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\nexport default function makeStore() {\n  return new Vuex.Store({\n    state: {\n      count: 0\n    },\n    mutations: {\n      increment(state) {\n        state.count++\n      }\n    }\n  })\n}\n"
  },
  {
    "path": "test/fixtures/todos.js",
    "content": "export function makeTodos() {\n  return {\n    1: { _id: 1, description: 'Dishes', isComplete: true },\n    2: { _id: 2, description: 'Laundry', isComplete: true },\n    3: { _id: 3, description: 'Groceries', isComplete: true }\n  }\n}\n"
  },
  {
    "path": "test/index.test.ts",
    "content": "import { assert } from 'chai'\nimport * as feathersVuex from '../src/index'\nimport Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\ndescribe('feathers-vuex', () => {\n  it('has correct exports', () => {\n    assert(typeof feathersVuex.default === 'function')\n    assert(\n      typeof feathersVuex.FeathersVuex.install === 'function',\n      'has Vue Plugin'\n    )\n    assert(feathersVuex.FeathersVuexFind)\n    assert(feathersVuex.FeathersVuexGet)\n    assert(feathersVuex.initAuth)\n    assert(feathersVuex.makeFindMixin)\n    assert(feathersVuex.makeGetMixin)\n    assert(feathersVuex.models)\n  })\n\n  it('requires a Feathers Client instance', () => {\n    try {\n      feathersVuex.default(\n        {},\n        {\n          serverAlias: 'index-test'\n        }\n      )\n    } catch (error) {\n      assert(\n        error.message ===\n          'The first argument to feathersVuex must be a feathers client.'\n      )\n    }\n  })\n})\n"
  },
  {
    "path": "test/make-find-mixin.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport jsdom from 'jsdom-global'\nimport Vue from 'vue/dist/vue'\nimport Vuex from 'vuex'\nimport feathersVuex, { FeathersVuex } from '../src/index'\nimport makeFindMixin from '../src/make-find-mixin'\nimport { feathersRestClient as feathersClient } from './fixtures/feathers-client'\n\njsdom()\nrequire('events').EventEmitter.prototype._maxListeners = 100\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'make-find-mixin'\n  })\n\n  class FindModel extends BaseModel {\n    public static modelName = 'FindModel'\n    public static test = true\n  }\n\n  return { FindModel, BaseModel, makeServicePlugin }\n}\n\nVue.use(Vuex)\nVue.use(FeathersVuex)\n\ndescribe('Find Mixin', function () {\n  const { makeServicePlugin, FindModel } = makeContext()\n  const serviceName = 'todos'\n  const store = new Vuex.Store({\n    plugins: [\n      makeServicePlugin({\n        Model: FindModel,\n        service: feathersClient.service(serviceName)\n      })\n    ]\n  })\n\n  it('correctly forms mixin data', function () {\n    const todosMixin = makeFindMixin({ service: 'todos' })\n    interface TodosComponent {\n      todos: []\n      todosServiceName: string\n      isFindTodosPending: boolean\n      haveTodosBeenRequestedOnce: boolean\n      haveTodosLoadedOnce: boolean\n      findTodos: Function\n      todosLocal: boolean\n      todosQid: string\n      todosQueryWhen: Function\n      todosParams: any\n      todosFetchParams: any\n    }\n\n    const vm = new Vue({\n      name: 'todos-component',\n      mixins: [todosMixin],\n      store,\n      template: `<div></div>`\n    }).$mount()\n\n    assert.deepEqual(vm.todos, [], 'todos prop was empty array')\n    assert(\n      vm.hasOwnProperty('todosPaginationData'),\n      'pagination data prop was present, even if undefined'\n    )\n    assert(vm.todosServiceName === 'todos', 'service name was correct')\n    assert(vm.isFindTodosPending === false, 'loading boolean is in place')\n    assert(\n      vm.haveTodosBeenRequestedOnce === false,\n      'requested once boolean is in place'\n    )\n    assert(vm.haveTodosLoadedOnce === false, 'loaded once boolean is in place')\n    assert(typeof vm.findTodos === 'function', 'the find action is in place')\n    assert(vm.todosLocal === false, 'local boolean is false by default')\n    assert(\n      typeof vm.$options.created[0] === 'function',\n      'created lifecycle hook function is in place given that local is false'\n    )\n    assert(\n      vm.todosQid === 'default',\n      'the default query identifier is in place'\n    )\n    assert(vm.todosQueryWhen === true, 'the default queryWhen is true')\n    // assert(vm.todosWatch.length === 0, 'the default watch is an empty array')\n    assert(\n      vm.todosParams === undefined,\n      'no params are in place by default, must be specified by the user'\n    )\n    assert(\n      vm.todosFetchParams === undefined,\n      'no fetch params are in place by default, must be specified by the user'\n    )\n  })\n\n  it('correctly forms mixin data for dynamic service', function () {\n    const tasksMixin = makeFindMixin({\n      service() {\n        return this.serviceName\n      },\n      local: true\n    })\n\n    interface TasksComponent {\n      tasks: []\n      serviceServiceName: string\n      isFindTasksPending: boolean\n      findTasks: Function\n      tasksLocal: boolean\n      tasksQid: string\n      tasksQueryWhen: Function\n      tasksParams: any\n      tasksFetchParams: any\n    }\n\n    const vm = new Vue({\n      name: 'tasks-component',\n      data: () => ({\n        serviceName: 'tasks'\n      }),\n      mixins: [tasksMixin],\n      store,\n      template: `<div></div>`\n    }).$mount()\n\n    assert.deepEqual(vm.items, [], 'items prop was empty array')\n    assert(\n      vm.hasOwnProperty('servicePaginationData'),\n      'pagination data prop was present, even if undefined'\n    )\n    assert(vm.serviceServiceName === 'tasks', 'service name was correct')\n    assert(vm.isFindServicePending === false, 'loading boolean is in place')\n    assert(typeof vm.findService === 'function', 'the find action is in place')\n    assert(vm.serviceLocal === true, 'local boolean is set to true')\n    assert(\n      typeof vm.$options.created === 'undefined',\n      'created lifecycle hook function is NOT in place given that local is true'\n    )\n    assert(\n      vm.serviceQid === 'default',\n      'the default query identifier is in place'\n    )\n    assert(vm.serviceQueryWhen === true, 'the default queryWhen is true')\n    // assert(vm.tasksWatch.length === 0, 'the default watch is an empty array')\n    assert(\n      vm.serviceParams === undefined,\n      'no params are in place by default, must be specified by the user'\n    )\n    assert(\n      vm.serviceFetchParams === undefined,\n      'no fetch params are in place by default, must be specified by the user'\n    )\n  })\n})\n"
  },
  {
    "path": "test/service-module/make-service-plugin.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport { ServiceState } from './types'\nimport { clearModels } from '../../src/service-module/global-models'\nimport { clients } from '../../src/service-module/global-clients'\nimport { feathersRestClient as feathers } from '../../test/fixtures/feathers-client'\nimport feathersVuex from '../../src/index'\nimport _pick from 'lodash/pick'\nimport _omit from 'lodash/omit'\n\nVue.use(Vuex)\n\ndescribe('makeServicePlugin', function () {\n  beforeEach(() => {\n    clearModels()\n  })\n\n  it('adds Feathers client to the global clients', () => {\n    feathersVuex(feathers, {\n      serverAlias: 'this is a test'\n    })\n    assert(clients.byAlias['this is a test'], 'got a reference to the client.')\n  })\n\n  it('registers the vuex module with options', function () {\n    interface RootState {\n      todos: {}\n    }\n\n    const serverAlias = 'make-service-plugin'\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathers, {\n      serverAlias\n    })\n    const servicePath = 'make-service-plugin-todos'\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static servicePath = servicePath\n    }\n    const todosPlugin = makeServicePlugin({\n      servicePath,\n      Model: Todo,\n      service: feathers.service(servicePath),\n      namespace: 'make-service-plugin-todos'\n    })\n    const store = new Vuex.Store<RootState>({ plugins: [todosPlugin] })\n\n    const keys = Object.keys(store.state['make-service-plugin-todos'])\n    const received = _pick(store.state['make-service-plugin-todos'], keys)\n    const expected = {\n      addOnUpsert: false,\n      autoRemove: false,\n      debug: false,\n      copiesById: {},\n      enableEvents: true,\n      errorOnCreate: null,\n      errorOnFind: null,\n      errorOnGet: null,\n      errorOnPatch: null,\n      errorOnRemove: null,\n      errorOnUpdate: null,\n      idField: 'id',\n      tempIdField: '__id',\n      ids: [],\n      isCreatePending: false,\n      isFindPending: false,\n      isGetPending: false,\n      isPatchPending: false,\n      isRemovePending: false,\n      isUpdatePending: false,\n      keepCopiesInStore: false,\n      debounceEventsTime: null,\n      debounceEventsMaxWait: 1000,\n      keyedById: {},\n      modelName: 'Todo',\n      nameStyle: 'short',\n      namespace: 'make-service-plugin-todos',\n      pagination: {\n        defaultLimit: null,\n        defaultSkip: null\n      },\n      paramsForServer: ['$populateParams'],\n      preferUpdate: false,\n      replaceItems: false,\n      serverAlias: 'make-service-plugin',\n      servicePath: 'make-service-plugin-todos',\n      skipRequestIfExists: false,\n      tempsById: {},\n      whitelist: [],\n      isIdCreatePending: [],\n      isIdUpdatePending: [],\n      isIdPatchPending: [],\n      isIdRemovePending: []\n    }\n\n    assert.deepEqual(_omit(received), _omit(expected), 'defaults in place.')\n  })\n\n  it('sets up Model.store && service.FeathersVuexModel', function () {\n    const serverAlias = 'make-service-plugin'\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathers, {\n      serverAlias\n    })\n\n    const servicePath = 'make-service-plugin-todos'\n    const service = feathers.service(servicePath)\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static servicePath = servicePath\n    }\n    const todosPlugin = makeServicePlugin({ servicePath, Model: Todo, service })\n    const store = new Vuex.Store({ plugins: [todosPlugin] })\n\n    assert(Todo.store === store, 'the store is on the Model!')\n    // @ts-ignore\n    assert.equal(service.FeathersVuexModel, Todo, 'Model accessible on service')\n  })\n\n  it('allows accessing other models', function () {\n    const serverAlias = 'make-service-plugin'\n    const { makeServicePlugin, BaseModel, models } = feathersVuex(feathers, {\n      idField: '_id',\n      serverAlias\n    })\n\n    const servicePath = 'make-service-plugin-todos'\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static servicePath = servicePath\n    }\n    const todosPlugin = makeServicePlugin({\n      servicePath,\n      Model: Todo,\n      service: feathers.service(servicePath)\n    })\n\n    const store = new Vuex.Store({\n      plugins: [todosPlugin]\n    })\n\n    assert(models[serverAlias][Todo.name] === Todo)\n    assert(Todo.store === store)\n  })\n\n  it('allows service specific handleEvents', async function () {\n    // feathers.use('todos', new TodosService())\n    const serverAlias = 'make-service-plugin'\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathers, {\n      idField: '_id',\n      serverAlias\n    })\n\n    const servicePath = 'make-service-plugin-todos'\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static servicePath = servicePath\n      public static namespace = 'make-service-plugin-todos'\n    }\n\n    let createdCalled = false\n    let updatedCalled = false\n    let patchedCalled = false\n    let removedCalled = false\n    const todosPlugin = makeServicePlugin({\n      servicePath,\n      Model: Todo,\n      service: feathers.service(servicePath),\n      handleEvents: {\n        created() {\n          createdCalled = true\n          return true\n        },\n        updated() {\n          updatedCalled = true\n          return true\n        },\n        patched() {\n          patchedCalled = true\n          return true\n        },\n        removed() {\n          removedCalled = true\n          return true\n        }\n      }\n    })\n\n    const store = new Vuex.Store({\n      plugins: [todosPlugin]\n    })\n\n    const todo = new Todo()\n\n    // Fake server call\n    feathers.service('make-service-plugin-todos').hooks({\n      before: {\n        create: [\n          context => {\n            delete context.data.__id\n            delete context.data.__isTemp\n          },\n          context => {\n            context.result = { _id: 24, ...context.data }\n            return context\n          }\n        ],\n        update: [\n          context => {\n            context.result = { ...context.data }\n            return context\n          }\n        ],\n        patch: [\n          context => {\n            context.result = { ...context.data }\n            return context\n          }\n        ],\n        remove: [\n          context => {\n            context.result = { ...todo }\n            return context\n          }\n        ]\n      }\n    })\n\n    await todo.create()\n    assert(createdCalled, 'created handler called')\n\n    await todo.update()\n    assert(updatedCalled, 'updated handler called')\n\n    await todo.patch()\n    assert(patchedCalled, 'patched handler called')\n\n    await todo.remove()\n    assert(removedCalled, 'removed handler called')\n  })\n\n  it('fall back to globalOptions handleEvents if service specific handleEvents handler is missing', async function () {\n    // feathers.use('todos', new TodosService())\n    const serverAlias = 'make-service-plugin'\n\n    let globalCreatedCalled = false\n    let globalUpdatedCalled = false\n    let globalPatchedCalled = false\n    let globalRemovedCalled = false\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathers, {\n      idField: '_id',\n      serverAlias,\n      handleEvents: {\n        created() {\n          globalCreatedCalled = true\n          return true\n        },\n        updated() {\n          globalUpdatedCalled = true\n          return true\n        },\n        patched() {\n          globalPatchedCalled = true\n          return true\n        },\n        removed() {\n          globalRemovedCalled = true\n          return true\n        }\n      }\n    })\n\n    const servicePath = 'make-service-plugin-todos'\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static servicePath = servicePath\n      public static namespace = 'make-service-plugin-todos'\n    }\n\n    let specificUpdatedCalled = false\n    const todosPlugin = makeServicePlugin({\n      servicePath,\n      Model: Todo,\n      service: feathers.service(servicePath),\n      namespace: 'make-service-plugin-todos',\n      handleEvents: {\n        updated() {\n          specificUpdatedCalled = true\n          return true\n        }\n      }\n    })\n\n    const store = new Vuex.Store({\n      plugins: [todosPlugin]\n    })\n\n    const todo = new Todo()\n\n    // Fake server call\n    feathers.service('make-service-plugin-todos').hooks({\n      before: {\n        create: [\n          context => {\n            delete context.data.__id\n            delete context.data.__isTemp\n          },\n          context => {\n            context.result = { _id: 24, ...context.data }\n            return context\n          }\n        ],\n        update: [\n          context => {\n            context.result = { ...context.data }\n            return context\n          }\n        ],\n        patch: [\n          context => {\n            context.result = { ...context.data }\n            return context\n          }\n        ],\n        remove: [\n          context => {\n            context.result = { ...todo }\n            return context\n          }\n        ]\n      }\n    })\n\n    await todo.create()\n    assert(globalCreatedCalled, 'global created handler called')\n\n    await todo.update()\n    assert(specificUpdatedCalled, 'specific updated handler called')\n    assert(!globalUpdatedCalled, 'global updated handler NOT called')\n\n    await todo.patch()\n    assert(globalPatchedCalled, 'global patched handler called')\n\n    await todo.remove()\n    assert(globalRemovedCalled, 'global removed handler called')\n  })\n\n  it('allow handleEvents handlers to return extracted event data', async function () {\n    const serverAlias = 'make-service-plugin'\n\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathers, {\n      idField: '_id',\n      serverAlias,\n      handleEvents: {\n        created(e) {\n          return [true, e.myCreatedPropWithActualData]\n        },\n        updated(e) {\n          return [true, e.myUpdatedPropWithActualData]\n        },\n        patched(e) {\n          return [true, e.myPatchedPropWithActualData]\n        },\n        removed(e) {\n          return [true, e.myRemovedPropWithActualData]\n        }\n      }\n    })\n\n    const servicePath = 'make-service-plugin-todos'\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static servicePath = servicePath\n    }\n\n    const todosService = feathers.service(servicePath)\n    const todosPlugin = makeServicePlugin({\n      servicePath,\n      Model: Todo,\n      service: todosService,\n      namespace: 'make-service-plugin-todos'\n    })\n\n    const store = new Vuex.Store<{ todos: ServiceState }>({\n      plugins: [todosPlugin]\n    })\n    const { keyedById } = store.state['make-service-plugin-todos']\n\n    let createdData = null\n    let updatedData = null\n    let patchedData = null\n    let removedData = null\n    Todo.on('created', e => (createdData = e))\n    Todo.on('updated', e => (updatedData = e))\n    Todo.on('patched', e => (patchedData = e))\n    Todo.on('removed', e => (removedData = e))\n\n    assert(Object.keys(keyedById).length === 0, 'no todos in store')\n\n    todosService.emit('created', {\n      context: 'foo',\n      myCreatedPropWithActualData: { _id: 42, text: '' }\n    })\n    assert(keyedById[42], 'todo added to store')\n    assert(keyedById[42].text === '', 'todo string is empty')\n    assert(createdData, \"Model's created event fired\")\n    assert(\n      createdData.context === 'foo' && createdData.myCreatedPropWithActualData,\n      \"Model's created event got all event data\"\n    )\n\n    todosService.emit('updated', {\n      context: 'bar',\n      myUpdatedPropWithActualData: { _id: 42, text: 'updated' }\n    })\n    assert(keyedById[42].text === 'updated', 'todo was updated')\n    assert(updatedData, \"Model's updated event fired\")\n    assert(\n      updatedData.context === 'bar' && updatedData.myUpdatedPropWithActualData,\n      \"Model's updated event got all event data\"\n    )\n\n    todosService.emit('patched', {\n      context: 'baz',\n      myPatchedPropWithActualData: { _id: 42, text: 'patched' }\n    })\n    assert(keyedById[42].text === 'patched', 'todo was patched')\n    assert(patchedData, \"Model's patched event fired\")\n    assert(\n      patchedData.context === 'baz' && patchedData.myPatchedPropWithActualData,\n      \"Model's patched event got all event data\"\n    )\n\n    todosService.emit('removed', {\n      context: 'spam',\n      myRemovedPropWithActualData: { _id: 42 }\n    })\n    assert(Object.keys(keyedById).length === 0, 'todo removed from store')\n    assert(removedData, \"Model's removed event fired\")\n    assert(\n      removedData.context === 'spam' && removedData.myRemovedPropWithActualData,\n      \"Model's removed event got all event data\"\n    )\n  })\n})\n"
  },
  {
    "path": "test/service-module/misconfigured-client.test.ts",
    "content": "/* eslint @typescript-eslint/ban-ts-ignore:0 */\nimport { assert } from 'chai'\nimport feathersVuex from '../../src/index'\nimport feathers from '@feathersjs/client'\nimport auth from '@feathersjs/authentication-client'\n\n// @ts-ignore\nconst feathersClient = feathers().configure(auth())\n\ndescribe('Service Module - Bad Client Setup', () => {\n  it('throws an error when no client transport plugin is registered', () => {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'misconfigured'\n    })\n    class MisconfiguredTask extends BaseModel {\n      public static modelName = 'MisconfiguredTask'\n      public static test = true\n    }\n\n    try {\n      makeServicePlugin({\n        Model: MisconfiguredTask,\n        service: feathersClient.service('misconfigured-todos')\n      })\n    } catch (error) {\n      assert(\n        error.message.includes(\n          'No service was provided. If you passed one in, check that you have configured a transport plugin on the Feathers Client. Make sure you use the client version of the transport.'\n        ),\n        'got an error with a misconfigured client'\n      )\n    }\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-base.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport { clearModels } from '../../src/service-module/global-models'\nimport {\n  feathersRestClient as feathers,\n  makeFeathersRestClient\n} from '../fixtures/feathers-client'\nimport feathersVuex from '../../src/index'\n\nVue.use(Vuex)\n\nprocess.setMaxListeners(100)\n\ndescribe.skip('Model - Standalone', function () {\n  it.skip('allows using a model without a service', function () {})\n  it.skip('rename serverAlias to just `alias` or maybe `groupName`', function () {})\n})\n\ndescribe('makeModel / BaseModel', function () {\n  before(() => {\n    clearModels()\n  })\n\n  it('properly sets up the BaseModel', function () {\n    const alias = 'model-base'\n    const { BaseModel } = feathersVuex(feathers, { serverAlias: alias })\n    const {\n      name,\n      store,\n      namespace,\n      idField,\n      preferUpdate,\n      serverAlias,\n      models,\n      copiesById\n    } = BaseModel\n\n    assert(name === 'BaseModel', 'name in place')\n\n    // Monkey patched onto the Model class in `makeServicePlugin()`\n    assert(!store, 'no store by default')\n    assert(!namespace, 'no namespace by default')\n\n    assert(idField === 'id', 'default idField is id')\n    assert(!preferUpdate, 'prefer fetch by default')\n\n    // Readonly props\n    assert(serverAlias === 'model-base', 'serverAlias')\n    assert(models, 'models are available')\n    assert.equal(Object.keys(copiesById).length, 0, 'copiesById is empty')\n\n    // Static Methods\n    const staticMethods = [\n      'getId',\n      'find',\n      'findInStore',\n      'count',\n      'countInStore',\n      'get',\n      'getFromStore'\n    ]\n    staticMethods.forEach(method => {\n      assert(typeof BaseModel[method] === 'function', `has ${method} method`)\n    })\n\n    // Prototype Methods\n    const prototypeMethods = [\n      'clone',\n      'reset',\n      'commit',\n      'save',\n      'create',\n      'patch',\n      'update',\n      'remove'\n    ]\n    prototypeMethods.forEach(method => {\n      assert(\n        typeof BaseModel.prototype[method] === 'function',\n        `has ${method} method`\n      )\n    })\n\n    // Utility Methods\n    const utilityMethods = ['hydrateAll']\n    utilityMethods.forEach(method => {\n      assert(typeof BaseModel[method] === 'function', `has ${method} method`)\n    })\n\n    const eventMethods = [\n      'on',\n      'off',\n      'once',\n      'emit',\n      'addListener',\n      'removeListener',\n      'removeAllListeners'\n    ]\n    eventMethods.forEach(method => {\n      assert(typeof BaseModel[method] === 'function', `has ${method} method`)\n    })\n\n    const getterMethods = [\n      'isCreatePending',\n      'isUpdatePending',\n      'isPatchPending',\n      'isRemovePending',\n      'isSavePending',\n      'isPending'\n    ]\n    const m = new BaseModel()\n    getterMethods.forEach(method => {\n      assert(\n        typeof Object.getOwnPropertyDescriptor(Object.getPrototypeOf(m), method).get === 'function',\n        `has ${method} getter`\n      )\n    })\n  })\n\n  it('allows customization through the FeathersVuexOptions', function () {\n    const { BaseModel } = feathersVuex(feathers, {\n      serverAlias: 'myApi',\n      idField: '_id',\n      preferUpdate: true\n    })\n    const { idField, preferUpdate, serverAlias } = BaseModel\n\n    assert(idField === '_id', 'idField was set')\n    assert(preferUpdate, 'turned on preferUpdate')\n    assert(serverAlias === 'myApi', 'serverAlias was set')\n  })\n\n  it('receives store & other props after Vuex plugin is registered', function () {\n    const { BaseModel, makeServicePlugin } = feathersVuex(feathers, {\n      serverAlias: 'myApi'\n    })\n    BaseModel.modelName = 'TestModel'\n    const plugin = makeServicePlugin({\n      servicePath: 'todos',\n      service: feathers.service('todos'),\n      Model: BaseModel\n    })\n    new Vuex.Store({\n      plugins: [plugin]\n    })\n    const { store, namespace, servicePath } = BaseModel\n\n    assert(store, 'store is in place')\n    assert.equal(namespace, 'todos', 'namespace is in place')\n    assert.equal(servicePath, 'todos', 'servicePath is in place')\n  })\n\n  it('allows access to other models after Vuex plugins are registered', function () {\n    const serverAlias = 'model-base'\n    const { makeServicePlugin, BaseModel, models } = feathersVuex(feathers, {\n      idField: '_id',\n      serverAlias\n    })\n\n    // Create a Todo Model & Plugin\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public test = true\n    }\n    const todosPlugin = makeServicePlugin({\n      servicePath: 'todos',\n      Model: Todo,\n      service: feathers.service('todos')\n    })\n\n    // Create a Task Model & Plugin\n    class Task extends BaseModel {\n      public static modelName = 'Task'\n      public test = true\n    }\n    const tasksPlugin = makeServicePlugin({\n      servicePath: 'tasks',\n      Model: Task,\n      service: feathers.service('tasks')\n    })\n\n    // Register the plugins\n    new Vuex.Store({\n      plugins: [todosPlugin, tasksPlugin]\n    })\n\n    assert(models[serverAlias][Todo.name] === Todo)\n    assert.equal(Todo.models, models, 'models available at Model.models')\n    assert.equal(Task.models, models, 'models available at Model.models')\n  })\n\n  it('works with multiple, independent Feathers servers', function () {\n    // Create a Todo Model & Plugin on myApi\n    const feathersMyApi = makeFeathersRestClient('https://api.my-api.com')\n    const myApi = feathersVuex(feathersMyApi, {\n      idField: '_id',\n      serverAlias: 'myApi'\n    })\n    class Todo extends myApi.BaseModel {\n      public static modelName = 'Todo'\n      public test = true\n    }\n    const todosPlugin = myApi.makeServicePlugin({\n      Model: Todo,\n      service: feathersMyApi.service('todos')\n    })\n\n    // Create a Task Model & Plugin on theirApi\n    const feathersTheirApi = makeFeathersRestClient('https://api.their-api.com')\n    const theirApi = feathersVuex(feathersTheirApi, {\n      serverAlias: 'theirApi'\n    })\n    class Task extends theirApi.BaseModel {\n      public static modelName = 'Task'\n      public test = true\n    }\n    const tasksPlugin = theirApi.makeServicePlugin({\n      Model: Task,\n      service: feathersTheirApi.service('tasks')\n    })\n\n    // Register the plugins\n    new Vuex.Store({\n      plugins: [todosPlugin, tasksPlugin]\n    })\n    const { models } = myApi\n\n    assert(models.myApi.Todo === Todo)\n    assert(!models.theirApi.Todo, `Todo stayed out of the 'theirApi' namespace`)\n    assert(models.theirApi.Task === Task)\n    assert(!models.myApi.Task, `Task stayed out of the 'myApi' namespace`)\n\n    assert.equal(\n      models.myApi.byServicePath[Todo.servicePath],\n      Todo,\n      'also registered in models.byServicePath'\n    )\n    assert.equal(\n      models.theirApi.byServicePath[Task.servicePath],\n      Task,\n      'also registered in models.byServicePath'\n    )\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-instance-defaults.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { ServiceState, Location } from './types'\nimport { assert } from 'chai'\nimport feathersVuex, { models } from '../../src/index'\nimport { mergeWithAccessors } from '../../src/utils'\nimport { clearModels } from '../../src/service-module/global-models'\nimport {\n  makeFeathersRestClient,\n  feathersRestClient as feathersClient,\n  feathersSocketioClient\n} from '../fixtures/feathers-client'\nimport Vuex from 'vuex'\nimport { makeContext as makeLetterContext } from './model-methods.test'\n\ninterface TodoState extends ServiceState {\n  test: any\n  test2: {\n    test: boolean\n  }\n  isTrue: boolean\n}\ninterface RootState {\n  todos: TodoState\n  tasks: ServiceState\n  tests: ServiceState\n  blah: ServiceState\n  things: ServiceState\n}\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'service-module'\n  })\n\n  class Todo extends BaseModel {\n    public constructor(data = {}, options?) {\n      super(data, options)\n    }\n    public static modelName = 'Todo'\n    public description: string\n  }\n  class Person extends BaseModel {\n    public constructor(data = {}, options?) {\n      super(data, options)\n    }\n    public static modelName = 'Person'\n    public static test = true\n  }\n  class Item extends BaseModel {\n    public static modelName = 'Item'\n    public static test = true\n  }\n  class Task extends BaseModel {\n    public static modelName = 'Task'\n    public static test = true\n  }\n  class Car extends BaseModel {\n    public static modelName = 'Car'\n    public static test = true\n  }\n  class Group extends BaseModel {\n    public static modelName = 'Group'\n    public static test = true\n  }\n  class Test extends BaseModel {\n    public static modelName = 'Test'\n    public static test = true\n  }\n  class Thing extends BaseModel {\n    public static modelName = 'Thing'\n    public static test = true\n  }\n\n  const todosPlugin = makeServicePlugin({\n    Model: Todo,\n    service: feathersClient.service('service-todos')\n  })\n  const store = new Vuex.Store<RootState>({\n    plugins: [\n      todosPlugin,\n      makeServicePlugin({\n        Model: Person,\n        service: feathersClient.service('people')\n      }),\n      makeServicePlugin({\n        Model: Car,\n        service: feathersClient.service('cars')\n      }),\n      makeServicePlugin({\n        Model: Group,\n        service: feathersClient.service('groups')\n      })\n    ]\n  })\n\n  return {\n    makeServicePlugin,\n    BaseModel,\n    Todo,\n    Person,\n    Item,\n    Task,\n    Car,\n    Group,\n    Test,\n    Thing,\n    store\n  }\n}\n\ndescribe('Models - Default Values', function() {\n  beforeEach(() => {\n    clearModels()\n  })\n\n  it('models default to an empty object when there is no BaseModel.store', function() {\n    const { BaseModel } = makeContext()\n\n    // Since we're not using this NakedTodo model in a service plugin, it doesn't get\n    // monkey patched with the store.\n    class NakedTodo extends BaseModel {\n      public static modelName = 'NakedTodo'\n      public static test = true\n    }\n    const todo = new NakedTodo()\n\n    assert.deepEqual(todo.toJSON(), {}, 'default model is an empty object')\n  })\n\n  it('models have tempIds when there is a store', function() {\n    const { Todo } = makeContext()\n    const todo = new Todo()\n\n    const expectedProps = ['__id', '__isTemp']\n\n    assert.deepEqual(\n      Object.keys(todo),\n      expectedProps,\n      'default model is a temp'\n    )\n  })\n\n  it('adds new instances containing an id to the store', function() {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      id: 1,\n      description: 'test',\n      isComplete: true\n    })\n    const todoInStore = Todo.store.state['service-todos'].keyedById[1]\n\n    assert.deepEqual(todoInStore, todo, 'task was added to the store')\n  })\n\n  it('stores clones in Model.copiesById by default', function() {\n    const { Todo } = makeContext()\n    const todo = new Todo({ id: 1, description: 'This is the original' })\n\n    assert.deepEqual(\n      Todo.copiesById,\n      {},\n      'Model.copiesById should start out empty'\n    )\n\n    const todoClone = todo.clone()\n    assert(Todo.copiesById[1], 'should have a copy stored on Model.copiesById')\n\n    todoClone.description = `I'm a clone, now!`\n    todoClone.commit()\n\n    assert.equal(\n      todo.description,\n      `I'm a clone, now!`,\n      'the original should have been updated'\n    )\n  })\n\n  it('each model has its own Model.copiesById', function() {\n    const { Todo, Person } = makeContext()\n    const todo = new Todo({ id: 1, description: 'This is the original' })\n    const person = new Person({ id: 2, name: 'Xavier' })\n\n    todo.clone()\n    assert(Todo.copiesById[1], 'should have a copy stored on Todo.copiesById')\n    assert(\n      !Person.copiesById[1],\n      'should not have a copy stored on Person.copiesById'\n    )\n\n    person.clone()\n    assert(\n      Person.copiesById[2],\n      'should have a copy stored on Person.copiesById'\n    )\n    assert(\n      !Todo.copiesById[2],\n      'should not have a copy stored on Todo.copiesById'\n    )\n  })\n\n  it('allows instance defaults, including getters and setters', function() {\n    const { BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'instance-defaults'\n    })\n\n    class Car extends BaseModel {\n      public id?\n      public year = 1905\n      public make = 'Tesla'\n      public model = 'Roadster'\n      public get combined(): string {\n        return `${this.year} ${this.make} ${this.model}`\n      }\n      public set yearBeforeCurrent(year) {\n        if (year < this.year) {\n          this.year = year\n        }\n      }\n\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n\n    const car = new Car()\n\n    assert.equal(car.year, 1905, 'default year set')\n    assert.equal(car.make, 'Tesla', 'default make set')\n    assert.equal(car.model, 'Roadster', 'default model set')\n    assert.equal(car.combined, '1905 Tesla Roadster', 'getters work, too!')\n\n    car.yearBeforeCurrent = 1900\n\n    assert.equal(car.combined, '1900 Tesla Roadster', 'setters work, too!')\n  })\n\n  it('allows overriding default values in the constructor', function() {\n    const { BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'instance-defaults'\n    })\n\n    class Car extends BaseModel {\n      public id?\n      public year = 1905\n      public make = 'Tesla'\n      public model = 'Roadster'\n\n      public constructor(data?, options?) {\n        super(data, options)\n        if (this.make === 'Tesla') {\n          this.make = 'Porsche'\n        }\n      }\n    }\n\n    const car = new Car()\n\n    assert.equal(car.make, 'Porsche', 'default make set')\n  })\n\n  it(`uses the class defaults if you don't override them in the constructor`, function() {\n    const { BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'instance-defaults'\n    })\n\n    class Person extends BaseModel {\n      public id?\n      public firstName = 'Harry'\n      public location: Location = {\n        coordinates: [0, 0]\n      }\n\n      public constructor(data?, options?) {\n        // Calling super calls the BaseModel constructor, which merges the data\n        // onto `this`.\n        super(data, options)\n\n        // Once the BaseModel constructor has finished, the props in the class\n        // definition are applied to `this` before running any additional code in the\n        // extending class's constructor. This means that at this point, all\n        // new instances have `location.coordinates = [0, 0]`\n\n        // Since we're not re-applying the `data` to `this`, the class defaults have\n        // overwritten whatever we passed in.\n        return this\n      }\n    }\n\n    const location: Location = {\n      coordinates: [1, 1]\n    }\n\n    const person1 = new Person({ firstName: 'Marshall', location })\n    const person2 = new Person({ firstName: 'Austin', location })\n    const areSame = person1.location === person2.location\n\n    assert(!areSame, 'the locations are different objects')\n    assert(person1.firstName === 'Harry', 'the defaults replaced our args')\n    assert(person2.firstName === 'Harry', 'the defaults replaced our args')\n\n    // See, even the location we passed in was overwritten by the defaults.\n    assert.deepEqual(person1.location.coordinates, [0, 0], 'defaults won')\n  })\n\n  it('does not share nested objects between instances when you override class defaults in the constructor', function() {\n    const { BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'instance-defaults'\n    })\n\n    class Person extends BaseModel {\n      public id?\n      public firstName: string\n      public location: Location = {\n        coordinates: [0, 0]\n      }\n\n      public constructor(data?, options?) {\n        // Pass { merge: false } in the third arg to prevent BaseModel from\n        // doing its own merge\n        super(data, { merge: false })\n\n        // Calling merge here overwrites the Class's default location.\n        // You could also write `this.location = data.location`\n        Person.merge(this, data)\n      }\n    }\n\n    const location: Location = {\n      coordinates: [1, 1]\n    }\n\n    // Look, I'm passing in location with coordinates [1, 1]\n    const person1 = new Person({ firstName: 'Marshall', location })\n    const person2 = new Person({ firstName: 'Austin', location })\n    const areSame = person1.location === person2.location\n\n    // But the objects are distinct because they've been merged in the constructor\n    assert(!areSame, 'the locations are different objects')\n  })\n\n  it('allows passing instanceDefaults in the service plugin options', function() {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'instance-defaults'\n    })\n\n    class Person extends BaseModel {\n      public static modelName = 'Person'\n\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n\n    const location: Location = {\n      coordinates: [1, 1]\n    }\n\n    new Vuex.Store({\n      plugins: [\n        makeServicePlugin({\n          Model: Person,\n          service: feathersClient.service('people'),\n          instanceDefaults: () => ({\n            firstName: 'Harry',\n            lastName: 'Potter',\n            location,\n            get fullName() {\n              return `${this.firstName} ${this.lastName}`\n            },\n            set fullName(val) {\n              const [firstName, lastName] = val.split(' ')\n              Object.assign(this, { firstName, lastName })\n            }\n          })\n        })\n      ]\n    })\n\n    const person1 = new Person({ firstName: 'Marshall', lastName: 'Thompson' })\n    const person2 = new Person({\n      firstName: 'Kai',\n      location: { coordinates: [0, 0] },\n      fullName: 'Jerry Seinfeld'\n    })\n    const areSame = person1.location === person2.location\n    assert(!areSame, 'nested objects are unique')\n\n    assert.equal(person1.lastName, 'Thompson', 'person1 has correct lastName')\n    assert.equal(person2.lastName, 'Potter', 'person2 got default lastName')\n    assert.deepEqual(\n      person1.location.coordinates,\n      [1, 1],\n      'person1 got default location'\n    )\n    assert.deepEqual(\n      person2.location.coordinates,\n      [0, 0],\n      'person2 got provided location'\n    )\n    assert.equal(person1.fullName, 'Marshall Thompson', 'getter is in place')\n    assert.equal(person2.fullName, 'Kai Potter', 'getter is still in place')\n\n    person1.fullName = 'Marshall Me'\n    person2.fullName = 'Kai Me'\n\n    assert.equal(person1.firstName, 'Marshall', 'firstName was set')\n    assert.equal(person1.lastName, 'Me', 'lastName was set')\n    assert.equal(person2.firstName, 'Kai', 'firstName was set')\n    assert.equal(person2.lastName, 'Me', 'lastName was set')\n  })\n\n  it('instanceDefault accessors stay intact with clone and commit', function() {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'instance-defaults'\n    })\n\n    class Person extends BaseModel {\n      public static modelName = 'Person'\n\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n\n    const location: Location = {\n      coordinates: [1, 1]\n    }\n\n    new Vuex.Store({\n      plugins: [\n        makeServicePlugin({\n          Model: Person,\n          service: feathersClient.service('people'),\n          instanceDefaults: () => ({\n            firstName: 'Harry',\n            lastName: 'Potter',\n            location,\n            get fullName() {\n              return `${this.firstName} ${this.lastName}`\n            },\n            set fullName(val) {\n              const [firstName, lastName] = val.split(' ')\n              Object.assign(this, { firstName, lastName })\n            }\n          })\n        })\n      ]\n    })\n\n    const person = new Person({ firstName: 'Marshall', lastName: 'Thompson' })\n\n    // Clone the person\n    const clone = person.clone()\n\n    // Check the getter\n    clone.firstName = 'FeathersJS'\n    clone.lastName = 'Developer'\n    assert.equal(clone.fullName, 'FeathersJS Developer', 'getter is in place')\n\n    // Check the setter\n    clone.fullName = 'Marshall Me'\n    assert.equal(\n      `${clone.firstName} ${clone.lastName}`,\n      'Marshall Me',\n      'Setter is in place'\n    )\n\n    // Commit the clone\n    clone.commit()\n\n    //Check the getter\n    person.firstName = 'FeathersJS'\n    person.lastName = 'Developer'\n    assert.equal(person.fullName, 'FeathersJS Developer', 'getter is in place')\n\n    // Check the setter\n    person.fullName = 'Scooby Doo'\n    assert.equal(\n      `${person.firstName} ${person.lastName}`,\n      'Scooby Doo',\n      'Setter is in place'\n    )\n  })\n\n  it('instanceDefaults in place after patch', async function() {\n    const { Letter, store, lettersService } = makeLetterContext()\n    let letter = new Letter({ name: 'Garmadon', age: 1025 })\n\n    letter = await letter.save()\n\n    assert.equal(typeof letter.to, 'string', 'default to field still in place')\n    assert.equal(typeof letter.status, 'string', 'accessor prop still in place')\n\n    letter = await letter.save()\n\n    assert.equal(typeof letter.to, 'string', 'default to field still in place')\n    assert.equal(typeof letter.status, 'string', 'accessor prop still in place')\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-methods.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { ServiceState } from './types'\nimport { assert } from 'chai'\nimport feathersVuex from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport Vuex from 'vuex'\nimport { clearModels } from '../../src/service-module/global-models'\nimport memory from 'feathers-memory'\nimport { makeStore } from '../test-utils'\nimport { isDate } from 'date-fns'\n\nrequire('events').EventEmitter.prototype._maxListeners = 100\n\ninterface TodoState extends ServiceState {\n  test: any\n  test2: {\n    test: boolean\n  }\n  isTrue: boolean\n}\ninterface RootState {\n  ['model-methods-persons']: ServiceState\n  ['model-methods-todos']: TodoState\n  ['model-methods-tasks']: ServiceState\n  tests: ServiceState\n  blah: ServiceState\n  things: ServiceState\n}\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'model-methods'\n  })\n\n  const serialize = context => {\n    context.data = JSON.parse(JSON.stringify(context.data))\n  }\n  const deserialize = context => {\n    context.result = JSON.parse(JSON.stringify(context.result))\n  }\n\n  feathersClient.use('model-methods-letters', memory())\n\n  const lettersService = feathersClient.service('model-methods-letters')\n\n  // Setup hooks on letters service to simulate toJSON serialization that occurs\n  // with a remote API request.\n  lettersService.hooks({\n    before: {\n      create: [serialize],\n      update: [serialize],\n      patch: [serialize]\n    },\n    after: {\n      create: [deserialize],\n      patch: [deserialize],\n      update: [deserialize]\n    }\n  })\n\n  class Task extends BaseModel {\n    public static modelName = 'Task'\n    public static servicePath: 'model-methods-tasks'\n    public constructor(data?, options?) {\n      super(data, options)\n    }\n  }\n  class Todo extends BaseModel {\n    public static modelName = 'Todo'\n    public static servicePath: 'model-methods-todos'\n    public constructor(data?, options?) {\n      super(data, options)\n    }\n  }\n\n  class Letter extends BaseModel {\n    public constructor(data?, options?) {\n      super(data, options)\n    }\n    public static modelName = 'Letter'\n    public static servicePath = 'model-methods-letters'\n    public static instanceDefaults(data, { models, store }) {\n      return {\n        to: '',\n        from: ''\n      }\n    }\n    public static setupInstance(data, { models }) {\n      if (typeof data.createdAt === 'string') {\n        data.createdAt = new Date(data.createdAt) // just assuming the date is formatted correctly ;)\n      }\n      return data\n    }\n    public get status() {\n      return 'pending'\n    }\n  }\n\n  class Person extends BaseModel {\n    public static modelName = 'Person'\n    public static servicePath = 'model-methods-persons'\n    public constructor(data?, options?) {\n      super(data, options)\n    }\n  }\n\n  const store = new Vuex.Store<RootState>({\n    strict: true,\n    plugins: [\n      makeServicePlugin({\n        Model: Task,\n        servicePath: 'model-methods-tasks',\n        service: feathersClient.service('model-methods-tasks'),\n        preferUpdate: true,\n        namespace: 'model-methods-tasks'\n      }),\n      makeServicePlugin({\n        Model: Todo,\n        servicePath: 'model-methods-todos',\n        service: feathersClient.service('model-methods-todos'),\n        namespace: 'model-methods-todos'\n      }),\n      makeServicePlugin({\n        Model: Letter,\n        servicePath: 'model-methods-letters',\n        service: feathersClient.service('model-methods-letters'),\n        namespace: 'model-methods-letters'\n      }),\n      makeServicePlugin({\n        Model: Person,\n        servicePath: 'model-methods-persons',\n        service: feathersClient.service('model-methods-persons'),\n        keepCopiesInStore: true,\n        namespace: 'model-methods-persons'\n      })\n    ]\n  })\n\n  // Fake server call\n  feathersClient.service('model-methods-tasks').hooks({\n    before: {\n      create: [\n        context => {\n          delete context.data.__id\n          delete context.data.__isTemp\n        },\n        context => {\n          context.result = { _id: 24, ...context.data }\n          return context\n        }\n      ],\n      update: [\n        context => {\n          context.result = { ...context.data }\n          return context\n        }\n      ],\n      patch: [\n        context => {\n          context.result = { ...context.data }\n          return context\n        }\n      ],\n      remove: [\n        context => {\n          context.result = {}\n          return context\n        }\n      ]\n    }\n  })\n\n  // Fake server call\n  feathersClient.service('model-methods-persons').hooks({\n    before: {\n      create: [\n        context => {\n          delete context.data.__id\n          delete context.data.__isTemp\n        },\n        context => {\n          context.result = { _id: 24, ...context.data }\n          return context\n        }\n      ],\n      update: [\n        context => {\n          context.result = { ...context.data }\n          return context\n        }\n      ],\n      patch: [\n        context => {\n          context.result = { ...context.data }\n          return context\n        }\n      ],\n      remove: [\n        context => {\n          context.result = {}\n          return context\n        }\n      ]\n    }\n  })\n\n  return {\n    BaseModel,\n    Task,\n    Todo,\n    Letter,\n    Person,\n    lettersService,\n    store\n  }\n}\n\nexport { makeContext }\n\ndescribe('Models - Methods', function () {\n  beforeEach(() => {\n    clearModels()\n  })\n\n  it('Model.find is a function', function () {\n    const { Task } = makeContext()\n\n    assert(typeof Task.find === 'function')\n  })\n\n  it('Model.find returns a Promise', function () {\n    const { Task } = makeContext()\n    const result = Task.find()\n    assert(typeof result.then !== 'undefined')\n    result.catch(err => {\n      /* noop -- prevents UnhandledPromiseRejectionWarning */\n    })\n  })\n\n  it('Model.findInStore', function () {\n    const { Task } = makeContext()\n\n    assert(typeof Task.findInStore === 'function')\n  })\n\n  it('Model.count is a function', function () {\n    const { Task } = makeContext()\n\n    assert(typeof Task.count === 'function')\n  })\n\n  it('Model.count returns a Promise', function () {\n    const { Task } = makeContext()\n    const result = Task.count({ query: {} })\n    assert(typeof result.then !== 'undefined')\n    result.catch(err => {\n      /* noop -- prevents UnhandledPromiseRejectionWarning */\n    })\n  })\n\n  it('Model.countInStore', function () {\n    const { Task } = makeContext()\n\n    assert(typeof Task.countInStore === 'function')\n  })\n\n  it('Model.get', function () {\n    const { Task } = makeContext()\n\n    assert(typeof Task.get === 'function')\n  })\n\n  it('Model.getFromStore', function () {\n    const { Task } = makeContext()\n\n    assert(typeof Task.getFromStore === 'function')\n  })\n\n  it('allows listening to Feathers events on Model', function (done) {\n    const { Letter } = makeContext()\n\n    Letter.on('created', data => {\n      assert(data.to === 'Santa', 'received event with data')\n      done()\n    })\n\n    // This should trigger an event from the bottom of make-service-plugin.ts\n    const letter = new Letter({\n      from: 'Me',\n      to: 'Santa'\n    }).save()\n  })\n\n  it('instance.save calls create with correct arguments', function () {\n    const { Task } = makeContext()\n    const task = new Task({ test: true })\n\n    Object.defineProperty(task, 'create', {\n      value(params) {\n        assert(arguments.length === 1, 'should have only called with params')\n        assert(\n          params === undefined,\n          'no params should have been passed this time'\n        )\n      }\n    })\n\n    task.save()\n  })\n\n  it('instance.save passes params to create', function () {\n    const { Task } = makeContext()\n    const task = new Task({ test: true })\n    let called = false\n\n    Object.defineProperty(task, 'create', {\n      value(params) {\n        assert(arguments.length === 1, 'should have only called with params')\n        assert(params.test, 'should have received params')\n        called = true\n      }\n    })\n\n    task.save({ test: true })\n    assert(called, 'create should have been called')\n  })\n\n  it('instance.save passes params to patch', function () {\n    const { Todo } = makeContext()\n    const todo = new Todo({ id: 1, test: true })\n    let called = false\n\n    Object.defineProperty(todo, 'patch', {\n      value(params) {\n        assert(arguments.length === 1, 'should have only called with params')\n        assert(params.test, 'should have received params')\n        called = true\n      }\n    })\n\n    todo.save({ test: true })\n    assert(called, 'patch should have been called')\n  })\n\n  it('instance.save passes params to update', function () {\n    const { Task } = makeContext()\n    Task.preferUpdate = true\n\n    const task = new Task({ id: 1, test: true })\n    let called = false\n\n    Object.defineProperty(task, 'update', {\n      value(params) {\n        assert(arguments.length === 1, 'should have only called with params')\n        assert(params.test, 'should have received params')\n        called = true\n      }\n    })\n\n    task.save({ test: true })\n    assert(called, 'update should have been called')\n  })\n\n  it('instance.remove works with temp records', function () {\n    const { Task, store } = makeContext()\n    const task = new Task({ test: true })\n    const tempId = task.__id\n\n    task.remove()\n\n    assert(\n      !store.state['model-methods-tasks'].tempsById[tempId],\n      'temp was removed'\n    )\n  })\n\n  it('instance.remove removes cloned record from the store', async function () {\n    const { Person, store } = makeContext()\n    const person = new Person({ _id: 1, test: true })\n    const id = person._id\n\n    // @ts-ignore\n    const { copiesById } = store.state['model-methods-persons']\n\n    person.clone()\n\n    assert(copiesById[id], 'clone exists')\n\n    await person.remove()\n\n    assert(!copiesById[id], 'clone was removed')\n  })\n\n  it('instance.remove removes cloned record from Model.copiesById', async function () {\n    const { Task } = makeContext()\n    const task = new Task({ _id: 2, test: true })\n    const id = task._id\n\n    task.clone()\n\n    assert(Task.copiesById[id], 'clone exists')\n\n    await task.remove()\n\n    assert(!Task.copiesById[id], 'clone was removed')\n  })\n\n  it('instance.remove for temp record removes cloned record from the store', function () {\n    const { Person, store } = makeContext()\n    const person = new Person({ test: true })\n    const tempId = person.__id\n\n    // @ts-ignore\n    const { copiesById } = store.state['model-methods-persons']\n\n    person.clone()\n\n    assert(copiesById[tempId], 'clone exists')\n\n    person.remove()\n\n    assert(!copiesById[tempId], 'clone was removed')\n  })\n\n  it('instance.remove for temp record removes cloned record from the Model.copiesById', function () {\n    const { Task } = makeContext()\n    const task = new Task({ test: true })\n    const tempId = task.__id\n\n    task.clone()\n\n    assert(Task.copiesById[tempId], 'clone exists')\n\n    task.remove()\n\n    assert(!Task.copiesById[tempId], 'clone was removed')\n  })\n\n  it('removes clone and original upon calling clone.remove()', async function () {\n    const { Person, store } = makeContext()\n    const person = new Person({ _id: 1, test: true })\n    const id = person._id\n\n    // @ts-ignore\n    const { copiesById, keyedById } = store.state['model-methods-persons']\n\n    person.clone()\n\n    assert(copiesById[id], 'clone exists')\n    assert(keyedById[id], 'original exists')\n\n    const clone = copiesById[id]\n\n    await clone.remove()\n\n    assert(!copiesById[id], 'clone was removed')\n    assert(!keyedById[id], 'original was removed')\n  })\n\n  it('instance methods still available in store data after updateItem mutation (or socket event)', async function () {\n    const { Letter, store, lettersService } = makeContext()\n    let letter = new Letter({ name: 'Garmadon', age: 1025 })\n\n    letter = await letter.save()\n\n    assert.equal(\n      typeof letter.save,\n      'function',\n      'saved instance has a save method'\n    )\n\n    store.commit('model-methods-letters/updateItem', {\n      id: letter.id,\n      name: 'Garmadon / Dad',\n      age: 1026\n    })\n\n    const letter2 = new Letter({\n      id: letter.id,\n      name: 'Just Garmadon',\n      age: 1027\n    })\n\n    assert.equal(\n      typeof letter2.save,\n      'function',\n      'new instance has a save method'\n    )\n  })\n\n  it('Dates remain as dates after changes', async function () {\n    const { Letter, store, lettersService } = makeContext()\n    let letter = new Letter({\n      name: 'Garmadon',\n      age: 1025,\n      createdAt: new Date().toString()\n    })\n\n    assert(isDate(letter.createdAt), 'createdAt should be a date')\n\n    letter = await letter.save()\n    assert(isDate(letter.createdAt), 'createdAt should be a date')\n\n    letter = await letter.save()\n    assert(isDate(letter.createdAt), 'createdAt should be a date')\n  })\n\n  it('instance.toJSON', function () {\n    const { Task } = makeContext()\n    const task = new Task({ id: 1, test: true })\n\n    Object.defineProperty(task, 'getter', {\n      get() {\n        return `got'er`\n      }\n    })\n\n    assert.equal(task.getter, `got'er`)\n\n    const json = task.toJSON()\n\n    assert(json, 'got json')\n  })\n\n  it('Model pending status sets/clears for create/update/patch/remove', async function() {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'model-methods'\n    })\n    class PendingThing extends BaseModel {\n      public static modelName = 'PendingThing'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: PendingThing,\n          service: feathersClient.service('methods-pending-things')\n        })\n      ]\n    })\n\n    // Create instance\n    const thing = new PendingThing({ description: 'pending test' })\n    const clone = thing.clone()\n    assert(!!thing.__id, \"thing has a tempId\")\n    assert(clone.__id === thing.__id, \"clone has thing's tempId\")\n\n    // Manually set the result in a hook to simulate the server request.\n    feathersClient.service('methods-pending-things').hooks({\n      before: {\n        create: [\n          context => {\n            context.result = { _id: 42, ...context.data }\n            // Check pending status\n            assert(thing.isCreatePending === true, 'isCreatePending set')\n            assert(thing.isSavePending === true, 'isSavePending set')\n            assert(thing.isPending === true, 'isPending set')\n            // Check clone's pending status\n            assert(clone.isCreatePending === true, 'isCreatePending set on clone')\n            assert(clone.isSavePending === true, 'isSavePending set on clone')\n            assert(clone.isPending === true, 'isPending set on clone')\n            return context\n          }\n        ],\n        update: [\n          context => {\n            context.result = { ...context.data }\n            // Check pending status\n            assert(thing.isUpdatePending === true, 'isUpdatePending set')\n            assert(thing.isSavePending === true, 'isSavePending set')\n            assert(thing.isPending === true, 'isPending set')\n            // Check clone's pending status\n            assert(clone.isUpdatePending === true, 'isUpdatePending set on clone')\n            assert(clone.isSavePending === true, 'isSavePending set on clone')\n            assert(clone.isPending === true, 'isPending set on clone')\n            return context\n          }\n        ],\n        patch: [\n          context => {\n            context.result = { ...context.data }\n            // Check pending status\n            assert(thing.isPatchPending === true, 'isPatchPending set')\n            assert(thing.isSavePending === true, 'isSavePending set')\n            assert(thing.isPending === true, 'isPending set')\n            // Check clone's pending status\n            assert(clone.isPatchPending === true, 'isPatchPending set on clone')\n            assert(clone.isSavePending === true, 'isSavePending set on clone')\n            assert(clone.isPending === true, 'isPending set on clone')\n            return context\n          }\n        ],\n        remove: [\n          context => {\n            context.result = { ...context.data }\n            // Check pending status\n            assert(thing.isRemovePending === true, 'isRemovePending set')\n            assert(thing.isSavePending === false, 'isSavePending clear on remove')\n            assert(thing.isPending === true, 'isPending set')\n            // Check clone's pending status\n            assert(clone.isRemovePending === true, 'isRemovePending set on clone')\n            assert(clone.isSavePending === false, 'isSavePending clear on remove on clone')\n            assert(clone.isPending === true, 'isPending set on clone')\n            return context\n          }\n        ]\n      }\n    })\n\n    // Create and verify status\n    await thing.create()\n    assert(thing.isCreatePending === false, 'isCreatePending cleared')\n    assert(thing.isSavePending === false, 'isSavePending cleared')\n    assert(thing.isPending === false, 'isPending cleared')\n    assert(clone.isCreatePending === false, 'isCreatePending cleared on clone')\n    assert(clone.isSavePending === false, 'isSavePending cleared on clone')\n    assert(clone.isPending === false, 'isPending cleared on clone')\n\n    // Update and verify status\n    await thing.update()\n    assert(thing.isUpdatePending === false, 'isUpdatePending cleared')\n    assert(thing.isSavePending === false, 'isSavePending cleared')\n    assert(thing.isPending === false, 'isPending cleared')\n    assert(clone.isUpdatePending === false, 'isUpdatePending cleared on clone')\n    assert(clone.isSavePending === false, 'isSavePending cleared on clone')\n    assert(clone.isPending === false, 'isPending cleared on clone')\n\n    // Patch and verify status\n    await thing.patch()\n    assert(thing.isPatchPending === false, 'isPatchPending cleared')\n    assert(thing.isSavePending === false, 'isSavePending cleared')\n    assert(thing.isPending === false, 'isPending cleared')\n    assert(clone.isPatchPending === false, 'isPatchPending cleared on clone')\n    assert(clone.isSavePending === false, 'isSavePending cleared on clone')\n    assert(clone.isPending === false, 'isPending cleared on clone')\n\n    // Remove and verify status\n    await thing.remove()\n    assert(thing.isRemovePending === false, 'isRemovePending cleared')\n    assert(thing.isSavePending === false, 'isSavePending cleared')\n    assert(thing.isPending === false, 'isPending cleared')\n    assert(clone.isRemovePending === false, 'isRemovePending cleared on clone')\n    assert(clone.isSavePending === false, 'isSavePending cleared on clone')\n    assert(clone.isPending === false, 'isPending cleared on clone')\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-relationships.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport feathersVuex, { models } from '../../src/index'\nimport { clearModels } from '../../src/service-module/global-models'\n\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport Vuex from 'vuex'\n\ndescribe('Models - `setupInstance` & Relatioships', function () {\n  beforeEach(function () {\n    clearModels()\n  })\n\n  it('initializes instance with return value from setupInstance', function () {\n    let calledSetupInstance = false\n\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'myApi'\n    })\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public id?\n      public description: string\n\n      public constructor(data, options?) {\n        super(data, options)\n      }\n    }\n    function setupInstance(instance, { models, store }): Todo {\n      calledSetupInstance = true\n\n      return Object.assign(instance, {\n        extraProp: true\n      })\n    }\n    const store = new Vuex.Store({\n      strict: true,\n      plugins: [\n        makeServicePlugin({\n          Model: Todo,\n          service: feathersClient.service('service-todos'),\n          setupInstance\n        })\n      ]\n    })\n\n    const createdAt = '2018-05-01T04:42:24.136Z'\n    const todo = new Todo({\n      description: 'Go on a date.',\n      isComplete: true,\n      createdAt\n    })\n\n    assert(calledSetupInstance, 'setupInstance was called')\n    assert(todo.extraProp, 'got the extraProp')\n  })\n\n  it('allows setting up relationships between models and other constructors', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'myApi'\n    })\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public id?\n      public description: string\n      public user: User\n\n      public constructor(data, options?) {\n        super(data, options)\n      }\n    }\n    class User extends BaseModel {\n      public static modelName = 'User'\n      public _id: string\n      public firstName: string\n      public email: string\n    }\n\n    function setupInstance(instance, { models, store }): Todo {\n      const { User } = models.myApi\n\n      return Object.assign(instance, {\n        // If instance.user exists, convert it to a User instance\n        ...(instance.user && { user: new User(instance.user) }),\n        // If instance.createdAt exists, convert it to an actual date\n        ...(instance.createdAt && { createdAt: new Date(instance.createdAt) })\n      })\n    }\n    const store = new Vuex.Store({\n      strict: true,\n      plugins: [\n        makeServicePlugin({\n          Model: Todo,\n          service: feathersClient.service('service-todos'),\n          setupInstance\n        }),\n        makeServicePlugin({\n          Model: User,\n          service: feathersClient.service('users'),\n          idField: '_id'\n        })\n      ]\n    })\n\n    const todo = new Todo({\n      description: `Show Master Splinter what's up.`,\n      isComplete: true,\n      createdAt: '2018-05-01T04:42:24.136Z',\n      user: {\n        _id: 1,\n        firstName: 'Michaelangelo',\n        email: 'mike@tmnt.com'\n      }\n    })\n\n    // Check the date\n    assert(\n      typeof todo.createdAt === 'object',\n      'module.createdAt is an instance of object'\n    )\n    assert(\n      todo.createdAt.constructor.name === 'Date',\n      'module.createdAt is an instance of date'\n    )\n\n    // Check the user\n    assert(todo.user instanceof User, 'the user is an instance of User')\n\n    const user = User.getFromStore(1)\n    assert.equal(todo.user, user, 'user was added to the user store.')\n  })\n})\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'myApi'\n  })\n  class Task extends BaseModel {\n    public static modelName = 'Task'\n    public static instanceDefaults() {\n      return {\n        id: null,\n        description: '',\n        isComplete: false\n      }\n    }\n    public constructor(data, options?) {\n      super(data, options)\n    }\n  }\n  /**\n   * This Model demonstrates how to use a dynamic set of instanceDefaults based on incoming data.\n   */\n  class Todo extends BaseModel {\n    public static modelName = 'Todo'\n    public static instanceDefaults(data) {\n      const priority = data.priority || 'normal'\n      const defaultsByPriority = {\n        normal: {\n          description: '',\n          isComplete: false,\n          priority: ''\n        },\n        high: {\n          isHighPriority: true,\n          priority: ''\n        }\n      }\n      return defaultsByPriority[priority]\n    }\n    public static setupInstance(data, { models, store }) {\n      const { Task, Item } = models.myApi\n\n      return Object.assign(data, {\n        ...(data.task && { task: new Task(data.task) }),\n        ...(data.item && { item: new Item(data.item) }),\n        ...(data.items && { items: data.items.map(item => new Item(item)) })\n      })\n    }\n    public constructor(data, options?) {\n      super(data, options)\n    }\n  }\n  class Item extends BaseModel {\n    public static modelName = 'Item'\n    public get todos() {\n      return BaseModel.models.Todo.findInStore({ query: {} }).data\n    }\n    public static instanceDefaults() {\n      return {\n        test: false,\n        todo: 'Todo'\n      }\n    }\n    public static setupInstance(data, { models, store }) {\n      const { Todo } = models.myApi\n\n      return Object.assign(data, {\n        ...(data.todo && { todo: new Todo(data.todo) })\n      })\n    }\n    public constructor(data, options?) {\n      super(data, options)\n    }\n  }\n  const store = new Vuex.Store({\n    strict: true,\n    plugins: [\n      makeServicePlugin({\n        Model: Task,\n        service: feathersClient.service('tasks')\n      }),\n      makeServicePlugin({\n        Model: Todo,\n        service: feathersClient.service('service-todos')\n      }),\n      makeServicePlugin({\n        Model: Item,\n        service: feathersClient.service('items'),\n        mutations: {\n          toggleTestBoolean(state, item) {\n            item.test = !item.test\n          }\n        }\n      })\n    ]\n  })\n  return {\n    makeServicePlugin,\n    BaseModel,\n    store,\n    Todo,\n    Task,\n    Item\n  }\n}\n\ndescribe('Models - Relationships', function () {\n  beforeEach(function () {\n    clearModels()\n  })\n\n  it('can have different instanceDefaults based on new instance data', function () {\n    const { Todo } = makeContext()\n    const normalTodo = new Todo({\n      description: 'Normal'\n    })\n    const highPriorityTodo = new Todo({\n      description: 'High Priority',\n      priority: 'high'\n    })\n\n    assert(\n      !normalTodo.hasOwnProperty('isHighPriority'),\n      'Normal todos do not have an isHighPriority default attribute'\n    )\n    assert(\n      highPriorityTodo.isHighPriority,\n      'High priority todos have a unique attribute'\n    )\n  })\n\n  it('adds model instances containing an id to the store', function () {\n    const { Todo, Task } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n\n    assert.deepEqual(\n      Task.getFromStore(1),\n      todo.task,\n      'task was added to the store'\n    )\n  })\n\n  it('works with multiple keys that match Model names', function () {\n    const { Todo, Task, Item } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      },\n      item: {\n        id: 2,\n        test: true\n      }\n    })\n\n    assert.deepEqual(\n      Task.getFromStore(1),\n      todo.task,\n      'task was added to the store'\n    )\n    assert.deepEqual(\n      Item.getFromStore(2),\n      todo.item,\n      'item was added to the store'\n    )\n  })\n\n  it('handles nested relationships', function () {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      },\n      item: {\n        id: 2,\n        test: true,\n        todo: {\n          description: 'nested todo under item'\n        }\n      }\n    })\n\n    assert(\n      todo.item.todo.constructor.name === 'Todo',\n      'the nested todo is an instance of Todo'\n    )\n  })\n\n  it('handles circular nested relationships', function () {\n    const { Todo, Item } = makeContext()\n\n    const todo = new Todo({\n      id: 1,\n      description: 'todo description',\n      item: {\n        id: 2,\n        test: true,\n        todo: {\n          id: 1,\n          description: 'todo description'\n        }\n      }\n    })\n\n    assert.deepEqual(Todo.getFromStore(1), todo, 'todo was added to the store')\n    assert.deepEqual(\n      Item.getFromStore(2),\n      todo.item,\n      'item was added to the store'\n    )\n    assert(todo.item, 'todo still has an item')\n    assert(todo.item.todo, 'todo still nested in itself')\n  })\n\n  it('updates related data', function () {\n    const { Todo, Item, store } = makeContext()\n\n    const module = new Todo({\n      id: 'todo-1',\n      description: 'todo description',\n      item: {\n        id: 'item-2',\n        test: true,\n        todo: {\n          id: 'todo-1',\n          description: 'todo description'\n        }\n      }\n    })\n\n    const storedTodo = Todo.getFromStore('todo-1')\n    const storedItem = Item.getFromStore('item-2')\n\n    store.commit('items/toggleTestBoolean', storedItem)\n    // module.item.test = false\n\n    assert.equal(\n      module.item.test,\n      false,\n      'the nested module.item.test should be false'\n    )\n    assert.equal(\n      storedTodo.item.test,\n      false,\n      'the nested item.test should be false'\n    )\n    assert.equal(storedItem.test, false, 'item.test should be false')\n  })\n\n  it(`allows creating more than once relational instance`, function () {\n    const { Todo, Item } = makeContext()\n\n    const todo1 = new Todo({\n      id: 'todo-1',\n      description: 'todo description',\n      item: {\n        id: 'item-2',\n        test: true\n      }\n    })\n    const todo2 = new Todo({\n      id: 'todo-2',\n      description: 'todo description',\n      item: {\n        id: 'item-3',\n        test: true\n      }\n    })\n\n    const storedTodo = Todo.getFromStore('todo-1')\n    const storedItem = Item.getFromStore('item-2')\n\n    assert.equal(\n      todo1.item.test,\n      true,\n      'the nested module.item.test should be true'\n    )\n    assert.equal(\n      todo2.item.test,\n      true,\n      'the nested module.item.test should be true'\n    )\n    assert.equal(\n      storedTodo.item.test,\n      true,\n      'the nested item.test should be true'\n    )\n    assert.equal(storedItem.test, true, 'item.test should be true')\n  })\n\n  it(`handles arrays of related data`, function () {\n    const { Todo, Item } = makeContext()\n\n    const todo1 = new Todo({\n      id: 'todo-1',\n      description: 'todo description',\n      items: [\n        {\n          id: 'item-1',\n          test: true\n        },\n        {\n          id: 'item-2',\n          test: true\n        }\n      ]\n    })\n    const todo2 = new Todo({\n      id: 'todo-2',\n      description: 'todo description',\n      items: [\n        {\n          id: 'item-3',\n          test: true\n        },\n        {\n          id: 'item-4',\n          test: true\n        }\n      ]\n    })\n\n    assert(todo1, 'todo1 is an instance')\n    assert(todo2, 'todo2 is an instance')\n\n    const storedTodo1 = Todo.getFromStore('todo-1')\n    const storedTodo2 = Todo.getFromStore('todo-2')\n    const storedItem1 = Item.getFromStore('item-1')\n    const storedItem2 = Item.getFromStore('item-2')\n    const storedItem3 = Item.getFromStore('item-3')\n    const storedItem4 = Item.getFromStore('item-4')\n\n    assert(storedTodo1, 'should have todo 1')\n    assert(storedTodo2, 'should have todo 2')\n    assert(storedItem1, 'should have item 1')\n    assert(storedItem2, 'should have item 2')\n    assert(storedItem3, 'should have item 3')\n    assert(storedItem4, 'should have item 4')\n  })\n\n  it('preserves relationships on clone', function () {\n    const { Todo, Task } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n    const clone = todo.clone()\n\n    assert(clone.task instanceof Task, 'nested task is a Task')\n  })\n  it('preserves relationships on commit', function () {\n    const { Todo, Task } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n    const clone = todo.clone()\n    const original = clone.commit()\n\n    assert(original.task instanceof Task, 'nested task is a Task')\n  })\n\n  it('preserves relationship with nested data clone and commit', function () {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n    // Create a clone of the nested task, modify and commit.\n    const taskClone = todo.task.clone()\n    taskClone.isComplete = false\n    taskClone.commit()\n\n    assert.equal(todo.task.isComplete, false, 'preserved after clone')\n  })\n\n  it('returns the same object when an instance is cloned twice', function () {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n\n    const clone1 = todo.clone()\n    const clone2 = todo.clone()\n\n    assert(clone1 === clone2, 'there should only ever be one clone in memory for an instance with the same id')\n  })\n\n  it('on clone, nested instances do not get cloned', function () {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n\n    const todoClone = todo.clone()\n\n    assert(todoClone.task.__isClone === undefined, 'todo.task should still be the original item and not the clone')\n  })\n\n  it('on nested commit in instance, original nested instances get updated', function () {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n\n    const taskClone = todo.task.clone()\n\n    taskClone.description = 'changed'\n    taskClone.commit()\n\n    assert(todo.task.description === 'changed', 'the nested task should have been updated')\n  })\n\n  it('nested instances get updated in clones and original records', function () {\n    const { Todo } = makeContext()\n\n    const todo = new Todo({\n      task: {\n        id: 1,\n        description: 'test',\n        isComplete: true\n      }\n    })\n    const todoClone = todo.clone()\n\n    assert(todo.task === todoClone.task, 'the same task instance should be in both the original and clone')\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-serialize.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport feathersVuex from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport { clearModels } from '../../src/service-module/global-models'\nimport _omit from 'lodash/omit'\nimport Vuex from 'vuex'\n\ndescribe('Models - Serialize', function () {\n  beforeEach(() => {\n    clearModels()\n  })\n\n  it('allows customizing toJSON', function () {\n    const { BaseModel, makeServicePlugin } = feathersVuex(feathersClient, {\n      serverAlias: 'myApi'\n    })\n\n    class Task extends BaseModel {\n      public static modelName = 'Task'\n      public static instanceDefaults() {\n        return {\n          id: null,\n          description: '',\n          isComplete: false\n        }\n      }\n      public toJSON() {\n        return _omit(this, ['isComplete'])\n      }\n      public constructor(data, options?) {\n        super(data, options)\n      }\n    }\n\n    const servicePath = 'thingies'\n    const plugin = makeServicePlugin({\n      servicePath: 'thingies',\n      Model: Task,\n      service: feathersClient.service(servicePath)\n    })\n\n    new Vuex.Store({ plugins: [plugin] })\n\n    const task = new Task({\n      description: 'Hello, World!',\n      isComplete: true\n    })\n\n    assert(!task.toJSON().hasOwnProperty('isComplete'), 'custom toJSON worked')\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-temp-ids.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { ServiceState } from './types'\nimport { assert } from 'chai'\nimport feathersVuex from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport { clearModels } from '../../src/service-module/global-models'\nimport { Service as MemoryService } from 'feathers-memory'\nimport Vue from 'vue/dist/vue'\nimport Vuex from 'vuex'\nimport { makeStore } from '../test-utils'\nimport ObjectID from 'bson-objectid'\nimport fastCopy from 'fast-copy'\n\ninterface RootState {\n  transactions: ServiceState\n  things: ServiceState\n}\n\nclass ComicService extends MemoryService {\n  public create(data, params) {\n    return super.create(data, params).then(response => {\n      delete response.__id\n      delete response.__isTemp\n      return response\n    })\n  }\n  // @ts-ignore\n  public update(id, data, params) {\n    data.createdAt = new Date()\n    // this._super(data, params, callback)\n  }\n}\n\nfunction makeContext() {\n  feathersClient.use(\n    'comics',\n    // @ts-ignore\n    new ComicService({ store: makeStore() })\n  )\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'model-temp-ids'\n  })\n  class Comic extends BaseModel {\n    public static modelName = 'Comic'\n    public static test = true\n\n    public constructor(data, options?) {\n      super(data, options)\n    }\n  }\n  const store = new Vuex.Store({\n    strict: true,\n    plugins: [\n      makeServicePlugin({\n        Model: Comic,\n        service: feathersClient.service('comics'),\n        servicePath: 'comics'\n      })\n    ]\n  })\n  return {\n    makeServicePlugin,\n    BaseModel,\n    Comic,\n    store\n  }\n}\n\ndescribe('Models - Temp Ids', function () {\n  beforeEach(() => {\n    clearModels()\n  })\n\n  it('adds tempIds for items without an [idField]', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Transaction extends BaseModel {\n      public static modelName = 'Transaction'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Transaction,\n          service: feathersClient.service('transactions')\n        })\n      ]\n    })\n    const txn = new Transaction({\n      description: 'Green Pasture - No More Dentists!',\n      website: 'https://www.greenpasture.org',\n      amount: 1.99\n    })\n\n    // Make sure we got an id.\n    assert(txn.__id, 'the record got an __id')\n    assert(txn.__isTemp, 'item is a temp')\n\n    // It should be non-enumerable and non-writable\n    const desc = Object.getOwnPropertyDescriptor(txn, '__id')\n    assert(desc.enumerable, 'it is enumerable')\n  })\n\n  it('allows specifying the value for the tempId', function () {\n    const context = makeContext()\n    const Comic = context.Comic\n    const oid = new ObjectID().toHexString()\n\n    const comic = new Comic({ __id: oid })\n\n    assert(comic.__isTemp, 'item is a temp')\n    assert.equal(comic.__id, oid, 'the objectid was used')\n  })\n\n  it('adds to state.tempsById', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Transaction extends BaseModel {\n      public static modelName = 'Transaction'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Transaction,\n          service: feathersClient.service('transactions')\n        })\n      ]\n    })\n\n    const txn = new Transaction({\n      description: 'Amazon - Cure Teeth Book',\n      website:\n        'https://www.amazon.com/Cure-Tooth-Decay-Cavities-Nutrition-ebook/dp/B004GB0JIM',\n      amount: 1.99\n    })\n\n    // Make sure we got an id.\n    assert(store.state.transactions.tempsById[txn.__id], 'it is in the store')\n  })\n\n  it('maintains reference to temp item after save', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Thing extends BaseModel {\n      public static modelName = 'Thing'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Thing,\n          service: feathersClient.service('things')\n        })\n      ]\n    })\n\n    // Manually set the result in a hook to simulate the server request.\n    feathersClient.service('things').hooks({\n      before: {\n        create: [\n          // Testing removing the __id and __isTemp so they're not sent to the server.\n          context => {\n            delete context.data.__id\n            delete context.data.__isTemp\n          },\n          context => {\n            assert(!context.data.__id, '__id was not sent to API server')\n            assert(\n              !context.data.__isTemp,\n              '__isTemp was not sent to API server'\n            )\n            context.result = {\n              _id: 1,\n              description: 'Robb Wolf - the Paleo Solution',\n              website:\n                'https://robbwolf.com/shop-old/products/the-paleo-solution-the-original-human-diet/',\n              amount: 1.99\n            }\n            return context\n          }\n        ]\n      }\n    })\n\n    const thing = new Thing({\n      description: 'Robb Wolf - the Paleo Solution',\n      website:\n        'https://robbwolf.com/shop-old/products/the-paleo-solution-the-original-human-diet/',\n      amount: 1.99\n    })\n\n    assert(store.state.things.tempsById[thing.__id], 'item is in the tempsById')\n\n    return thing.save().then(response => {\n      assert(response._id === 1)\n      assert(response.__id, 'the temp id is still intact')\n      assert(!store.state.things.tempsById[response.__id])\n      assert(response === thing, 'maintained the reference')\n    })\n  })\n\n  it('removes uncreated temp', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Thing extends BaseModel {\n      public static modelName = 'Thing'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Thing,\n          service: feathersClient.service('things')\n        })\n      ]\n    })\n\n    const thing = new Thing({\n      description: 'Robb Wolf - the Paleo Solution',\n      website:\n        'https://robbwolf.com/shop-old/products/the-paleo-solution-the-original-human-diet/',\n      amount: 1.99\n    })\n\n    assert(store.state.things.tempsById[thing.__id], 'item is in the tempsById')\n\n    store.commit('things/removeTemps', [thing.__id])\n\n    assert(!store.state.things.tempsById[thing.__id], 'temp item was removed')\n  })\n\n  it('clones into Model.copiesById', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Transaction extends BaseModel {\n      public static modelName = 'Transaction'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Transaction,\n          service: feathersClient.service('transactions')\n        })\n      ]\n    })\n    const txn = new Transaction({\n      description: 'Robb Wolf - the Paleo Solution',\n      website:\n        'https://robbwolf.com/shop-old/products/the-paleo-solution-the-original-human-diet/',\n      amount: 1.99\n    })\n\n    txn.clone()\n\n    assert(Transaction.copiesById[txn.__id], 'it is in the copiesById')\n  })\n\n  it('commits into store.tempsById', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Transaction extends BaseModel {\n      public static modelName = 'Transaction'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Transaction,\n          service: feathersClient.service('transactions')\n        })\n      ]\n    })\n    const txn = new Transaction({\n      description: 'Rovit Monthly Subscription',\n      website: 'https://rovit.com',\n      amount: 1.99\n    })\n\n    // Clone it, change it and commit it.\n    const clone = txn.clone()\n    clone.amount = 11.99\n    clone.commit()\n\n    const originalTemp = store.state.transactions.tempsById[txn.__id]\n\n    assert.equal(originalTemp.amount, 11.99, 'original was updated')\n  })\n\n  it('can reset a temp clone', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'temp-ids'\n    })\n    class Transaction extends BaseModel {\n      public static modelName = 'Transaction'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Transaction,\n          service: feathersClient.service('transactions')\n        })\n      ]\n    })\n    const txn = new Transaction({\n      description: 'Rovit Monthly Subscription',\n      website: 'https://rovit.com',\n      amount: 1.99\n    })\n\n    // Clone it, change it and commit it.\n    const clone = txn.clone()\n    clone.amount = 11.99\n    clone.reset()\n\n    assert.equal(clone.amount, 1.99, 'clone was reset')\n  })\n\n  it('returns the keyedById record after create, not the tempsById record', function (done) {\n    const { Comic, store } = makeContext()\n\n    const comic = new Comic({\n      name: 'The Uncanny X-Men',\n      year: 1969\n    })\n\n    // Create a temp and make sure it's in the tempsById\n    const tempId = comic.__id\n    // @ts-ignore\n    assert(store.state.comics.tempsById[tempId])\n    assert(comic.__isTemp)\n\n    comic\n      .save()\n      .then(response => {\n        assert(!response.hasOwnProperty('__isTemp'))\n        // The comic record is no longer in tempsById\n        // @ts-ignore\n        assert(!store.state.comics.tempsById[tempId], 'temp is gone')\n        // The comic record moved to keyedById\n        // @ts-ignore\n        assert(store.state.comics.keyedById[response.id], 'now a real record')\n        done()\n      })\n      .catch(done)\n  })\n\n  it('removes __isTemp from temp and clone', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Thing extends BaseModel {\n      public static modelName = 'Thing'\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Thing,\n          service: feathersClient.service('things')\n        })\n      ]\n    })\n\n    const thing = new Thing()\n    assert(thing.__isTemp, 'thing has __isTemp')\n\n    const clone = thing.clone()\n    assert(clone.__isTemp, 'Clone also has __isTemp')\n\n    store.commit('things/updateTemp', { id: 42, tempId: thing.__id })\n\n    assert(!thing.hasOwnProperty('__isTemp'), '__isTemp was removed from thing')\n    assert(!clone.hasOwnProperty('__isTemp'), '__isTemp was removed from clone')\n  })\n\n  it('updateTemp assigns ID to temp and migrates it from tempsById to keyedById', function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Thing extends BaseModel {\n      public static modelName = 'Thing'\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Thing,\n          service: feathersClient.service('things')\n        })\n      ]\n    })\n\n    const thing = new Thing()\n    assert(thing.__id, 'thing has tempId')\n    assert(!thing._id, 'thing does not have _id')\n    assert(store.state.things.tempsById[thing.__id], 'thing is in tempsById')\n\n    store.commit('things/updateTemp', { id: 42, tempId: thing.__id })\n    assert(thing._id === 42, 'thing got _id')\n    assert(store.state.things.keyedById[42] === thing, 'thing is in keyedById')\n    assert(store.state.things.ids.includes(42), \"thing's _id is in ids\")\n    assert(\n      !store.state.things.tempsById[thing.__id],\n      'thing is no longer in tempsById'\n    )\n  })\n\n  it('Clone gets _id after save (create only called once)', async function () {\n    // Test ensures subsequent calls to clone.save() do not call create\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class Thing extends BaseModel {\n      public static modelName = 'Thing'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: Thing,\n          service: feathersClient.service('things')\n        })\n      ]\n    })\n\n    // Manually set the result in a hook to simulate the server request.\n    let createCalled = false\n    feathersClient.service('things').hooks({\n      before: {\n        create: [\n          context => {\n            assert(createCalled === false, 'Create is only called once')\n            createCalled = true\n            context.result = { _id: 42, ...context.data }\n            return context\n          }\n        ],\n        patch: [\n          context => {\n            assert(context.data.__isClone, 'Patch called on clone')\n            assert(context.id === 42, 'context has correct ID')\n            assert(context.data._id === 42, 'patch called with correct _id')\n            assert(\n              context.data.description === 'Thing 3',\n              'patch called with correct description'\n            )\n            context.result = { ...context.data }\n            return context\n          }\n        ]\n      }\n    })\n\n    // Create instance and clone\n    const thing = new Thing({ description: 'Thing 1' })\n    const clone = thing.clone()\n    assert(thing.__id === clone.__id, \"clone has thing's tempId\")\n    assert(clone.description === 'Thing 1', \"clone got thing's description\")\n    assert(!thing.hasOwnProperty('_id'), 'thing has no _id')\n    assert(!clone.hasOwnProperty('_id'), 'clone has no _id')\n\n    // Modify clone and save\n    clone.description = 'Thing 2'\n    const response = await clone.save()\n    assert(response === thing, 'response from clone.save() is thing')\n    assert(thing._id === 42, 'thing got _id')\n    assert(thing.description === 'Thing 2', \"thing got clone's changes\")\n    assert(clone._id === response._id, 'clone got _id')\n\n    // Modify clone again and save again\n    clone.description = 'Thing 3'\n    const response2 = await clone.save()\n    assert(response2 === thing, 'response2 is still thing')\n    assert(thing.description === 'Thing 3', \"thing got clone's new changes\")\n  })\n\n  it('find() getter does not return duplicates with temps: true', async function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class FooModel extends BaseModel {\n      public static modelName = 'FooModel'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: FooModel,\n          service: feathersClient.service('foos'),\n          servicePath: 'foos'\n        })\n      ]\n    })\n\n    // Fake server call\n    feathersClient.service('foos').hooks({\n      before: {\n        create: [\n          context => {\n            delete context.data.__id\n            delete context.data.__isTemp\n          },\n          context => {\n            context.result = { _id: 24, ...context.data }\n            return context\n          }\n        ]\n      }\n    })\n\n    // Create component with find() computed prop\n    const watchEvents = []\n    new Vue({\n      template: `<div></div>`,\n      computed: {\n        things() {\n          return store.getters['foos/find']({\n            query: { test: true },\n            temps: true\n          }).data\n        }\n      },\n      watch: {\n        things(items) {\n          watchEvents.push(fastCopy(items))\n        }\n      }\n    }).$mount()\n\n    const item = new FooModel({ test: true })\n    await item.save()\n\n    assert(watchEvents.length > 0, 'watch fired at least once')\n    watchEvents.forEach(items => {\n      if (items.length === 2) {\n        assert(items[0]._id !== items[1]._id, 'no duplicate id')\n        assert(items[0].__id !== items[1].__id, 'no duplicate tempId')\n      }\n    })\n  })\n\n  it('Model pending status updated for tempIds and clones', async function() {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      idField: '_id',\n      serverAlias: 'temp-ids'\n    })\n    class PendingThing extends BaseModel {\n      public static modelName = 'PendingThing'\n      public constructor(data?, options?) {\n        super(data, options)\n      }\n    }\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: PendingThing,\n          service: feathersClient.service('pending-things')\n        })\n      ]\n    })\n\n    // Create instance\n    const thing = new PendingThing({ description: 'PendingThing 1' })\n    const clone = thing.clone()\n    assert(!!thing.__id, 'thing has a tempId')\n    assert(clone.__id === thing.__id, \"clone has thing's tempId\")\n\n    // Manually set the result in a hook to simulate the server request.\n    feathersClient.service('pending-things').hooks({\n      before: {\n        create: [\n          context => {\n            context.result = { _id: 42, ...context.data }\n            // Check pending status\n            assert(thing.isCreatePending === true, 'isCreatePending set')\n            assert(thing.isSavePending === true, 'isSavePending set')\n            assert(thing.isPending === true, 'isPending set')\n            // Check clone's pending status\n            assert(clone.isCreatePending === true, 'isCreatePending set')\n            assert(clone.isSavePending === true, 'isSavePending set on clone')\n            assert(clone.isPending === true, 'isPending set')\n            return context\n          }\n        ]\n      }\n    })\n\n    // Save and verify status\n    await thing.save()\n    assert(thing.isCreatePending === false, 'isCreatePending cleared')\n    assert(thing.isSavePending === false, 'isSavePending cleared')\n    assert(thing.isPending === false, 'isPending cleared')\n    assert(clone.isCreatePending === false, 'isCreatePending cleared')\n    assert(clone.isSavePending === false, 'isSavePending cleared on clone')\n    assert(clone.isPending === false, 'isPending cleared')\n  })\n})\n"
  },
  {
    "path": "test/service-module/model-tests.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\n\ninterface ModelOptions {\n  servicePath: string\n}\n\ndescribe('TypeScript Class Inheritance', () => {\n  it('Can access static instanceDefaults from BaseModel', () => {\n    abstract class BaseModel {\n      public static instanceDefaults\n      public constructor(data, options?) {\n        const { instanceDefaults } = this.constructor as typeof BaseModel\n        const defaults = instanceDefaults(data, options)\n        assert(\n          defaults.description === 'default description',\n          'We get defaults in the BaseModel constructor'\n        )\n        Object.assign(this, defaults, data)\n      }\n    }\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n\n      public description: string\n      public static instanceDefaults = (data, options) => ({\n        description: 'default description'\n      })\n\n      public constructor(data, options?) {\n        super(data, options)\n        const { instanceDefaults } = this.constructor as typeof BaseModel\n        const defaults = instanceDefaults(data, options)\n        assert(\n          defaults.description === 'default description',\n          'We get defaults in the Todo constructor, too'\n        )\n      }\n    }\n\n    const todo = new Todo({\n      test: true\n    })\n\n    assert(\n      todo.description === 'default description',\n      'got default description'\n    )\n  })\n\n  it('Can access static instanceDefaults from two levels of inheritance', () => {\n    abstract class BaseModel {\n      public static instanceDefaults\n      public constructor(data, options?) {\n        const { instanceDefaults } = this.constructor as typeof BaseModel\n        const defaults = instanceDefaults(data, options)\n        assert(\n          defaults.description === 'default description',\n          'We get defaults in the BaseModel constructor'\n        )\n        Object.assign(this, defaults, data)\n      }\n    }\n\n    function makeServiceModel(options) {\n      const { servicePath } = options\n\n      class ServiceModel extends BaseModel {\n        public static modelName = 'ServiceModel'\n        public constructor(data, options: ModelOptions = { servicePath: '' }) {\n          options.servicePath = servicePath\n          super(data, options)\n        }\n      }\n      return ServiceModel\n    }\n\n    class Todo extends makeServiceModel({ servicePath: 'todos' }) {\n      public static modelName = 'Todo'\n      public description: string\n\n      public static instanceDefaults = (data, options) => ({\n        description: 'default description'\n      })\n    }\n\n    const todo = new Todo({\n      test: true\n    })\n\n    assert(\n      todo.description === 'default description',\n      'got default description'\n    )\n  })\n\n  it('Can access static servicePath from Todo in BaseModel', () => {\n    abstract class BaseModel {\n      public static instanceDefaults\n      public static servicePath\n      public static namespace\n\n      public constructor(data, options?) {\n        const { instanceDefaults, servicePath, namespace } = this\n          .constructor as typeof BaseModel\n        const defaults = instanceDefaults(data, options)\n        assert(\n          defaults.description === 'default description',\n          'We get defaults in the BaseModel constructor'\n        )\n        Object.assign(this, defaults, data, {\n          _options: { namespace, servicePath }\n        })\n      }\n    }\n\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public static namespace: string = 'todos'\n      public static servicePath: string = 'v1/todos'\n\n      public description: string\n      public _options\n\n      public static instanceDefaults = (data, models) => ({\n        description: 'default description'\n      })\n    }\n\n    const todo = new Todo({\n      test: true\n    })\n\n    assert(todo._options.servicePath === 'v1/todos', 'got static servicePath')\n  })\n\n  it('cannot serialize instance methods', () => {\n    class BaseModel {\n      public clone() {\n        return this\n      }\n\n      public constructor(data) {\n        Object.assign(this, data)\n      }\n    }\n\n    class Todo extends BaseModel {\n      public static modelName = 'Todo'\n      public serialize() {\n        return Object.assign({}, this, { serialized: true })\n      }\n    }\n\n    const todo = new Todo({ name: 'test' })\n    const json = JSON.parse(JSON.stringify(todo))\n\n    assert(!json.clone)\n    assert(!json.serialize)\n  })\n})\n"
  },
  {
    "path": "test/service-module/service-module.actions.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { ServiceState } from './types'\nimport { assert } from 'chai'\nimport feathersVuex from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport Vuex, { mapActions } from 'vuex'\nimport memory from 'feathers-memory'\nimport { clearModels } from '../../src/service-module/global-models'\nimport { makeStore, makeStoreWithAtypicalIds } from '../test-utils'\n\ninterface RootState {\n  'my-todos': ServiceState\n  'my-tasks': ServiceState\n  broken: ServiceState\n}\ninterface NumberedList {\n  0?: {}\n  1?: {}\n}\n\nfunction makeContext() {\n  feathersClient.use(\n    'my-todos',\n    memory({\n      store: makeStore()\n    })\n  )\n  feathersClient.use(\n    'my-tasks',\n    memory({\n      store: makeStore(),\n      paginate: {\n        default: 10,\n        max: 50\n      }\n    })\n  )\n  const todoService = feathersClient.service('my-todos')\n  const taskService = feathersClient.service('my-tasks')\n  const noIdService = feathersClient.use(\n    'no-ids',\n    memory({\n      store: makeStoreWithAtypicalIds(),\n      paginate: {\n        default: 10,\n        max: 50\n      }\n    })\n  )\n  const brokenService = feathersClient.use('broken', {\n    find() {\n      return Promise.reject(new Error('find error'))\n    },\n    get() {\n      return Promise.reject(new Error('get error'))\n    },\n    create() {\n      return Promise.reject(new Error('create error'))\n    },\n    update() {\n      return Promise.reject(new Error('update error'))\n    },\n    patch() {\n      return Promise.reject(new Error('patch error'))\n    },\n    remove() {\n      return Promise.reject(new Error('remove error'))\n    },\n    // eslint-disable-next-line @typescript-eslint/no-empty-function\n    setup() {}\n  })\n\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'service-module-actions'\n  })\n  class Todo extends BaseModel {\n    public static modelName = 'Todo'\n    public static test = true\n  }\n  class Task extends BaseModel {\n    public static modelName = 'Task'\n    public static test = true\n  }\n  class NoId extends BaseModel {\n    public static modelName = 'NoId'\n    public static test = true\n  }\n  class Broken extends BaseModel {\n    public static modelName = 'Broken'\n    public static test = true\n  }\n  return {\n    makeServicePlugin,\n    BaseModel,\n    todoService,\n    taskService,\n    noIdService,\n    brokenService,\n    Todo,\n    Task,\n    NoId,\n    Broken\n  }\n}\n\nconst assertRejected = (promise, done, callback) => {\n  // resolve handler\n  promise.then(\n    () => done(new Error('expected promise to be rejected')),\n    // reject handler\n    () => {\n      try {\n        callback()\n        done()\n      } catch (e) {\n        done(e)\n      }\n    }\n  )\n}\n\ndescribe('Service Module - Actions', () => {\n  beforeEach(() => {\n    clearModels()\n  })\n  describe('Find', () => {\n    describe('without pagination', () => {\n      it('Find without pagination', done => {\n        const { makeServicePlugin, Todo } = makeContext()\n        const todosPlugin = makeServicePlugin({\n          servicePath: 'my-todos',\n          Model: Todo,\n          service: feathersClient.service('my-todos')\n        })\n        const store = new Vuex.Store<RootState>({\n          plugins: [todosPlugin]\n        })\n        const todoState = store.state['my-todos']\n        const actions = mapActions('my-todos', ['find'])\n\n        assert(todoState.ids.length === 0, 'no ids before find')\n        assert(todoState.errorOnFind === null, 'no error before find')\n        assert(todoState.isFindPending === false, 'isFindPending is false')\n        assert(todoState.idField === 'id', 'idField is `id`')\n\n        actions.find.call({ $store: store }, {}).then(response => {\n          assert(todoState.ids.length === 10, 'three ids populated')\n          assert(todoState.errorOnFind === null, 'errorOnFind still null')\n          assert(todoState.isFindPending === false, 'isFindPending is false')\n          const expectedKeyedById: NumberedList = makeStore()\n          const currentKeyedById = JSON.parse(\n            JSON.stringify(todoState.keyedById)\n          )\n          assert.deepEqual(\n            currentKeyedById,\n            expectedKeyedById,\n            'keyedById matches'\n          )\n\n          assert(\n            typeof todoState.keyedById[1].save === 'function',\n            'added FeathersVuexModel class methods to the data'\n          )\n\n          done()\n        })\n\n        // Make sure proper state changes occurred before response\n        assert(todoState.ids.length === 0)\n        assert(todoState.errorOnFind === null)\n        assert(todoState.isFindPending === true)\n        assert.deepEqual(todoState.keyedById, {})\n      })\n\n      it('find with limit', done => {\n        const { makeServicePlugin, Todo } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-todos',\n              Model: Todo,\n              service: feathersClient.service('my-todos')\n            })\n          ]\n        })\n        const actions = mapActions('my-todos', ['find'])\n\n        actions.find\n          .call({ $store: store }, { query: { $limit: 1 } })\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response[0]))\n            assert(response.length === 1, 'only one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 0, description: 'Do the first', isComplete: false },\n              'the first record was returned'\n            )\n            done()\n          })\n      })\n\n      it('find with $select', done => {\n        const { makeServicePlugin, Todo } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-todos',\n              Model: Todo,\n              service: feathersClient.service('my-todos')\n            })\n          ]\n        })\n        const actions = mapActions('my-todos', ['find'])\n\n        actions.find\n          .call(\n            { $store: store },\n            { query: { $limit: 1, $select: ['id', 'description'] } }\n          )\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response[0]))\n            assert(response.length === 1, 'only one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 0, description: 'Do the first' },\n              'the first record was returned'\n            )\n            done()\n          })\n      })\n\n      it('find with skip', done => {\n        const { makeServicePlugin, Todo } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-todos',\n              Model: Todo,\n              service: feathersClient.service('my-todos')\n            })\n          ]\n        })\n        const actions = mapActions('my-todos', ['find'])\n\n        actions.find\n          .call({ $store: store }, { query: { $skip: 9 } })\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response[0]))\n            assert(response.length === 1, 'one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 9, description: 'Do the tenth', isComplete: false },\n              'the tenth record was returned'\n            )\n            done()\n          })\n      })\n\n      it('Find with limit and skip', done => {\n        const { makeServicePlugin, Todo } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-todos',\n              Model: Todo,\n              service: feathersClient.service('my-todos')\n            })\n          ]\n        })\n        const actions = mapActions('my-todos', ['find'])\n\n        actions.find\n          .call({ $store: store }, { query: { $limit: 1, $skip: 8 } })\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response[0]))\n            assert(response.length === 1, 'one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 8, description: 'Do the ninth', isComplete: false },\n              'the ninth record was returned'\n            )\n            done()\n          })\n      })\n    })\n\n    describe('with pagination', () => {\n      it('find with limit', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n\n        actions.find\n          .call({ $store: store }, { query: { $limit: 1 } })\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response.data[0]))\n            assert(response.data.length === 1, 'only one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 0, description: 'Do the first', isComplete: false },\n              'the first record was returned'\n            )\n            assert(response.limit === 1, 'limit was correct')\n            assert(response.skip === 0, 'skip was correct')\n            assert(response.total === 10, 'total was correct')\n            done()\n          })\n      })\n\n      it('find with $select', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n\n        actions.find\n          .call(\n            { $store: store },\n            { query: { $limit: 1, $select: ['id', 'description'] } }\n          )\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response.data[0]))\n            assert(response.data.length === 1, 'only one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 0, description: 'Do the first' },\n              'the first record was returned'\n            )\n            assert(response.limit === 1, 'limit was correct')\n            assert(response.skip === 0, 'skip was correct')\n            assert(response.total === 10, 'total was correct')\n            done()\n          })\n      })\n\n      it('find with skip', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n\n        actions.find\n          .call({ $store: store }, { query: { $skip: 9 } })\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response.data[0]))\n            assert(response.data.length === 1, 'only one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 9, description: 'Do the tenth', isComplete: false },\n              'the tenth record was returned'\n            )\n            assert(response.limit === 10, 'limit was correct')\n            assert(response.skip === 9, 'skip was correct')\n            assert(response.total === 10, 'total was correct')\n            done()\n          })\n      })\n\n      it('find with limit and skip', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n\n        actions.find\n          .call({ $store: store }, { query: { $limit: 1, $skip: 8 } })\n          .then(response => {\n            const returnedRecord = JSON.parse(JSON.stringify(response.data[0]))\n            assert(response.data.length === 1, 'only one record was returned')\n            assert.deepEqual(\n              returnedRecord,\n              { id: 8, description: 'Do the ninth', isComplete: false },\n              'the ninth record was returned'\n            )\n            assert(response.limit === 1, 'limit was correct')\n            assert(response.skip === 8, 'skip was correct')\n            assert(response.total === 10, 'total was correct')\n            done()\n          })\n      })\n\n      it('adds default pagination data to the store', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n\n        actions.find.call({ $store: store }, { query: {} }).then(() => {\n          const { default: d } = store.state['my-tasks'].pagination\n          assert(d.mostRecent)\n          assert(d.mostRecent.queriedAt)\n          assert(d.mostRecent.query)\n          assert(d.mostRecent.queryId === '{}')\n          assert(d.mostRecent.queryParams)\n          assert(d.mostRecent.pageId === '{\"$limit\":10,\"$skip\":0}')\n          assert.deepEqual(d.mostRecent.pageParams, { $limit: 10, $skip: 0 })\n          assert(d['{}'])\n          assert(d['{}'].queryParams)\n          assert(d['{}'].total === 10)\n          assert(d['{}']['{\"$limit\":10,\"$skip\":0}'])\n          assert(d['{}']['{\"$limit\":10,\"$skip\":0}'].ids.length === 10)\n          assert(d['{}']['{\"$limit\":10,\"$skip\":0}'].queriedAt)\n          assert.deepEqual(d['{}']['{\"$limit\":10,\"$skip\":0}'].pageParams, {\n            $limit: 10,\n            $skip: 0\n          })\n\n          done()\n        })\n      })\n\n      it('can provide a query identifier to store pagination', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n        const qid = 'component-name'\n\n        actions.find.call({ $store: store }, { query: {}, qid }).then(() => {\n          const qidPaginationState = store.state['my-tasks'].pagination[qid]\n          assert(qidPaginationState, 'got pagination state for qid')\n          done()\n        })\n      })\n\n      it('updates properly with limit and skip', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n        const qid = 'component-name'\n\n        actions.find\n          .call({ $store: store }, { query: { $limit: 5, $skip: 2 }, qid })\n          .then(response => {\n            assert(store.state['my-tasks'].pagination[qid])\n            assert.deepEqual(\n              store.state['my-tasks'].pagination[qid].mostRecent.query,\n              { $limit: 5, $skip: 2 }\n            )\n            done()\n          })\n      })\n\n      it('works with multiple queries and identifiers', done => {\n        const { makeServicePlugin, Task } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'my-tasks',\n              Model: Task,\n              service: feathersClient.service('my-tasks')\n            })\n          ]\n        })\n        const actions = mapActions('my-tasks', ['find'])\n        const qids = ['component-query-zero', 'component-query-one']\n\n        actions.find\n        actions.find\n          .call({ $store: store }, { query: {}, qid: qids[0] })\n          .then(response =>\n            actions.find.call({ $store: store }, { query: {}, qid: qids[1] })\n          )\n          .then(response => {\n            qids.forEach(qid => {\n              assert(store.state['my-tasks'].pagination[qid])\n            })\n\n            done()\n          })\n      })\n\n      it(`allows non-id'd data to pass through`, done => {\n        const { makeServicePlugin, NoId } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'no-ids',\n              Model: NoId,\n              service: feathersClient.service('no-ids'),\n              idField: '_id'\n            })\n          ]\n        })\n        const actions = mapActions('no-ids', ['find'])\n\n        actions.find.call({ $store: store }, { query: {} }).then(response => {\n          assert(response.data.length === 10, 'records were still returned')\n          assert(\n            store.state['no-ids'].ids.length === 0,\n            'no records were stored in the state'\n          )\n\n          done()\n        })\n      })\n\n      it(`runs the afterFind action`, done => {\n        const { makeServicePlugin, NoId } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              servicePath: 'no-ids',\n              Model: NoId,\n              service: feathersClient.service('no-ids'),\n              idField: '_id',\n              actions: {\n                afterFind({}, response) {\n                  assert(\n                    response.data.length === 10,\n                    'records were still returned'\n                  )\n                  assert(\n                    store.state['no-ids'].ids.length === 0,\n                    'no records were stored in the state'\n                  )\n\n                  done()\n                }\n              }\n            })\n          ]\n        })\n        const actions = mapActions('no-ids', ['find'])\n\n        actions.find.call({ $store: store }, { query: {} })\n      })\n    })\n\n    it('updates errorOnFind state on service failure', done => {\n      const { makeServicePlugin, Broken } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'broken',\n            Model: Broken,\n            service: feathersClient.service('broken')\n          })\n        ]\n      })\n      const brokenState = store.state.broken\n      const actions = mapActions('broken', ['find'])\n\n      assertRejected(actions.find.call({ $store: store }, {}), done, () => {\n        assert(\n          brokenState.errorOnFind.message === 'find error',\n          'errorOnFind was set'\n        )\n        assert(brokenState.isFindPending === false, 'pending state was cleared')\n        assert(brokenState.ids.length === 0)\n      })\n\n      // Make sure proper state changes occurred before response\n      assert(brokenState.ids.length === 0)\n      assert(brokenState.errorOnFind === null)\n      assert(brokenState.isFindPending === true)\n    })\n  })\n\n  describe('Count', () => {\n    it('count without params fails', done => {\n      const { makeServicePlugin, Task } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-tasks',\n            Model: Task,\n            service: feathersClient.service('my-tasks')\n          })\n        ]\n      })\n      const actions = mapActions('my-tasks', ['count'])\n\n      try {\n        actions.count.call({ $store: store })\n      } catch (err) {\n        assert(err)\n        done()\n      }\n    })\n\n    it('count with query returns number', done => {\n      const { makeServicePlugin, Task } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-tasks',\n            Model: Task,\n            service: feathersClient.service('my-tasks')\n          })\n        ]\n      })\n      const actions = mapActions('my-tasks', ['count'])\n\n      actions.count.call({ $store: store }, { query: {} }).then(response => {\n        assert(response === 10, 'total is 10')\n        done()\n      })\n    })\n  })\n\n  describe('Get', function () {\n    it('updates store list state on service success', async () => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['get'])\n\n      assert(todoState.ids.length === 0)\n      assert(todoState.errorOnGet === null)\n      assert(todoState.isGetPending === false)\n      assert(todoState.idField === 'id')\n\n      const todo1 = await actions.get.call({ $store: store }, 0)\n      assert(todoState.ids.length === 1, 'only one item is in the store')\n      assert(todoState.errorOnGet === null, 'there was no errorOnGet')\n      assert(todoState.isGetPending === false, 'isGetPending is set to false')\n\n      let expectedKeyedById: NumberedList = {\n        0: { id: 0, description: 'Do the first', isComplete: false }\n      }\n      assert.deepEqual(\n        JSON.parse(JSON.stringify(todoState.keyedById)),\n        expectedKeyedById\n      )\n\n      // Make a request with the array syntax that allows passing params\n      const response2 = await actions.get.call({ $store: store }, [1, {}])\n      expectedKeyedById = {\n        0: { id: 0, description: 'Do the first', isComplete: false },\n        1: { id: 1, description: 'Do the second', isComplete: false }\n      }\n      assert(response2.description === 'Do the second')\n      assert.deepEqual(\n        JSON.parse(JSON.stringify(todoState.keyedById)),\n        expectedKeyedById\n      )\n\n      // Edit the first record in the store so the data is different.\n      // Make a request for the first record again, and it should be updated.\n      const clone1 = todo1.clone()\n      clone1.description = 'MODIFIED IN THE VUEX STORE'\n      clone1.commit()\n\n      assert.strictEqual(\n        todoState.keyedById[0].description,\n        clone1.description,\n        'the store instance was updated'\n      )\n\n      const response3 = await actions.get.call({ $store: store }, [0, {}])\n      const todo0 = Todo.getFromStore(0)\n      assert(response3.description === 'Do the first')\n      assert.deepEqual(\n        JSON.parse(JSON.stringify(todoState.keyedById)),\n        expectedKeyedById,\n        'The data is back as it was on the API server'\n      )\n    })\n\n    it('does not make remote call when skipRequestIfExists=true', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['get'])\n\n      assert(todoState.ids.length === 0)\n      assert(todoState.errorOnGet === null)\n      assert(todoState.isGetPending === false)\n      assert(todoState.idField === 'id')\n\n      actions.get.call({ $store: store }, 0).then(() => {\n        assert(todoState.ids.length === 1, 'only one item is in the store')\n        assert(todoState.errorOnGet === null, 'there was no errorOnGet')\n        assert(todoState.isGetPending === false, 'isGetPending is set to false')\n        let expectedKeyedById: NumberedList = {\n          0: { id: 0, description: 'Do the first', isComplete: false }\n        }\n        assert.deepEqual(\n          JSON.parse(JSON.stringify(todoState.keyedById)),\n          expectedKeyedById\n        )\n\n        // Make a request with the array syntax that allows passing params\n        actions.get.call({ $store: store }, [1, {}]).then(response2 => {\n          expectedKeyedById = {\n            0: { id: 0, description: 'Do the first', isComplete: false },\n            1: { id: 1, description: 'Do the second', isComplete: false }\n          }\n          assert(response2.description === 'Do the second')\n          assert.deepEqual(\n            JSON.parse(JSON.stringify(todoState.keyedById)),\n            expectedKeyedById\n          )\n\n          // Make a request to an existing record and return the existing data first, then update `keyedById`\n          todoState.keyedById = {\n            0: { id: 0, description: 'Do the FIRST', isComplete: false }, // twist the data to see difference\n            1: { id: 1, description: 'Do the second', isComplete: false }\n          }\n          actions.get\n            .call({ $store: store }, [0, { skipRequestIfExists: true }])\n            .then(response3 => {\n              expectedKeyedById = {\n                0: { id: 0, description: 'Do the FIRST', isComplete: false },\n                1: { id: 1, description: 'Do the second', isComplete: false }\n              }\n              assert(response3.description === 'Do the FIRST')\n              assert.deepEqual(\n                JSON.parse(JSON.stringify(todoState.keyedById)),\n                expectedKeyedById\n              )\n\n              // The remote data will never arriive\n              setTimeout(() => {\n                expectedKeyedById = {\n                  0: { id: 0, description: 'Do the FIRST', isComplete: false },\n                  1: { id: 1, description: 'Do the second', isComplete: false }\n                }\n                assert.deepEqual(\n                  JSON.parse(JSON.stringify(todoState.keyedById)),\n                  expectedKeyedById\n                )\n                done()\n              }, 100)\n            })\n        })\n      })\n\n      // Make sure proper state changes occurred before response\n      assert(todoState.ids.length === 0)\n      assert(todoState.errorOnCreate === null)\n      assert(todoState.isGetPending === true)\n      assert.deepEqual(JSON.parse(JSON.stringify(todoState.keyedById)), {})\n    })\n\n    it('updates errorOnGet state on service failure', done => {\n      const { makeServicePlugin, Broken } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'broken',\n            Model: Broken,\n            service: feathersClient.service('broken')\n          })\n        ]\n      })\n      const brokenState = store.state.broken\n      const actions = mapActions('broken', ['get'])\n\n      assertRejected(actions.get.call({ $store: store }, {}), done, () => {\n        assert(\n          brokenState.errorOnGet.message === 'get error',\n          'errorOnGet was set'\n        )\n        assert(brokenState.isGetPending === false, 'pending state was cleared')\n        assert(brokenState.ids.length === 0)\n      })\n\n      // Make sure proper state changes occurred before response\n      assert(brokenState.ids.length === 0)\n      assert(brokenState.errorOnGet === null)\n      assert(brokenState.isGetPending === true)\n    })\n  })\n\n  describe('Create', function () {\n    it('updates store list state on service success', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['create'])\n\n      actions.create\n        .call({ $store: store }, { description: 'Do the second' })\n        .then(response => {\n          assert(todoState.ids.length === 1)\n          assert(todoState.errorOnCreate === null)\n          assert(todoState.isCreatePending === false)\n          assert.deepEqual(todoState.keyedById[response.id], response)\n          done()\n        })\n\n      // Make sure proper state changes occurred before response\n      assert(todoState.ids.length === 0)\n      assert(todoState.errorOnCreate === null)\n      assert(todoState.isCreatePending === true)\n      assert(todoState.idField === 'id')\n      assert.deepEqual(JSON.parse(JSON.stringify(todoState.keyedById)), {})\n    })\n\n    it('updates errorOnCreate state on service failure', done => {\n      const { makeServicePlugin, Broken } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'broken',\n            Model: Broken,\n            service: feathersClient.service('broken')\n          })\n        ]\n      })\n      const brokenState = store.state.broken\n      const actions = mapActions('broken', ['create'])\n\n      assertRejected(actions.create.call({ $store: store }, {}), done, () => {\n        assert(\n          brokenState.errorOnCreate.message === 'create error',\n          'errorOnCreate was set'\n        )\n        assert(\n          brokenState.isCreatePending === false,\n          'pending state was cleared'\n        )\n        assert(brokenState.ids.length === 0)\n      })\n\n      // Make sure proper state changes occurred before response\n      assert(brokenState.ids.length === 0)\n      assert(brokenState.errorOnCreate === null)\n      assert(brokenState.isCreatePending === true)\n    })\n  })\n\n  describe('Update', () => {\n    it('updates store list state on service success', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['create', 'update'])\n\n      actions.create\n        .call({ $store: store }, { description: 'Do the second' })\n        .then(() => {\n          actions.update\n            .call({ $store: store }, [\n              0,\n              { id: 0, description: 'Do da dishuz' }\n            ])\n            .then(responseFromUpdate => {\n              assert(todoState.ids.length === 1)\n              assert(todoState.errorOnUpdate === null)\n              assert(todoState.isUpdatePending === false)\n              assert(store.getters['my-todos/isUpdatePendingById'](0) === false, 'ID pending update clear')\n              assert(store.getters['my-todos/isSavePendingById'](0) === false, 'ID pending save clear')\n              assert(store.getters['my-todos/isPendingById'](0) === false, 'ID pending clear')\n              assert.deepEqual(\n                todoState.keyedById[responseFromUpdate.id],\n                responseFromUpdate\n              )\n              done()\n            })\n\n          // Make sure proper state changes occurred before response\n          assert(todoState.ids.length === 1)\n          assert(todoState.errorOnUpdate === null)\n          assert(todoState.isUpdatePending === true)\n          assert(store.getters['my-todos/isUpdatePendingById'](0) === true, 'ID pending update set')\n          assert(store.getters['my-todos/isSavePendingById'](0) === true, 'ID pending save set')\n          assert(store.getters['my-todos/isPendingById'](0) === true, 'ID pending set')\n          assert(todoState.idField === 'id')\n        })\n        .catch(error => {\n          assert(!error, error)\n        })\n    })\n\n    it('updates errorOnUpdate state on service failure', done => {\n      const { makeServicePlugin, Broken } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'broken',\n            Model: Broken,\n            service: feathersClient.service('broken')\n          })\n        ]\n      })\n      const brokenState = store.state.broken\n      const actions = mapActions('broken', ['update'])\n\n      assertRejected(\n        actions.update.call({ $store: store }, [0, { id: 0 }]),\n        done,\n        () => {\n          assert(\n            brokenState.errorOnUpdate.message === 'update error',\n            'errorOnUpdate was set'\n          )\n          assert(\n            brokenState.isUpdatePending === false,\n            'pending state was cleared'\n          )\n          assert(brokenState.ids.length === 0)\n        }\n      )\n\n      // Make sure proper state changes occurred before response\n      assert(brokenState.ids.length === 0)\n      assert(brokenState.errorOnUpdate === null)\n      assert(brokenState.isUpdatePending === true)\n    })\n  })\n\n  describe('Patch', () => {\n    it('updates only the changed properties', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['create', 'patch'])\n\n      const dataUnchanged = {\n        unchanged: true,\n        deep: { changed: false, unchanged: true }\n      }\n      const dataChanged = {\n        unchanged: true,\n        deep: { changed: true, unchanged: true }\n      }\n\n      actions.create\n        .call(\n          { $store: store },\n          Object.assign({ description: 'Do the second' }, dataUnchanged)\n        )\n        .then(() => {\n          actions.patch\n            .call({ $store: store }, [\n              0,\n              Object.assign({ description: 'Write a Vue app' }, dataChanged)\n            ])\n            .then(responseFromPatch => {\n              assert(todoState.ids.length === 1)\n              assert(todoState.errorOnPatch === null)\n              assert(todoState.isPatchPending === false)\n              assert(store.getters['my-todos/isPatchPendingById'](0) === false, 'ID pending patch clear')\n              assert(store.getters['my-todos/isSavePendingById'](0) === false, 'ID pending save clear')\n              assert(store.getters['my-todos/isPendingById'](0) === false, 'ID pending clear')\n              assert.deepEqual(\n                todoState.keyedById[responseFromPatch.id],\n                responseFromPatch\n              )\n              done()\n            })\n\n          // Make sure proper state changes occurred before response\n          assert(todoState.ids.length === 1)\n          assert(todoState.errorOnPatch === null)\n          assert(todoState.isPatchPending === true)\n          assert(store.getters['my-todos/isPatchPendingById'](0) === true, 'ID pending patch set')\n          assert(store.getters['my-todos/isSavePendingById'](0) === true, 'ID pending save set')\n          assert(store.getters['my-todos/isPendingById'](0) === true, 'ID pending set')\n          assert(todoState.idField === 'id')\n        })\n    })\n\n    it('overrides patch data with params.data', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const actions = mapActions('my-todos', ['create', 'patch'])\n      const originalData = { description: 'Do something', test: true }\n\n      actions.create.call({ $store: store }, originalData).then(() => {\n        const data = {\n          description:\n            'This description should not be patched since params.data is provided'\n        }\n        const params = { data: { test: false } }\n        actions.patch\n          .call({ $store: store }, [0, data, params])\n          .then(responseFromPatch => {\n            assert.equal(\n              responseFromPatch.description,\n              originalData.description,\n              'description should not have changed'\n            )\n            assert.equal(\n              responseFromPatch.test,\n              false,\n              'Providing params.data should have set the test attribute to false.'\n            )\n            done()\n          })\n      })\n    })\n\n    it('updates store state on service success', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['create', 'patch'])\n\n      actions.create\n        .call({ $store: store }, { description: 'Do the second' })\n        .then(() => {\n          actions.patch\n            .call({ $store: store }, [0, { description: 'Write a Vue app' }])\n            .then(responseFromPatch => {\n              assert(todoState.ids.length === 1)\n              assert(todoState.errorOnPatch === null)\n              assert(todoState.isPatchPending === false)\n              assert(store.getters['my-todos/isPatchPendingById'](0) === false, 'ID pending patch clear')\n              assert(store.getters['my-todos/isSavePendingById'](0) === false, 'ID pending save clear')\n              assert(store.getters['my-todos/isPendingById'](0) === false, 'ID pending clear')\n              assert.deepEqual(\n                todoState.keyedById[responseFromPatch.id],\n                responseFromPatch\n              )\n              done()\n            })\n\n          // Make sure proper state changes occurred before response\n          assert(todoState.ids.length === 1)\n          assert(todoState.errorOnPatch === null)\n          assert(todoState.isPatchPending === true)\n          assert(store.getters['my-todos/isPatchPendingById'](0) === true, 'ID pending patch set')\n          assert(store.getters['my-todos/isSavePendingById'](0) === true, 'ID pending save set')\n          assert(store.getters['my-todos/isPendingById'](0) === true, 'ID pending set')\n          assert(todoState.idField === 'id')\n        })\n    })\n\n    it('updates errorOnPatch state on service failure', done => {\n      const { makeServicePlugin, Broken } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'broken',\n            Model: Broken,\n            service: feathersClient.service('broken')\n          })\n        ]\n      })\n      const brokenState = store.state.broken\n      const actions = mapActions('broken', ['patch'])\n\n      assertRejected(\n        actions.patch.call({ $store: store }, [0, { id: 0 }]),\n        done,\n        () => {\n          assert(\n            brokenState.errorOnPatch.message === 'patch error',\n            'errorOnPatch was set'\n          )\n          assert(\n            brokenState.isPatchPending === false,\n            'pending state was cleared'\n          )\n          assert(brokenState.ids.length === 0)\n        }\n      )\n\n      // Make sure proper state changes occurred before response\n      assert(brokenState.ids.length === 0)\n      assert(brokenState.errorOnPatch === null)\n      assert(brokenState.isPatchPending === true)\n    })\n  })\n\n  describe('Remove', () => {\n    it('updates store state on service success', done => {\n      const { makeServicePlugin, Todo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'my-todos',\n            Model: Todo,\n            service: feathersClient.service('my-todos')\n          })\n        ]\n      })\n      const todoState = store.state['my-todos']\n      const actions = mapActions('my-todos', ['create', 'remove'])\n\n      actions.create\n        .call({ $store: store }, { description: 'Do the second' })\n        .then(() => {\n          actions.remove\n            .call({ $store: store }, 0)\n            .then(() => {\n              assert(todoState.ids.length === 0)\n              assert(todoState.errorOnRemove === null)\n              assert(todoState.isRemovePending === false)\n              assert(store.getters['my-todos/isRemovePendingById'](0) === false, 'ID pending remove clear')\n              assert(store.getters['my-todos/isSavePendingById'](0) === false, 'ID pending save clear')\n              assert(store.getters['my-todos/isPendingById'](0) === false, 'ID pending clear')\n              assert.deepEqual(todoState.keyedById, {})\n              done()\n            })\n            .catch(error => {\n              // eslint-disable-next-line no-console\n              console.log(error)\n            })\n\n          // Make sure proper state changes occurred before response\n          assert(todoState.ids.length === 1)\n          assert(todoState.errorOnRemove === null)\n          assert(todoState.isRemovePending === true)\n          assert(store.getters['my-todos/isRemovePendingById'](0) === true, 'ID pending remove set')\n          assert(store.getters['my-todos/isSavePendingById'](0) === false, 'ID pending save clear')\n          assert(store.getters['my-todos/isPendingById'](0) === true, 'ID pending set')\n          assert(todoState.idField === 'id')\n        })\n    })\n\n    it('updates errorOnRemove state on service failure', done => {\n      const { makeServicePlugin, Broken } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'broken',\n            Model: Broken,\n            service: feathersClient.service('broken')\n          })\n        ]\n      })\n      const brokenState = store.state.broken\n      const actions = mapActions('broken', ['remove'])\n\n      assertRejected(actions.remove.call({ $store: store }, 0), done, () => {\n        assert(\n          brokenState.errorOnRemove.message === 'remove error',\n          'errorOnRemove was set'\n        )\n        assert(\n          brokenState.isRemovePending === false,\n          'pending state was cleared'\n        )\n        assert(brokenState.ids.length === 0)\n      })\n\n      // Make sure proper state changes occurred before response\n      assert(brokenState.ids.length === 0)\n      assert(brokenState.errorOnRemove === null)\n      assert(brokenState.isRemovePending === true)\n    })\n  })\n})\n"
  },
  {
    "path": "test/service-module/service-module.getters.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport makeServiceGetters from '../../src/service-module/service-module.getters'\nimport makeServiceMutations from '../../src/service-module/service-module.mutations'\nimport makeServiceState from '../../src/service-module/service-module.state'\nimport {\n  globalModels,\n  clearModels\n} from '../../src/service-module/global-models'\n\nimport { values as _values } from 'lodash'\n\nconst options = {\n  idField: '_id',\n  tempIdField: '__id',\n  autoRemove: false,\n  serverAlias: 'service-module-getters',\n  Model: null,\n  service: null\n}\n\nconst { find, count, list, get, getCopyById, isCreatePendingById, isUpdatePendingById, isPatchPendingById, isRemovePendingById, isSavePendingById, isPendingById } = makeServiceGetters()\nconst { addItems, setIdPending, unsetIdPending } = makeServiceMutations()\n\ndescribe('Service Module - Getters', function () {\n  beforeEach(function () {\n    const state = makeServiceState(options)\n    this.items = [\n      {\n        _id: 1,\n        otherField: true,\n        age: 21,\n        teethRemaining: 2.501,\n        test: true\n      },\n      {\n        _id: 2,\n        name: 'Marshall',\n        otherField: true,\n        age: 24,\n        teethRemaining: 2.5,\n        test: true,\n        movies: [{ actors: ['Jerry the Mouse'] }]\n      },\n      {\n        _id: 3,\n        otherField: true,\n        age: 27,\n        teethRemaining: 12,\n        test: false,\n        movies: [{ actors: ['Tom Hanks', 'Tom Cruise', 'Tomcat'] }]\n      },\n      {\n        name: 'Mariah',\n        age: 19,\n        teethRemaining: 24,\n        status: 'temp'\n      }\n    ]\n    addItems(state, this.items)\n    this.state = state\n  })\n\n  it('list', function () {\n    const { state, items } = this\n    const results = list(state)\n\n    results.forEach((record, index) => {\n      const item = items[index]\n\n      assert.deepEqual(record, item, 'item in correct order')\n    })\n  })\n\n  it('getCopyById with keepCopiesInStore: true', function () {\n    const state = {\n      keepCopiesInStore: true,\n      copiesById: {\n        1: { test: true }\n      }\n    }\n\n    const result = getCopyById(state)(1)\n\n    assert(result.test, 'got the copy')\n  })\n\n  it('getCopyById with keepCopiesInStore: false', function () {\n    const state = {\n      keepCopiesInStore: false,\n      servicePath: 'todos',\n      serverAlias: 'my-getters-test'\n    }\n    Object.assign(globalModels, {\n      [state.serverAlias]: {\n        byServicePath: {\n          todos: {\n            copiesById: {\n              1: { test: true }\n            }\n          }\n        }\n      }\n    })\n\n    const result = getCopyById(state)(1)\n\n    assert(result.test, 'got the copy')\n\n    clearModels()\n  })\n\n  it('get works on keyedById', function () {\n    const { state, items } = this\n\n    const result = get(state)(1)\n\n    assert.deepEqual(result, items[0])\n  })\n\n  it('get works on tempsById', function () {\n    const { state } = this\n    const tempId = Object.keys(state.tempsById)[0]\n\n    const result = get(state)(tempId)\n\n    assert(result.__id === tempId)\n  })\n\n  it('find - no temps by default', function () {\n    const { state, items } = this\n    const params = { query: {} }\n    const results = find(state)(params)\n\n    assert.deepEqual(\n      results.data,\n      items.filter(i => i._id),\n      'the list was correct'\n    )\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n  })\n\n  it('find with temps', function () {\n    const { state, items } = this\n    // Set temps: false to skip the temps.\n    const params = { query: {}, temps: true }\n    const results = find(state)(params)\n\n    assert.deepEqual(results.data, items, 'the list was correct')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 4, 'total was correct')\n  })\n\n  it('find - no copies by default', function () {\n    const state = {\n      keepCopiesInStore: false,\n      servicePath: 'todos',\n      serverAlias: 'my-getters-test',\n      keyedById: {\n        1: { _id: 1, test: true, __isClone: false },\n        2: { _id: 2, test: true, __isClone: false },\n        3: { _id: 3, test: true, __isClone: false }\n      },\n      copiesById: {\n        1: { _id: 1, test: true, __isClone: true }\n      }\n    }\n    Object.assign(globalModels, {\n      [state.serverAlias]: {\n        byServicePath: {\n          todos: {\n            copiesById: {\n              1: { _id: 1, test: true, __isClone: true }\n            }\n          }\n        }\n      }\n    })\n\n    const params = { query: {} }\n    const results = find(state)(params)\n\n    assert.deepEqual(\n      results.data,\n      _values(state.keyedById).filter(i => !i.__isClone),\n      'the list was correct'\n    )\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n\n    clearModels()\n  })\n\n  it('find - with copies with keepCopiesInStore:true', function () {\n    const state = {\n      keepCopiesInStore: true,\n      idField: '_id',\n      keyedById: {\n        1: { _id: 1, test: true, __isClone: false },\n        2: { _id: 2, test: true, __isClone: false },\n        3: { _id: 3, test: true, __isClone: false }\n      },\n      copiesById: {\n        1: { _id: 1, test: true, __isClone: true }\n      }\n    }\n\n    const params = { query: {}, copies: true }\n    const results = find(state)(params)\n\n    const expected = [\n      { _id: 1, test: true, __isClone: true },\n      { _id: 2, test: true, __isClone: false },\n      { _id: 3, test: true, __isClone: false }\n    ]\n\n    assert.deepEqual(results.data, expected, 'the list was correct')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n  })\n\n  it('find - with copies with keepCopiesInStore:false', function () {\n    const state = {\n      keepCopiesInStore: false,\n      servicePath: 'todos',\n      serverAlias: 'my-getters-test',\n      idField: '_id',\n      keyedById: {\n        1: { _id: 1, test: true, __isClone: false },\n        2: { _id: 2, test: true, __isClone: false },\n        3: { _id: 3, test: true, __isClone: false }\n      }\n    }\n    Object.assign(globalModels, {\n      [state.serverAlias]: {\n        byServicePath: {\n          todos: {\n            copiesById: {\n              1: { _id: 1, test: true, __isClone: true }\n            }\n          }\n        }\n      }\n    })\n\n    const params = { query: {}, copies: true }\n    const results = find(state)(params)\n\n    const expected = [\n      { _id: 1, test: true, __isClone: true },\n      { _id: 2, test: true, __isClone: false },\n      { _id: 3, test: true, __isClone: false }\n    ]\n\n    assert.deepEqual(results.data, expected, 'the list was correct')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n\n    clearModels()\n  })\n\n  it('find - with copies and temps', function () {\n    const state = {\n      keepCopiesInStore: false,\n      servicePath: 'todos',\n      serverAlias: 'my-getters-test',\n      idField: '_id',\n      keyedById: {\n        1: { _id: 1, test: true, __isClone: false },\n        2: { _id: 2, test: true, __isClone: false },\n        3: { _id: 3, test: true, __isClone: false }\n      },\n      tempsById: {\n        abc: { __id: 'abc', test: true, __isClone: false, __isTemp: true }\n      }\n    }\n    Object.assign(globalModels, {\n      [state.serverAlias]: {\n        byServicePath: {\n          todos: {\n            copiesById: {\n              1: { _id: 1, test: true, __isClone: true }\n            }\n          }\n        }\n      }\n    })\n\n    const params = { query: {}, copies: true, temps: true }\n    const results = find(state)(params)\n\n    const expected = [\n      { _id: 1, test: true, __isClone: true },\n      { _id: 2, test: true, __isClone: false },\n      { _id: 3, test: true, __isClone: false },\n      { __id: 'abc', test: true, __isClone: false, __isTemp: true }\n    ]\n\n    assert.deepEqual(results.data, expected, 'the list was correct')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 4, 'total was correct')\n\n    clearModels()\n  })\n\n  it('find with query', function () {\n    const { state } = this\n    const params = { query: { test: false } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 3, 'the correct record was returned')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 1, 'total was correct')\n  })\n\n  it('find with custom operator', function () {\n    const { state } = this\n    const params = { query: { test: false, $populateParams: 'test' } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 3, 'the correct record was returned')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 1, 'total was correct')\n  })\n\n  it('find with paramsForServer option', function () {\n    const { state } = this\n    state.paramsForServer = ['_$client']\n    const params = { query: { test: false, _$client: 'test' } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 3, 'the correct record was returned')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 1, 'total was correct')\n  })\n\n  it('find with non-whitelisted custom operator fails', function () {\n    const { state } = this\n    const params = { query: { $client: 'test' } }\n    try {\n      find(state)(params)\n    } catch (error) {\n      assert(error)\n    }\n  })\n\n  it('find with whitelisted custom operators', function () {\n    const { state } = this\n    state.whitelist = ['$regex', '$options']\n    const query = {\n      name: { $regex: 'marsh', $options: 'igm' }\n    }\n    const params = { query }\n    let results\n    try {\n      results = find(state)(params)\n    } catch (error) {\n      assert(!error, 'should not have failed with whitelisted custom operator')\n    }\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 2, 'the correct record was returned')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 1, 'total was correct')\n  })\n\n  it('find works with $elemMatch', function () {\n    const { state } = this\n    const query = {\n      movies: {\n        $elemMatch: { actors: 'Jerry the Mouse' }\n      }\n    }\n    const params = { query }\n    const results = find(state)(params)\n\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 2, 'the correct record was returned')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 1, 'total was correct')\n  })\n\n  it('find with limit', function () {\n    const { state } = this\n    const params = { query: { $limit: 1 } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 1, 'the correct record was returned')\n    assert(results.limit === 1, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n  })\n\n  it('find with skip', function () {\n    const { state } = this\n    const params = { query: { $skip: 1 } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 2, 'the length was correct')\n    assert(results.data[0]._id === 2, 'the correct record was returned')\n    assert(results.data[1]._id === 3, 'the correct record was returned')\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 1, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n  })\n\n  it('find with limit and skip', function () {\n    const { state } = this\n    const params = { query: { $limit: 1, $skip: 1 } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 1, 'the length was correct')\n    assert(results.data[0]._id === 2, 'the correct record was returned')\n    assert(results.limit === 1, 'limit was correct')\n    assert(results.skip === 1, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n  })\n\n  it('find with select', function () {\n    const { state } = this\n    const params = { query: { $select: ['otherField'] } }\n    const results = find(state)(params)\n\n    assert(results.data.length === 3, 'the length was correct')\n    results.data.forEach(result => {\n      assert(Object.keys(result).length <= 1, 'only one field was returned')\n    })\n    assert.equal(\n      results.data.filter(i => i.otherField).length,\n      3,\n      'three records have the field.'\n    )\n    assert(results.limit === 0, 'limit was correct')\n    assert(results.skip === 0, 'skip was correct')\n    assert(results.total === 3, 'total was correct')\n  })\n\n  it('find with sort ascending on integers', function () {\n    const { state } = this\n    const params = {\n      query: {\n        $sort: { age: 1 }\n      }\n    }\n    const results = find(state)(params)\n\n    results.data\n      .map(i => i.age)\n      .reduce((oldest, current) => {\n        assert(current > oldest, 'age should have been older than previous')\n        return current\n      }, 0)\n  })\n\n  it('find with sort descending on integers', function () {\n    const { state } = this\n    const params = {\n      query: {\n        $sort: { age: -1 }\n      }\n    }\n    const results = find(state)(params)\n\n    results.data\n      .map(i => i.age)\n      .reduce((oldest, current) => {\n        assert(current < oldest, 'age should have been younger than previous')\n        return current\n      }, 100)\n  })\n\n  it('find with sort ascending on floats', function () {\n    const { state } = this\n    const params = {\n      query: {\n        $sort: { teethRemaining: 1 }\n      }\n    }\n    const results = find(state)(params)\n\n    results.data\n      .map(i => i.teethRemaining)\n      .reduce((oldest, current) => {\n        assert(\n          current > oldest,\n          'teethRemaining should have been older than previous'\n        )\n        return current\n      }, 0)\n  })\n\n  it('find with sort descending on floats', function () {\n    const { state } = this\n    const params = {\n      query: {\n        $sort: { teethRemaining: -1 }\n      }\n    }\n    const results = find(state)(params)\n\n    results.data\n      .map(i => i.teethRemaining)\n      .reduce((oldest, current) => {\n        assert(\n          current < oldest,\n          'teethRemaining should have been younger than previous'\n        )\n        return current\n      }, 100)\n  })\n\n  it('count without params fails', function () {\n    const { state } = this\n\n    try {\n      count(state, { find })(null)\n    } catch (error) {\n      assert(error)\n    }\n  })\n\n  it('count without query fails', function () {\n    const { state } = this\n\n    try {\n      count(state, { find: find(state) })({})\n    } catch (error) {\n      assert(error)\n    }\n  })\n\n  it('count returns the number of records in the store', function () {\n    const { state } = this\n\n    const total = count(state, { find: find(state) })({ query: {} })\n    assert(total === 3, 'count is 3')\n  })\n\n  it('is*PendingById', function() {\n    const { state } = this\n\n    // Set up getters\n    const getters: any = {\n      isCreatePendingById: isCreatePendingById(state),\n      isUpdatePendingById: isUpdatePendingById(state),\n      isPatchPendingById: isPatchPendingById(state),\n      isRemovePendingById: isRemovePendingById(state),\n      isSavePendingById,\n      isPendingById\n    }\n    getters.isSavePendingById = isSavePendingById(state, getters)\n    getters.isPendingById = isPendingById(state, getters)\n\n    assert(isCreatePendingById(state)(42) === false, 'creating status is clear')\n    assert(isUpdatePendingById(state)(42) === false, 'updating status is clear')\n    assert(isPatchPendingById(state)(42) === false, 'patching status is clear')\n    assert(isRemovePendingById(state)(42) === false, 'removing status is clear')\n    assert(isSavePendingById(state, getters)(42) === false, 'saving status is clear')\n    assert(isPendingById(state, getters)(42) === false, 'any method pending status is clear')\n\n    // Create\n    setIdPending(state, { method: 'create', id: 42})\n    assert(isCreatePendingById(state)(42) === true, 'creating status is set')\n    assert(isSavePendingById(state, getters)(42) === true, 'saving status is set')\n    assert(isPendingById(state, getters)(42) === true, 'any method pending status is set')\n\n    unsetIdPending(state, { method: 'create', id: 42 })\n    assert(isCreatePendingById(state)(42) === false, 'creating status is clear')\n    assert(isUpdatePendingById(state)(42) === false, 'updating status is clear')\n    assert(isPatchPendingById(state)(42) === false, 'patching status is clear')\n    assert(isRemovePendingById(state)(42) === false, 'removing status is clear')\n    assert(isSavePendingById(state, getters)(42) === false, 'saving status is clear')\n    assert(isPendingById(state, getters)(42) === false, 'any method pending status is clear')\n\n    // Update\n    setIdPending(state, { method: 'update', id: 42})\n    assert(isUpdatePendingById(state)(42) === true, 'updating status is set')\n    assert(isSavePendingById(state, getters)(42) === true, 'saving status is set')\n    assert(isPendingById(state, getters)(42) === true, 'any method pending status is set')\n\n    unsetIdPending(state, { method: 'update', id: 42 })\n    assert(isCreatePendingById(state)(42) === false, 'creating status is clear')\n    assert(isUpdatePendingById(state)(42) === false, 'updating status is clear')\n    assert(isPatchPendingById(state)(42) === false, 'patching status is clear')\n    assert(isRemovePendingById(state)(42) === false, 'removing status is clear')\n    assert(isSavePendingById(state, getters)(42) === false, 'saving status is clear')\n    assert(isPendingById(state, getters)(42) === false, 'any method pending status is clear')\n\n    // Patch\n    setIdPending(state, { method: 'patch', id: 42})\n    assert(isPatchPendingById(state)(42) === true, 'patching status is set')\n    assert(isSavePendingById(state, getters)(42) === true, 'saving status is set')\n    assert(isPendingById(state, getters)(42) === true, 'any method pending status is set')\n\n    unsetIdPending(state, { method: 'patch', id: 42 })\n    assert(isCreatePendingById(state)(42) === false, 'creating status is clear')\n    assert(isUpdatePendingById(state)(42) === false, 'updating status is clear')\n    assert(isPatchPendingById(state)(42) === false, 'patching status is clear')\n    assert(isRemovePendingById(state)(42) === false, 'removing status is clear')\n    assert(isSavePendingById(state, getters)(42) === false, 'saving status is clear')\n    assert(isPendingById(state, getters)(42) === false, 'any method pending status is clear')\n\n    // Remove\n    setIdPending(state, { method: 'remove', id: 42})\n    assert(isRemovePendingById(state)(42) === true, 'removing status is set')\n    assert(isSavePendingById(state, getters)(42) === false, 'saving status is clear for remove')\n    assert(isPendingById(state, getters)(42) === true, 'any method pending status is set')\n\n    unsetIdPending(state, { method: 'remove', id: 42 })\n    assert(isCreatePendingById(state)(42) === false, 'creating status is clear')\n    assert(isUpdatePendingById(state)(42) === false, 'updating status is clear')\n    assert(isPatchPendingById(state)(42) === false, 'patching status is clear')\n    assert(isRemovePendingById(state)(42) === false, 'removing status is clear')\n    assert(isSavePendingById(state, getters)(42) === false, 'saving status is clear')\n    assert(isPendingById(state, getters)(42) === false, 'any method pending status is clear')\n  })\n})\n"
  },
  {
    "path": "test/service-module/service-module.mutations.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport { assertGetter, makeStore } from '../test-utils'\nimport makeServiceMutations, {\n  PendingServiceMethodName, PendingIdServiceMethodName\n} from '../../src/service-module/service-module.mutations'\nimport makeServiceState from '../../src/service-module/service-module.state'\nimport errors from '@feathersjs/errors'\nimport Vue from 'vue'\nimport Vuex from 'vuex'\nimport fakeData from '../fixtures/fake-data'\nimport { Service as MemoryService } from 'feathers-memory'\nimport { getQueryInfo } from '../../src/utils'\nimport { diff as deepDiff } from 'deep-object-diff'\nimport omitDeep from 'omit-deep-lodash'\nimport feathersVuex from '../../src/index'\n\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\n\nimport {\n  globalModels,\n  clearModels\n} from '../../src/service-module/global-models'\n\nconst { BaseModel } = feathersVuex(feathersClient, {\n  serverAlias: 'mutations'\n})\n\nVue.use(Vuex)\n\nclass Todo extends BaseModel {\n  public static modelName = 'Todo'\n  public static test = true\n}\n\nconst options = {\n  idField: '_id',\n  tempIdField: '__id',\n  autoRemove: false,\n  serverAlias: 'myApi',\n  service: feathersClient.service('mutations-todo'),\n  Model: Todo\n}\n\nconst {\n  addItem,\n  addItems,\n  updateItem,\n  updateItems,\n  removeItem,\n  removeItems,\n  clearAll,\n  createCopy,\n  resetCopy,\n  commitCopy,\n  clearCopy,\n  updatePaginationForQuery,\n  setPending,\n  unsetPending,\n  setError,\n  clearError,\n  setIdPending,\n  unsetIdPending\n} = makeServiceMutations()\n\nclass ComicService extends MemoryService {\n  public create(data, params) {\n    return super.create(data, params).then(response => {\n      delete response.__id\n      delete response.__isTemp\n      return response\n    })\n  }\n  // @ts-ignore\n  public update(id, data, params) {\n    data.createdAt = new Date()\n    // this._super(data, params, callback)\n  }\n}\n\nfunction makeContext() {\n  feathersClient.use(\n    'comics',\n    // @ts-ignore\n    new ComicService({ store: makeStore() })\n  )\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'service-module-mutations'\n  })\n  class Comic extends BaseModel {\n    public static modelName = 'Comic'\n    public static test = true\n\n    public constructor(data, options?) {\n      super(data, options)\n    }\n  }\n  const store = new Vuex.Store({\n    strict: true,\n    plugins: [\n      makeServicePlugin({\n        Model: Comic,\n        service: feathersClient.service('comics'),\n        servicePath: 'comics',\n        idField: '_id',\n        tempIdField: '__id'\n      })\n    ]\n  })\n  return {\n    makeServicePlugin,\n    BaseModel,\n    Comic,\n    store\n  }\n}\n\ndescribe('Service Module - Mutations', function () {\n  beforeEach(function () {\n    this.state = makeServiceState(options)\n    this.state.keepCopiesInStore = true\n  })\n\n  describe('Create, Update, Remove', function () {\n    it('addItem', function () {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const item2 = {\n        _id: 2,\n        test: true\n      }\n\n      addItem(state, item1)\n      assert(state.ids.length === 1)\n      assert(state.ids[0] === 1)\n      assert(state.keyedById[1].test)\n\n      // Add item 2\n      addItem(state, item2)\n      assert(state.ids.length === 2)\n      assert(state.ids[1] === 2)\n      assert(state.keyedById[2].test)\n\n      // Re-add item 1\n      addItem(state, item1)\n      assert(state.ids.length === 2, 'still only two items in the ids array')\n      assert(state.ids[0] === 1)\n      assert(state.keyedById[1].test)\n      assert(state.ids[1] === 2)\n      assert(state.keyedById[2].test)\n    })\n\n    it('addItems', function () {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const item2 = {\n        _id: 2,\n        test: true\n      }\n      const items = [item1, item2]\n      addItems(state, items)\n      assert(state.ids.length === 2, 'still only two items in the ids array')\n      assert(state.ids[0] === 1)\n      assert(state.keyedById[1].test)\n      assert(state.ids[1] === 2)\n      assert(state.keyedById[2].test)\n    })\n\n    it('updateItems', function () {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const item2 = {\n        _id: 2,\n        test: true\n      }\n      const items = [item1, item2]\n      addItems(state, items)\n\n      const item1updated = {\n        _id: 1,\n        test: false\n      }\n      const item2updated = {\n        _id: 2,\n        test: false\n      }\n      const itemsToUpdate = [item1updated, item2updated]\n      updateItems(state, itemsToUpdate)\n\n      assert(state.keyedById[1].test === false)\n      assert(state.keyedById[2].test === false)\n    })\n\n    it('removeItem', function () {\n      const state = this.state\n\n      addItem(state, { _id: 1, test: true })\n      removeItem(state, 1)\n\n      assert(state.ids.length === 0)\n      assert(Object.keys(state.keyedById).length === 0)\n    })\n\n    it('removeItem also removes clone', function () {\n      const state = this.state\n\n      const _id = 1\n\n      addItem(state, { _id, test: true })\n      createCopy(state, _id)\n\n      assert(state.copiesById[_id], 'clone exists')\n\n      removeItem(state, _id)\n\n      assert(!state.copiesById[_id], 'clone is removed')\n    })\n\n    it('removeItem also removes clone with keepCopiesInStore', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const _id = 1\n\n      store.commit('comics/addItem', { _id, test: true })\n      store.commit('comics/createCopy', _id)\n\n      assert(Comic.copiesById[_id], 'clone exists')\n\n      store.commit('comics/removeItem', _id)\n\n      assert(!Comic.copiesById[_id], 'clone is removed')\n    })\n\n    it('removeItems with array of ids', function () {\n      const state = this.state\n      const items = [\n        { _id: 1, test: true },\n        { _id: 2, test: true },\n        { _id: 3, test: true },\n        { _id: 4, test: true }\n      ]\n      addItems(state, items)\n      const itemsToRemove = [1, 2]\n      removeItems(state, itemsToRemove)\n\n      assert(state.ids.length === 2, 'should have 2 ids left')\n      assert(\n        Object.keys(state.keyedById).length === 2,\n        'should have 2 items left'\n      )\n    })\n\n    it('removeItems with array of items', function () {\n      const state = this.state\n      const items = [\n        { _id: 1, test: true },\n        { _id: 2, test: true },\n        { _id: 3, test: true },\n        { _id: 4, test: true }\n      ]\n      addItems(state, items)\n      const itemsToRemove = [\n        { _id: 1, test: true },\n        { _id: 2, test: true }\n      ]\n      removeItems(state, itemsToRemove)\n\n      assert(state.ids.length === 2, 'should have 2 ids left')\n      assert(\n        Object.keys(state.keyedById).length === 2,\n        'should have 2 items left'\n      )\n    })\n\n    it('removeItems also removes clone', function () {\n      const state = this.state\n\n      addItems(state, [\n        { _id: 1, test: true },\n        { _id: 2, test: true },\n        { _id: 3, test: true },\n        { _id: 4, test: true }\n      ])\n      const itemsToRemove = [1, 2]\n      createCopy(state, 1)\n      createCopy(state, 3)\n\n      assert(state.copiesById[1], 'clone exists')\n\n      removeItems(state, itemsToRemove)\n\n      assert(!state.copiesById[1], 'clone is removed')\n      assert(state.copiesById[3], 'other clone is not affected')\n    })\n\n    it('removeItems also removes clone with keepCopiesInStore', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      store.commit('comics/addItems', [\n        { _id: 1, test: true },\n        { _id: 2, test: true },\n        { _id: 3, test: true },\n        { _id: 4, test: true }\n      ])\n\n      const itemsToRemove = [1, 2]\n      store.commit('comics/createCopy', 1)\n      store.commit('comics/createCopy', 3)\n\n      assert(Comic.copiesById[1], 'clone exists')\n\n      store.commit('comics/removeItems', itemsToRemove)\n\n      assert(!Comic.copiesById[1], 'clone is removed')\n      assert(Comic.copiesById[3], 'other clone is not affected')\n    })\n\n    it('clearAll', function () {\n      const state = this.state\n\n      assert(state.ids.length === 0, 'initialy empty')\n      assert(Object.keys(state.keyedById).length === 0, 'initialy empty')\n      assert(Object.keys(state.copiesById).length === 0, 'initialy empty')\n\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const item2 = {\n        _id: 2,\n        test: true\n      }\n      const items = [item1, item2]\n      addItems(state, items)\n\n      createCopy(state, item1._id)\n\n      assert(state.ids.length === 2, 'ids are added correctly')\n      assert(\n        Object.keys(state.keyedById).length === 2,\n        'items are added correctly'\n      )\n      assert(\n        Object.keys(state.copiesById).length === 1,\n        'clone is added correctly'\n      )\n\n      clearAll(state)\n      assert(state.ids.length === 0, 'ids empty again')\n      assert(Object.keys(state.keyedById).length === 0, 'items empty again')\n      assert(Object.keys(state.copiesById).length === 0, 'clones empty again')\n    })\n\n    it('clearAll with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n      // @ts-ignore\n      const state = store.state.comics\n\n      assert(state.ids.length === 0, 'initialy empty')\n      assert(Object.keys(state.keyedById).length === 0, 'initialy empty')\n      assert(Object.keys(Comic.copiesById).length === 0, 'initialy empty')\n\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const item2 = {\n        _id: 2,\n        test: true\n      }\n      const items = [item1, item2]\n      store.commit('comics/addItems', items)\n      store.commit('comics/createCopy', item1._id)\n\n      assert(state.ids.length === 2, 'ids are added correctly')\n      assert(\n        Object.keys(state.keyedById).length === 2,\n        'items are added correctly'\n      )\n      assert(\n        Object.keys(Comic.copiesById).length === 1,\n        'clone is added correctly'\n      )\n\n      store.commit('comics/clearAll')\n\n      assert(state.ids.length === 0, 'ids empty again')\n      assert(Object.keys(state.keyedById).length === 0, 'items empty again')\n      assert(Object.keys(Comic.copiesById).length === 0, 'clones empty again')\n    })\n  })\n\n  describe('updateItem', function () {\n    it('updates existing item when addOnUpsert=true', function () {\n      const state = this.state\n      state.addOnUpsert = true\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const item1updated = {\n        _id: 1,\n        test: false\n      }\n      updateItem(state, item1updated)\n\n      assert(state.keyedById[1].test === false)\n    })\n\n    it('updates existing item when addOnUpsert=false', function () {\n      const state = this.state\n      state.addOnUpsert = false\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const item1updated = {\n        _id: 1,\n        test: false\n      }\n      updateItem(state, item1updated)\n\n      assert(state.keyedById[1].test === false)\n    })\n\n    it('adds non-existing item when addOnUpsert=true', function () {\n      const state = this.state\n      state.addOnUpsert = true\n\n      const item1updated = {\n        _id: 1,\n        test: false\n      }\n      updateItem(state, item1updated)\n\n      assert.deepEqual(\n        [state.addOnUpsert, state.ids, state.keyedById],\n        [true, [1], { 1: { _id: 1, test: false } }]\n      )\n      // assert(state.keyedById[1].test === false)\n    })\n\n    it('discards non-existing item when addOnUpsert=false', function () {\n      const state = this.state\n      state.addOnUpsert = false\n\n      const item1updated = {\n        _id: 1,\n        test: false\n      }\n      updateItem(state, item1updated)\n\n      assert(state.keyedById[1] == null)\n    })\n  })\n\n  describe('Vue event bindings', function () {\n    it('does not break when attempting to overwrite a getter', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        get getter() {\n          return 'Release the flying monkeys!'\n        }\n      }\n      assertGetter(item1, 'getter', 'Release the flying monkeys!')\n      const items = [item1]\n\n      addItems(state, items)\n\n      // Prove the getter is still in place in the store\n      assertGetter(state.keyedById[1], 'getter', 'Release the flying monkeys!')\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.getter'() {\n            // eslint-disable-next-line no-console\n            console.log(state.keyedById)\n            throw new Error('this should never happen')\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        getter: true\n      }\n      updateItem(state, updatedItem)\n\n      assert(state.keyedById[1].getter === 'Release the flying monkeys!')\n      done()\n    })\n\n    it('correctly emits events for existing array properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true,\n        users: ['Marshall', 'Mariah']\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.users'() {\n            assert(this.item.users.length === 3)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        test: false,\n        users: ['Marshall', 'Mariah', 'Scooby Doo']\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for new array properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.users'() {\n            assert(this.item.users.length === 3)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        test: false,\n        users: ['Marshall', 'Mariah', 'Scooby Doo']\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for existing object properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        obj: { test: true }\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.obj'() {\n            assert(this.item.obj.test === false)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        obj: { test: false }\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for new object properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.obj'() {\n            assert(this.item.obj.test === false)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        obj: { test: false }\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for existing boolean properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        isValid: true\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.isValid'() {\n            assert(this.item.isValid === false)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        isValid: false\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for new boolean properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.isValid'() {\n            assert(this.item.isValid === false)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        isValid: false\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for existing string properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        name: 'Marshall'\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.name'() {\n            assert(this.item.name === 'Xavier')\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        name: 'Xavier'\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for new string properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.name'() {\n            assert(this.item.name === 'Xavier')\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        name: 'Xavier'\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for existing null properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        name: null\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.name'() {\n            assert(this.item.name === 'Xavier')\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        name: 'Xavier'\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for properties set to null', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        name: 'Marshall'\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.name'() {\n            assert(this.item.name === null)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        name: null\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for existing number properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        age: 45\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.age'() {\n            assert(this.item.age === 50)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        age: 50\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events for new number properties', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1\n      }\n      const items = [item1]\n      addItems(state, items)\n\n      const vm = new Vue({\n        data: {\n          item: state.keyedById[1]\n        },\n        watch: {\n          'item.age'() {\n            assert(this.item.age === 50)\n            done()\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n\n      const updatedItem = {\n        _id: 1,\n        age: 50\n      }\n      updateItem(state, updatedItem)\n    })\n\n    it('correctly emits events after commitCopy', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        obj: { test: true },\n        get getter() {\n          return this.obj.test\n        },\n        set setter(val) {\n          this.obj.test = val\n        }\n      }\n      const items = [item1]\n\n      addItems(state, items)\n      const item = state.keyedById[item1._id]\n\n      createCopy(state, item._id)\n      const copy = state.copiesById[item1._id]\n\n      const vm = new Vue({\n        data: {\n          item,\n          copy\n        },\n        watch: {\n          'item.obj': {\n            handler() {\n              assert(this.item.obj.test === false)\n              done()\n            },\n            deep: true\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n      assert(vm.copy, 'vm has copy')\n\n      // Modify copy and commit\n      vm.copy.setter = false\n      commitCopy(state, item1._id)\n\n      assert(item.obj.test === false, 'deep obj should be false')\n      assert(vm.item.obj.test === false, 'deep obj should be false')\n    })\n\n    it('correctly emits events after resetCopy', function (done) {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        obj: { test: true },\n        get getter() {\n          return this.obj.test\n        },\n        set setter(val) {\n          this.obj.test = val\n        }\n      }\n      const items = [item1]\n\n      addItems(state, items)\n      const item = state.keyedById[item1._id]\n\n      // createCopy and modify, but don't commit\n      createCopy(state, item._id)\n      const copy = state.copiesById[item1._id]\n      copy.setter = false\n\n      const vm = new Vue({\n        data: {\n          item,\n          copy\n        },\n        watch: {\n          'copy.obj': {\n            handler() {\n              assert(this.copy.obj.test === true)\n              done()\n            },\n            deep: true\n          }\n        }\n      })\n\n      assert(vm.item, 'vm has item')\n      assert(vm.copy, 'vm has copy')\n\n      resetCopy(state, item1._id)\n\n      assert(item.obj.test === true, 'deep obj should be true')\n      assert(vm.item.obj.test === true, 'deep obj should be true')\n    })\n  })\n\n  describe('Copy & Commit', function () {\n    it('createCopy', function () {\n      const { state } = this\n      const item1 = {\n        _id: 1,\n        test: true,\n        get getter() {\n          return 'Life is a Joy!'\n        },\n        set setter(val) {\n          this.test = val\n        }\n      }\n      addItem(state, item1)\n      const original = state.keyedById[1]\n\n      createCopy(state, item1._id)\n\n      const copy = state.copiesById[item1._id]\n\n      assert.deepEqual(\n        original,\n        copy,\n        `original and copy have the same properties`\n      )\n\n      copy.setter = false\n      assert(copy.getter === 'Life is a Joy!', `getter was preserved`)\n      assert(copy.test === false, `copy was changed through setter`)\n      assert(original.test === true, `original item intact after copy changed`)\n    })\n\n    it('createCopy with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      store.commit('comics/addItem', item1)\n\n      // @ts-ignore\n      const original = store.state.comics.keyedById[1]\n\n      store.commit('comics/createCopy', item1._id)\n\n      const copy = Comic.copiesById[item1._id]\n\n      assert.deepEqual(\n        original,\n        copy,\n        `original and copy have the same properties`\n      )\n\n      copy.test = false\n      assert(copy.test === false, `copy was changed through setter`)\n      assert(original.test === true, `original item intact after copy changed`)\n\n      clearModels()\n    })\n\n    it('createCopy of temp', function () {\n      const { state } = this\n      const item1 = {\n        __id: 'abc',\n        test: true,\n        get getter() {\n          return 'Life is a Joy!'\n        },\n        set setter(val) {\n          this.test = val\n        }\n      }\n      addItem(state, item1)\n      const original = state.tempsById[item1[state.tempIdField]]\n\n      createCopy(state, original[state.tempIdField])\n\n      const copy = state.copiesById[original[state.tempIdField]]\n\n      copy.setter = false\n      assert(copy.getter === 'Life is a Joy!', `getter was preserved`)\n      assert(copy.test === false, `copy was changed through setter`)\n      assert(original.test === true, `original item intact after copy changed`)\n    })\n\n    it('createCopy of temp with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = {\n        __id: 'abc',\n        test: true\n      }\n      store.commit('comics/addItem', item1)\n\n      // @ts-ignore\n      const original = store.state.comics.tempsById[item1.__id]\n\n      store.commit('comics/createCopy', item1.__id)\n\n      const copy = Comic.copiesById[item1.__id]\n\n      copy.test = false\n      assert(copy.test === false, `copy was changed through setter`)\n      assert(original.test === true, `original item intact after copy changed`)\n\n      clearModels()\n    })\n\n    it('createCopy while existing copy', function () {\n      const { state } = this\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      addItem(state, item1)\n\n      const original = state.keyedById[1]\n\n      createCopy(state, item1._id)\n\n      const copy = state.copiesById[item1._id]\n      copy.test = false\n\n      createCopy(state, item1._id)\n\n      const copy2 = state.copiesById[item1._id]\n\n      assert(copy === copy2, `only one clone exists`)\n      assert(\n        copy.test === true && copy2.test === true,\n        `new clone overwrites old clone`\n      )\n    })\n\n    it('createCopy while existing copy with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      store.commit('comics/addItem', item1)\n\n      // @ts-ignore\n      const original = store.state.comics.keyedById[1]\n\n      store.commit('comics/createCopy', item1._id)\n      const copy = Comic.copiesById[item1._id]\n      copy.test = false\n\n      store.commit('comics/createCopy', original._id)\n      const copy2 = Comic.copiesById[item1._id]\n\n      assert(copy === copy2, `only one clone exists`)\n      assert(\n        copy.test === true && copy2.test === true,\n        `new clone overwrites old clone`\n      )\n\n      clearModels()\n    })\n\n    it('resetCopy', function () {\n      const { state } = this\n      const item1 = {\n        _id: 1,\n        test: true,\n        get getter() {\n          return 'Life is a Joy!'\n        },\n        set setter(val) {\n          this.test = val\n        }\n      }\n      addItem(state, item1)\n\n      // Create a copy and modify it.\n      createCopy(state, item1._id)\n      const copy = state.copiesById[item1._id]\n      copy.test = false\n\n      // Call resetCopy and check that it's back to the original value\n      resetCopy(state, item1._id)\n      assert(copy.test === true, 'the copy was reset')\n\n      // Make sure accessors stayed intact\n      assertGetter(copy, 'getter', 'Life is a Joy!')\n      copy.setter = false\n      assert(copy.test === false, 'the setter is intact')\n    })\n\n    it('resetCopy with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      store.commit('comics/addItem', item1)\n\n      // Create a copy and modify it.\n      store.commit('comics/createCopy', item1._id)\n      const copy = Comic.copiesById[item1._id]\n      copy.test = false\n\n      // Call resetCopy and check that it's back to the original value\n      store.commit('comics/resetCopy', item1._id)\n\n      assert(copy.test === true, 'the copy was reset')\n\n      clearModels()\n    })\n\n    it.skip('resetCopy with keepCopiesInStore: false and with intact getter/setter', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = {\n        _id: 1,\n        test: true,\n        get getter() {\n          return 'Life is a Joy!'\n        },\n        set setter(val) {\n          this.test = val\n        }\n      }\n      store.commit('comics/addItem', item1)\n\n      // Create a copy and modify it.\n      store.commit('comics/createCopy', item1._id)\n      const copy = Comic.copiesById[item1._id]\n      copy.test = false\n\n      // Call resetCopy and check that it's back to the original value\n      store.commit('comics/resetCopy', item1._id)\n\n      assert(copy.test === true, 'the copy was reset')\n\n      // Make sure accessors stayed intact\n      assertGetter(copy, 'getter', 'Life is a Joy!')\n      copy.setter = false\n      assert(copy.test === false, 'the setter is intact')\n\n      clearModels()\n    })\n\n    it('commitCopy', function () {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true,\n        get getter() {\n          return 'Life is a Joy!'\n        },\n        set setter(val) {\n          this.test = val\n        }\n      }\n      addItem(state, item1)\n      const original = state.keyedById[item1._id]\n\n      // Create a copy and modify it.\n      createCopy(state, item1._id)\n      const copy = state.copiesById[item1._id]\n      copy.test = false\n\n      commitCopy(state, item1._id)\n      assert(copy.test === false, `the copy wasn't changed after commitCopy`)\n      assert(original.test === false, 'original item updated after commitCopy')\n    })\n\n    it('commitCopy with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      store.commit('comics/addItem', item1)\n      // @ts-ignore\n      const original = store.state.comics.keyedById[item1._id]\n\n      // Create a copy and modify it.\n      store.commit('comics/createCopy', item1._id)\n      const copy = Comic.copiesById[item1._id]\n      copy.test = false\n\n      store.commit('comics/commitCopy', item1._id)\n\n      assert(copy.test === false, `the copy wasn't changed after commitCopy`)\n      assert(original.test === false, 'original item updated after commitCopy')\n\n      clearModels()\n    })\n\n    it('clearCopy', function () {\n      const state = this.state\n      const item1 = {\n        _id: 1,\n        test: true\n      }\n      addItem(state, item1)\n\n      // Create a copy then clear it.\n      createCopy(state, item1._id)\n      assert(state.copiesById[item1._id], `the copy is there!`)\n      clearCopy(state, item1._id)\n      assert(!state.copiesById[item1._id], `the copy is gone!`)\n    })\n\n    it('clearCopy with keepCopiesInStore: false', function () {\n      const context = makeContext()\n      const { Comic, store } = context\n\n      const item1 = { _id: 1, test: true }\n      store.commit('comics/addItem', item1)\n\n      // Create a copy then clear it.\n      store.commit('comics/createCopy', item1._id)\n\n      assert(Comic.copiesById[item1._id], `the copy is there!`)\n      store.commit('comics/clearCopy', item1._id)\n      assert(!Comic.copiesById[item1._id], `the copy is gone!`)\n\n      clearModels()\n    })\n  })\n\n  describe('Pagination', function () {\n    it('updatePaginationForQuery', function () {\n      this.timeout(600000)\n      const state = this.state\n      const qid = 'main-list'\n      const decisionTable = [\n        {\n          description: 'initial query empty',\n          query: {},\n          response: {\n            data: fakeData.transactions.slice(0, 10),\n            limit: 10,\n            skip: 0,\n            total: fakeData.transactions.length\n          },\n          makeResult(props) {\n            const {\n              query,\n              queryId,\n              queryParams,\n              pageId,\n              pageParams,\n              queriedAt\n            } = props\n\n            return {\n              defaultLimit: 10,\n              defaultSkip: 0,\n              'main-list': {\n                mostRecent: {\n                  query,\n                  queryId,\n                  queryParams,\n                  pageId,\n                  pageParams,\n                  queriedAt,\n                  total: 155\n                },\n                '{}': {\n                  total: fakeData.transactions.length,\n                  queryParams: {},\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":0}\"]: { //eslint-disable-line\n                    pageParams,\n                    ids: fakeData.transactions\n                      .slice(0, 10)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  }\n                }\n              }\n            }\n          }\n        },\n        {\n          description: 'initial query, limit 10, skip 0',\n          query: { $limit: 10 },\n          response: {\n            data: fakeData.transactions.slice(0, 10),\n            limit: 10,\n            skip: 0,\n            total: fakeData.transactions.length\n          },\n          makeResult(props) {\n            const {\n              query,\n              queryId,\n              queryParams,\n              pageId,\n              pageParams,\n              queriedAt\n            } = props\n\n            return {\n              defaultLimit: 10,\n              defaultSkip: 0,\n              'main-list': {\n                mostRecent: {\n                  query,\n                  queryId,\n                  queryParams,\n                  pageId,\n                  pageParams,\n                  queriedAt,\n                  total: 155\n                },\n                '{}': {\n                  total: fakeData.transactions.length,\n                  queryParams: {},\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":0}\"]: { //eslint-disable-line\n                    pageParams,\n                    ids: fakeData.transactions\n                      .slice(0, 10)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  }\n                }\n              }\n            }\n          }\n        },\n        {\n          description: 'initial query, limit 10, skip 10',\n          query: { $limit: 10, $skip: 10 },\n          response: {\n            data: fakeData.transactions.slice(10, 20),\n            limit: 10,\n            skip: 10,\n            total: fakeData.transactions.length\n          },\n          makeResult(props) {\n            const {\n              query,\n              queryId,\n              queryParams,\n              pageId,\n              pageParams,\n              queriedAt\n            } = props\n\n            return {\n              defaultLimit: 10,\n              defaultSkip: 0,\n              'main-list': {\n                mostRecent: {\n                  query,\n                  queryId,\n                  queryParams,\n                  pageId,\n                  pageParams,\n                  queriedAt,\n                  total: 155\n                },\n                '{}': {\n                  total: fakeData.transactions.length,\n                  queryParams: {},\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":0}\"]: { //eslint-disable-line\n                    pageParams: {\n                      $limit: 10,\n                      $skip: 0\n                    },\n                    ids: fakeData.transactions\n                      .slice(0, 10)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  },\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":10}\"]: { //eslint-disable-line\n                    pageParams: {\n                      $limit: 10,\n                      $skip: 10\n                    },\n                    ids: fakeData.transactions\n                      .slice(10, 20)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  }\n                }\n              }\n            }\n          }\n        },\n        {\n          description: 'separate query, limit 10, skip 10',\n          query: { test: true, $limit: 10, $skip: 10 },\n          response: {\n            data: fakeData.transactions.slice(10, 20),\n            limit: 10,\n            skip: 10,\n            total: fakeData.transactions.length\n          },\n          makeResult(props) {\n            const {\n              query,\n              queryId,\n              queryParams,\n              pageId,\n              pageParams,\n              queriedAt\n            } = props\n\n            return {\n              defaultLimit: 10,\n              defaultSkip: 0,\n              'main-list': {\n                mostRecent: {\n                  query,\n                  queryId,\n                  queryParams,\n                  pageId,\n                  pageParams,\n                  queriedAt,\n                  total: 155\n                },\n                '{}': {\n                  total: fakeData.transactions.length,\n                  queryParams: {},\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":0}\"]: { //eslint-disable-line\n                    pageParams: {\n                      $limit: 10,\n                      $skip: 0\n                    },\n                    ids: fakeData.transactions\n                      .slice(0, 10)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  },\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":10}\"]: { //eslint-disable-line\n                    pageParams: {\n                      $limit: 10,\n                      $skip: 10\n                    },\n                    ids: fakeData.transactions\n                      .slice(10, 20)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  }\n                },\n                '{\"test\":true}': {\n                  total: fakeData.transactions.length,\n                  queryParams: { test: true },\n                  [\"{\\\"$limit\\\":10,\\\"$skip\\\":10}\"]: { //eslint-disable-line\n                    pageParams: {\n                      $limit: 10,\n                      $skip: 10\n                    },\n                    ids: fakeData.transactions\n                      .slice(10, 20)\n                      .map(i => i[state.idField]),\n                    queriedAt\n                  }\n                }\n              }\n            }\n          }\n        }\n      ]\n\n      decisionTable.forEach(({ description, query, response, makeResult }) => {\n        const { queryId, queryParams, pageId, pageParams } = getQueryInfo(\n          { qid, query },\n          response\n        )\n        const queriedAt = new Date().getTime()\n        const expectedResult = makeResult({\n          query,\n          queryId,\n          queryParams,\n          pageId,\n          pageParams,\n          queriedAt\n        })\n\n        updatePaginationForQuery(state, { qid, response, query })\n\n        const diff = deepDiff(\n          omitDeep(state.pagination, 'queriedAt'),\n          omitDeep(expectedResult, 'queriedAt')\n        )\n\n        assert.deepEqual(\n          omitDeep(state.pagination, 'queriedAt'),\n          omitDeep(expectedResult, 'queriedAt'),\n          description\n        )\n      })\n    })\n  })\n\n  describe('Pending', function () {\n    it('setPending && unsetPending', function () {\n      const state = this.state\n      const methods: PendingServiceMethodName[] = [\n        'find',\n        'get',\n        'create',\n        'update',\n        'patch',\n        'remove'\n      ]\n\n      methods.forEach(method => {\n        const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n        assert(!state[`is${uppercaseMethod}Pending`])\n\n        // Set pending & check\n        setPending(state, method)\n        assert(state[`is${uppercaseMethod}Pending`])\n\n        // Unset pending & check\n        unsetPending(state, method)\n        assert(!state[`is${uppercaseMethod}Pending`])\n      })\n    })\n  })\n\n  describe('Per-instance Pending', function() {\n    it('setIdPending && unsetIdPending', function() {\n      const state = this.state\n      const methods: PendingIdServiceMethodName[] = [\n        'create',\n        'update',\n        'patch',\n        'remove'\n      ]\n\n      methods.forEach(method => {\n        const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n        assert(state[`isId${uppercaseMethod}Pending`].length === 0)\n\n        // Set pending & check\n        setIdPending(state, { method, id: 42 })\n        assert(state[`isId${uppercaseMethod}Pending`].includes(42))\n\n        // Unset pending & check\n        unsetIdPending(state, { method, id: 42 })\n        assert(state[`isId${uppercaseMethod}Pending`].length === 0)\n      })\n    })\n  })\n\n  describe('Errors', function () {\n    it('setError', function () {\n      const state = this.state\n      const methods: PendingServiceMethodName[] = [\n        'find',\n        'get',\n        'create',\n        'update',\n        'patch',\n        'remove'\n      ]\n\n      methods.forEach(method => {\n        const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n        setError(state, { method, error: new Error('This is a test') })\n        assert(state[`errorOn${uppercaseMethod}`].message)\n        assert(state[`errorOn${uppercaseMethod}`].name)\n        assert(state[`errorOn${uppercaseMethod}`].stack)\n      })\n    })\n\n    it('setError with feathers-errors', function () {\n      const state = this.state\n      const methods: PendingServiceMethodName[] = [\n        'find',\n        'get',\n        'create',\n        'update',\n        'patch',\n        'remove'\n      ]\n\n      methods.forEach(method => {\n        const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n        setError(state, {\n          method,\n          error: new errors.NotAuthenticated('You are not logged in')\n        })\n        assert(state[`errorOn${uppercaseMethod}`].className)\n        assert(state[`errorOn${uppercaseMethod}`].code)\n        assert(state[`errorOn${uppercaseMethod}`].hasOwnProperty('errors'))\n        assert(state[`errorOn${uppercaseMethod}`].hasOwnProperty('data'))\n        assert(state[`errorOn${uppercaseMethod}`].message)\n        assert(state[`errorOn${uppercaseMethod}`].name)\n        assert(state[`errorOn${uppercaseMethod}`].stack)\n      })\n    })\n\n    it('clearError', function () {\n      const state = this.state\n      const methods: PendingServiceMethodName[] = [\n        'find',\n        'get',\n        'create',\n        'update',\n        'patch',\n        'remove'\n      ]\n\n      methods.forEach(method => {\n        const uppercaseMethod = method.charAt(0).toUpperCase() + method.slice(1)\n\n        setError(state, { method, error: new Error('This is a test') })\n        clearError(state, method)\n        assert(\n          state[`errorOn${uppercaseMethod}`] === null,\n          `errorOn${uppercaseMethod} was cleared`\n        )\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/service-module/service-module.reinitialization.test.ts",
    "content": "import { assert } from 'chai'\nimport Vuex from 'vuex'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport feathersVuex from '../../src/index'\n\ninterface RootState {\n  todos: any\n}\n\nfunction makeContext() {\n  const todoService = feathersClient.service('todos')\n  const serverAlias = 'reinitialization'\n  const { makeServicePlugin, BaseModel, models } = feathersVuex(\n    feathersClient,\n    {\n      serverAlias\n    }\n  )\n  class Todo extends BaseModel {\n    public static modelName = 'Todo'\n  }\n  return {\n    makeServicePlugin,\n    BaseModel,\n    todoService,\n    Todo,\n    models,\n    serverAlias\n  }\n}\n\ndescribe('Service Module - Reinitialization', function () {\n  /**\n   * Tests that when the make service plugin is reinitialized state\n   * is reset in the vuex module/model.\n   * This prevents state pollution in SSR setups.\n   */\n  it('does not preserve module/model state when reinitialized', function () {\n    const {\n      makeServicePlugin,\n      todoService,\n      Todo,\n      models,\n      serverAlias\n    } = makeContext()\n    const todosPlugin = makeServicePlugin({\n      servicePath: 'todos',\n      Model: Todo,\n      service: todoService\n    })\n    let store = new Vuex.Store<RootState>({\n      plugins: [todosPlugin]\n    })\n    let todoState = store.state['todos']\n    const virginState = {\n      addOnUpsert: false,\n      autoRemove: false,\n      debug: false,\n      copiesById: {},\n      enableEvents: true,\n      errorOnCreate: null,\n      errorOnFind: null,\n      errorOnGet: null,\n      errorOnPatch: null,\n      errorOnRemove: null,\n      errorOnUpdate: null,\n      idField: 'id',\n      tempIdField: '__id',\n      ids: [],\n      isCreatePending: false,\n      isFindPending: false,\n      isGetPending: false,\n      isPatchPending: false,\n      isRemovePending: false,\n      isUpdatePending: false,\n      keepCopiesInStore: false,\n      debounceEventsTime: null,\n      debounceEventsMaxWait: 1000,\n      keyedById: {},\n      modelName: 'Todo',\n      nameStyle: 'short',\n      namespace: 'todos',\n      pagination: {\n        defaultLimit: null,\n        defaultSkip: null\n      },\n      paramsForServer: ['$populateParams'],\n      preferUpdate: false,\n      replaceItems: false,\n      serverAlias,\n      servicePath: 'todos',\n      skipRequestIfExists: false,\n      tempsById: {},\n      whitelist: [],\n      isIdCreatePending: [],\n      isIdUpdatePending: [],\n      isIdPatchPending: [],\n      isIdRemovePending: [],\n    }\n\n    assert.deepEqual(\n      todoState,\n      virginState,\n      'vuex module state is correct on first initialization'\n    )\n    assert.deepEqual(\n      models[serverAlias][Todo.name].store.state[Todo.namespace],\n      todoState,\n      'model state is the same as vuex module state on first initialization'\n    )\n\n    // Simulate some mutations on the store.\n    const todo = {\n      id: 1,\n      testProp: true\n    }\n\n    store.commit('todos/addItem', todo)\n    const serviceTodo = store.state['todos'].keyedById[1]\n\n    assert.equal(\n      todo.testProp,\n      serviceTodo.testProp,\n      'todo is added to the store'\n    )\n\n    assert.deepEqual(\n      models[serverAlias][Todo.name].store.state[Todo.namespace],\n      todoState,\n      'model state is the same as vuex module state when store is mutated'\n    )\n\n    // Here we are going to simulate the make service plugin being reinitialized.\n    // This is the default behaviour in SSR setups, e.g. nuxt universal mode,\n    // although unlikely in SPAs.\n    store = new Vuex.Store<RootState>({\n      plugins: [todosPlugin]\n    })\n\n    todoState = store.state['todos']\n\n    // We expect vuex module state for this service to be reset.\n    assert.deepEqual(\n      todoState,\n      virginState,\n      'store state in vuex module is not preserved on reinitialization'\n    )\n    // We also expect model store state for this service to be reset.\n    assert.deepEqual(\n      models[serverAlias][Todo.name].store.state[Todo.namespace],\n      virginState,\n      'store state in service model is not preserved on reinitialization'\n    )\n  })\n})\n"
  },
  {
    "path": "test/service-module/service-module.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { ServiceState } from './types'\nimport { assert } from 'chai'\nimport feathersVuex, { models } from '../../src/index'\nimport { clearModels } from '../../src/service-module/global-models'\n\nimport {\n  makeFeathersRestClient,\n  feathersRestClient as feathersClient,\n  feathersSocketioClient\n} from '../fixtures/feathers-client'\nimport { stripSlashes } from '../../src/utils'\nimport memory from 'feathers-memory'\nimport { makeTodos } from '../fixtures/todos'\nimport Vuex from 'vuex'\nimport { performance } from 'perf_hooks'\nimport enableServiceEvents from '../../src/service-module/service-module.events'\nimport { Service } from '@feathersjs/feathers'\n\ninterface Options {\n  idField: string\n}\ninterface TodoState extends ServiceState {\n  test: any\n  test2: {\n    test: boolean\n  }\n  isTrue: boolean\n}\ninterface RootState {\n  todos: TodoState\n  tasks: ServiceState\n  tests: ServiceState\n  blah: ServiceState\n  things: ServiceState\n}\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'service-module'\n  })\n\n  class ServiceTodo extends BaseModel {\n    public static modelName = 'ServiceTodo'\n    public id\n    public description: string\n\n    public constructor(data, options?) {\n      super(data, options)\n    }\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    public static instanceDefaults(data) {\n      return {\n        description: ''\n      }\n    }\n  }\n  class HotspotMedia extends BaseModel {\n    public static modelName = 'HotspotMedia'\n    public id\n    public description: string\n  }\n  class Media extends BaseModel {\n    public static modelName = 'Media'\n    public id\n    public description: string\n  }\n  class Person extends BaseModel {\n    public static modelName = 'Person'\n    public static test = true\n  }\n  class Item extends BaseModel {\n    public static modelName = 'Item'\n    public static test = true\n  }\n  class Task extends BaseModel {\n    public static modelName = 'Task'\n    public static test = true\n  }\n  class Car extends BaseModel {\n    public static modelName = 'Car'\n    public static test = true\n  }\n  class Group extends BaseModel {\n    public static modelName = 'Group'\n    public static test = true\n  }\n  class Test extends BaseModel {\n    public static modelName = 'Test'\n    public static test = true\n  }\n  class Thing extends BaseModel {\n    public static modelName = 'Thing'\n    public static test = true\n  }\n\n  return {\n    makeServicePlugin,\n    BaseModel,\n    ServiceTodo,\n    HotspotMedia,\n    Media,\n    Person,\n    Item,\n    Task,\n    Car,\n    Group,\n    Test,\n    Thing\n  }\n}\n\nfunction makeContextWithState() {\n  const feathers = makeFeathersRestClient()\n  const service = feathers.use('service-todos', memory({ store: makeTodos() }))\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathers, {\n    serverAlias: 'basics'\n  })\n  class ServiceTodo extends BaseModel {\n    public static modelName = 'ServiceTodo'\n\n    public static test = true\n  }\n\n  return {\n    feathers,\n    service,\n    makeServicePlugin,\n    BaseModel,\n    ServiceTodo\n  }\n}\n\nfunction makeAutoRemoveContext() {\n  const feathers = makeFeathersRestClient()\n    .use(\n      'todos',\n      memory({\n        store: makeTodos()\n      })\n    )\n    .use(\n      'tasks',\n      memory({\n        store: makeTodos(),\n        paginate: {\n          default: 10,\n          max: 50\n        }\n      })\n    )\n  const todosService = feathers.service('todos')\n  const tasksService = feathers.service('tasks')\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'autoRemove'\n  })\n  class Todo extends BaseModel {\n    public static modelName = 'Todo'\n    public static servicePath = 'todos'\n    public static test = true\n  }\n  class Task extends BaseModel {\n    public static modelName = 'Task'\n    public static servicePath = 'tasks'\n    public static test = true\n  }\n  return {\n    feathers,\n    todosService,\n    tasksService,\n    makeServicePlugin,\n    BaseModel,\n    Todo,\n    Task\n  }\n}\n\nfunction makeSocketIoContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(\n    feathersSocketioClient,\n    {\n      serverAlias: 'updates-store-on-events'\n    }\n  )\n\n  class Thing extends BaseModel {\n    public static modelName = 'Thing'\n    public static test = true\n    public constructor(data = {}, options?) {\n      super(data, options)\n    }\n  }\n\n  class ThingDebounced extends BaseModel {\n    public static modelName = 'ThingDebounced'\n    public static test = true\n    public constructor(data = {}, options?) {\n      super(data, options)\n    }\n  }\n\n  class TodoDebounced extends BaseModel {\n    public static modelName = 'TodoDebounced'\n    public static test = true\n    public constructor(data = {}, options?) {\n      super(data, options)\n    }\n  }\n\n  const store = new Vuex.Store<RootState>({\n    strict: true,\n    plugins: [\n      makeServicePlugin({\n        Model: Thing,\n        service: feathersSocketioClient.service('things'),\n        servicePath: 'things'\n      }),\n      makeServicePlugin({\n        Model: ThingDebounced,\n        service: feathersSocketioClient.service('things-debounced'),\n        servicePath: 'things-debounced',\n        debounceEventsTime: 20,\n        namespace: 'things-debounced'\n      }),\n      makeServicePlugin({\n        Model: TodoDebounced,\n        service: feathersSocketioClient.service('todos-debounced'),\n        servicePath: 'todos-debounced',\n        debounceEventsTime: 20,\n        namespace: 'todos-debounced'\n      })\n    ]\n  })\n\n  const debouncedQueue = enableServiceEvents({\n    Model: TodoDebounced,\n    service: feathersSocketioClient.service('todos-debounced'),\n    store,\n    options: store.state['todos-debounced']\n  })\n\n  return {\n    feathersSocketioClient,\n    makeServicePlugin,\n    BaseModel,\n    Thing,\n    ThingDebounced,\n    TodoDebounced,\n    store,\n    debouncedQueue\n  }\n}\n\ndescribe('Service Module', function () {\n  beforeEach(() => {\n    clearModels()\n  })\n\n  it('registers a vuex plugin and Model for the service', function () {\n    const { makeServicePlugin, ServiceTodo, BaseModel } = makeContext()\n    const serviceName = 'service-todos'\n    const feathersService = feathersClient.service(serviceName)\n    const store = new Vuex.Store<RootState>({\n      plugins: [\n        makeServicePlugin({\n          Model: ServiceTodo,\n          service: feathersClient.service(serviceName)\n        })\n      ]\n    })\n    assert(\n      models['service-module'].hasOwnProperty('ServiceTodo'),\n      'the Model was added to the models'\n    )\n    assert(\n      feathersService.FeathersVuexModel === ServiceTodo,\n      'the Model is also found at service.FeathersVuexModel'\n    )\n\n    const serviceTodo = new ServiceTodo({\n      description: 'Do the dishes',\n      isComplete: false\n    })\n    assert(serviceTodo instanceof ServiceTodo, 'Model can be instantiated.')\n    assert(serviceTodo instanceof BaseModel, 'Model can be instantiated.')\n\n    assert(store.state[serviceName])\n  })\n\n  describe('Models', function () {\n    beforeEach(function () {\n      const { makeServicePlugin, ServiceTodo } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            Model: ServiceTodo,\n            service: feathersClient.service('service-todos')\n          })\n        ]\n      })\n      assert(store)\n      assert(\n        models['service-module'].hasOwnProperty('ServiceTodo'),\n        'the Model was added to the models'\n      )\n      const owners = (this.owners = [\n        { id: 1, name: 'Marshall' },\n        { id: 2, name: 'Mariah' },\n        { id: 3, name: 'Leah' }\n      ])\n      const data = {\n        id: 1,\n        description: 'Do the dishes',\n        isComplete: false,\n        owners\n      }\n      store.commit('service-todos/addItem', data)\n\n      const serviceTodo = store.state['service-todos'].keyedById[1]\n\n      this.serviceTodo = serviceTodo\n      this.ServiceTodo = ServiceTodo\n    })\n\n    it('allows creating model clones', function () {\n      const { ServiceTodo } = this\n      const serviceTodoClone = this.serviceTodo.clone()\n\n      assert(\n        serviceTodoClone.__isClone,\n        'created a todo clone with isClone attribute'\n      )\n      assert(\n        serviceTodoClone instanceof ServiceTodo,\n        'the copy is an instance of the same class'\n      )\n    })\n\n    it('allows modifying clones without affecting the original', function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone()\n\n      serviceTodoClone.description = 'Do something else'\n\n      assert(\n        serviceTodo.description === 'Do the dishes',\n        'the original todo remained intact'\n      )\n    })\n\n    it('allows commiting changes back to the original in the store', function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone()\n\n      serviceTodoClone.description = 'Do something else'\n      serviceTodoClone.commit()\n\n      assert(\n        serviceTodo.description === 'Do something else',\n        'the original todo was updated'\n      )\n    })\n\n    it('performs a shallow merge when commiting back to the original record', function () {\n      const { serviceTodo, owners } = this\n      const serviceTodoClone = serviceTodo.clone()\n\n      serviceTodoClone.owners = [\n        { id: 1, name: 'Marshall' },\n        { id: 2, name: 'Mariah' }\n      ]\n      assert.deepEqual(\n        serviceTodo.owners,\n        owners,\n        'original todo remained unchanged'\n      )\n\n      serviceTodoClone.commit()\n\n      assert.deepEqual(\n        serviceTodo.owners,\n        [owners[0], owners[1]],\n        'ownerIds were updated properly'\n      )\n    })\n\n    it(`the object returned from clone is not the same as the original`, function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone()\n\n      assert(serviceTodo !== serviceTodoClone, 'the objects are distinct')\n    })\n\n    it(`the object returned from commit is not the same as the clone`, function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone()\n      const committedTodo = serviceTodoClone.commit()\n\n      assert(committedTodo !== serviceTodoClone, 'the objects are distinct')\n    })\n\n    it(`the object returned from commit is the same as the original`, function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone()\n      const committedTodo = serviceTodoClone.commit()\n\n      assert(serviceTodo === committedTodo, 'the objects are the same')\n    })\n\n    it(`nested arrays are distinct after clone`, function () {\n      const { ServiceTodo } = this\n\n      const todo = new ServiceTodo({\n        description: 'test',\n        owners: ['Marshall', 'Mariah']\n      })\n      const clone = todo.clone()\n\n      assert(\n        todo.owners !== clone.owners,\n        'the arrays are not the same in memory'\n      )\n    })\n\n    it.skip(`modifying a clone after calling commit() does not change the original `, function () {\n      const { serviceTodo, owners } = this\n      const serviceTodoClone = serviceTodo.clone()\n\n      assert.deepEqual(\n        serviceTodo.owners,\n        owners,\n        'original todo remained unchanged'\n      )\n\n      serviceTodoClone.commit()\n      serviceTodoClone.owners[0].name = 'Ted'\n\n      assert.deepEqual(\n        serviceTodo.owners[0].name,\n        'Marshall',\n        'nested object in original todo was unchanged'\n      )\n    })\n\n    it(`changes the original if you modify return value of a commit`, function () {\n      const { serviceTodo, owners } = this\n      let serviceTodoClone = serviceTodo.clone()\n\n      assert.deepEqual(\n        serviceTodo.owners,\n        owners,\n        'original todo remained unchanged'\n      )\n\n      serviceTodoClone = serviceTodoClone.commit()\n      serviceTodoClone.owners[0].name = 'Ted'\n\n      assert.deepEqual(\n        serviceTodo.owners[0].name,\n        'Ted',\n        'nested object in original todo was changed'\n      )\n    })\n\n    it(`allows shallow assign of data when cloning`, function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone({\n        isComplete: !serviceTodo.isComplete\n      })\n\n      assert.equal(\n        !serviceTodo.isComplete,\n        serviceTodoClone.isComplete,\n        'clone value has changed'\n      )\n\n      serviceTodoClone.commit()\n\n      assert.equal(\n        serviceTodo.isComplete,\n        true,\n        'value has changed after commit'\n      )\n    })\n\n    it('allows reseting copy changes back to match the original', function () {\n      const { serviceTodo } = this\n      const serviceTodoClone = serviceTodo.clone()\n\n      serviceTodoClone.description = 'Do something else'\n      serviceTodoClone.reset()\n\n      assert(\n        serviceTodo.description === 'Do the dishes',\n        'the original todo was untouched'\n      )\n      assert(\n        serviceTodoClone.description === 'Do the dishes',\n        'the clone was reset to match the original'\n      )\n    })\n\n    it('adds additional properties to model instances when more data arrives for the same id', function () {\n      const { serviceTodo, owners } = this\n      const newData = {\n        id: 1,\n        description: 'Do the dishes',\n        isComplete: false,\n        owners,\n        test: true\n      }\n      const newTodo = new serviceTodo.constructor(newData)\n\n      assert(newTodo === serviceTodo, 'the records are the same')\n      assert(newTodo.test === true, 'the new attribute was added')\n      assert(\n        serviceTodo.test === true,\n        'the new attribute was also added to the original'\n      )\n    })\n\n    it('ignores when new data with matching id has fewer props than current record', function () {\n      const { serviceTodo, owners } = this\n      const newData = {\n        id: 1,\n        owners\n      }\n      const newTodo = new serviceTodo.constructor(newData)\n\n      assert(newTodo === serviceTodo, 'the records are the same')\n      assert(\n        serviceTodo.description === 'Do the dishes',\n        'the existing attributes remained in place'\n      )\n      assert(\n        serviceTodo.isComplete === false,\n        'the existing attributes remained in place'\n      )\n    })\n\n    it('updates the new record when non-null, non-undefined values do not match', function () {\n      const { serviceTodo, owners } = this\n      const newData = {\n        id: 1,\n        description: 'Do the mopping',\n        isComplete: true,\n        owners\n      }\n      const newTodo = new serviceTodo.constructor(newData)\n\n      assert(newTodo === serviceTodo, 'the records are the same')\n      assert(\n        serviceTodo.description === 'Do the mopping',\n        'non-matching string was updated'\n      )\n      assert(\n        serviceTodo.isComplete === true,\n        'non-matching boolean was updated'\n      )\n    })\n  })\n\n  describe('Setting Up', () => {\n    it('service stores have global defaults', function () {\n      const { makeServicePlugin, BaseModel, Task } = makeContext()\n      class Todo extends BaseModel {\n        public static modelName = 'Todo'\n        public static test = true\n      }\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            Model: Task,\n            service: feathersClient.service('tasks')\n          }),\n          makeServicePlugin({\n            Model: Todo,\n            service: feathersClient.service('/v2/todos')\n          })\n        ]\n      })\n      const { state } = store\n\n      assert(state.tasks.idField === 'id', 'default idField is `id`')\n      assert(state.tasks.autoRemove === false, 'autoRemove is off by default')\n      assert(state.todos, 'uses `short` nameStyle by default')\n    })\n\n    it('can customize the idField for each service', function () {\n      const { makeServicePlugin, Test, Person } = makeContext()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            idField: '_id',\n            Model: Test,\n            service: feathersClient.service('tests')\n          }),\n          makeServicePlugin({\n            idField: 'name',\n            Model: Person,\n            service: feathersClient.service('people')\n          })\n        ]\n      })\n\n      assert(\n        store.state.tests.idField === '_id',\n        'the idField was properly set'\n      )\n      assert(\n        // @ts-ignore\n        store.state.people.idField === 'name',\n        'the idField was properly set'\n      )\n    })\n\n    it('allows enabling autoRemove', function () {\n      const { makeServicePlugin, Test } = makeContext()\n      const autoRemove = true\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            Model: Test,\n            service: feathersClient.service('tests'),\n            autoRemove\n          })\n        ]\n      })\n\n      assert(\n        store.state.tests.autoRemove === autoRemove,\n        'the autoRemove was enabled'\n      )\n    })\n\n    it('can switch to path name as namespace', function () {\n      const { makeServicePlugin, Test } = makeContext()\n      const plugin = makeServicePlugin({\n        Model: Test,\n        service: feathersClient.service('/v1/tests'),\n        nameStyle: 'path'\n      })\n      const store = new Vuex.Store<RootState>({\n        plugins: [plugin]\n      })\n      const namespace = stripSlashes('/v1/tests')\n\n      assert(\n        store.state[namespace],\n        'the full path name was used as a namespace'\n      )\n    })\n\n    it('can explicitly provide a namespace', function () {\n      const { makeServicePlugin, Test } = makeContext()\n      const namespace = 'blah'\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            Model: Test,\n            service: feathersClient.service('/v1/tests'),\n            namespace\n          })\n        ]\n      })\n      assert(store.state.blah, 'the namespace option was used as the namespace')\n    })\n\n    it('prioritizes the explicit namespace', function () {\n      const { makeServicePlugin, Test } = makeContext()\n      const namespace = 'blah'\n      const nameStyle = 'path'\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            Model: Test,\n            service: feathersClient.service('/v1/tests'),\n            namespace,\n            nameStyle\n          })\n        ]\n      })\n      assert(store.state.blah, 'the namespace option was used as the namespace')\n    })\n  })\n\n  describe('Basics', () => {\n    it('populates default store', function () {\n      const {\n        makeServicePlugin,\n        feathers,\n        ServiceTodo\n      } = makeContextWithState()\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            servicePath: 'service-todos',\n            Model: ServiceTodo,\n            service: feathers.service('service-todos')\n          })\n        ]\n      })\n      const todoState = store.state['service-todos']\n      const expectedState = {\n        addOnUpsert: false,\n        autoRemove: false,\n        copiesById: {},\n        debug: false,\n        enableEvents: true,\n        errorOnCreate: null,\n        errorOnGet: null,\n        errorOnPatch: null,\n        errorOnRemove: null,\n        errorOnUpdate: null,\n        errorOnFind: null,\n        idField: 'id',\n        ids: [],\n        isFindPending: false,\n        isGetPending: false,\n        isCreatePending: false,\n        isUpdatePending: false,\n        isPatchPending: false,\n        isRemovePending: false,\n        keepCopiesInStore: false,\n        debounceEventsTime: null,\n        debounceEventsMaxWait: 1000,\n        keyedById: {},\n        nameStyle: 'short',\n        namespace: 'service-todos',\n        modelName: 'ServiceTodo',\n        serverAlias: 'basics',\n        skipRequestIfExists: false,\n        preferUpdate: false,\n        replaceItems: false,\n        servicePath: 'service-todos',\n        tempIdField: '__id',\n        tempsById: {},\n        pagination: {\n          defaultLimit: null,\n          defaultSkip: null\n        },\n        paramsForServer: ['$populateParams'],\n        whitelist: [],\n        isIdCreatePending: [],\n        isIdUpdatePending: [],\n        isIdPatchPending: [],\n        isIdRemovePending: []\n      }\n\n      assert.deepEqual(\n        todoState,\n        expectedState,\n        'the expected state was returned'\n      )\n    })\n\n    it('throws an error if no service is provided', function () {\n      const { makeServicePlugin } = makeContext()\n      try {\n        new Vuex.Store({\n          // @ts-ignore\n          plugins: [makeServicePlugin({})]\n        })\n      } catch (error) {\n        assert.equal(\n          error.message,\n          'No service was provided. If you passed one in, check that you have configured a transport plugin on the Feathers Client. Make sure you use the client version of the transport.',\n          'threw an error'\n        )\n      }\n    })\n\n    describe('Auto-Remove Items', function () {\n      beforeEach(function () {\n        clearModels()\n      })\n\n      it(`removes missing items when pagination is off`, function (done) {\n        const {\n          makeServicePlugin,\n          Todo,\n          todosService\n        } = makeAutoRemoveContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: Todo,\n              service: todosService,\n              idField: '_id',\n              autoRemove: true\n            })\n          ]\n        })\n\n        const todoState = store.state['todos']\n\n        assert(todoState.ids.length === 0)\n\n        // Load some data into the store\n        store\n          .dispatch('todos/find', { query: {} })\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          .then(todos => {\n            // Remove the third item from the service\n            // @ts-ignore\n            delete todosService.store[3]\n            // We went around using the store actions, so there will still be three items.\n            assert(\n              todoState.ids.length === 3,\n              'there are still three items in the store'\n            )\n\n            // Perform the same query again\n            return store.dispatch('todos/find', { query: {} })\n          })\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          .then(todos => {\n            assert(\n              todoState.ids.length === 2,\n              'there are now two items in the store'\n            )\n            done()\n          })\n          .catch(error => {\n            assert(!error, error.message)\n            done()\n          })\n      })\n\n      it(`does not remove missing items when pagination is on`, function (done) {\n        const {\n          makeServicePlugin,\n          Task,\n          tasksService\n        } = makeAutoRemoveContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: Task,\n              service: tasksService,\n              idField: '_id',\n              autoRemove: true\n            })\n          ]\n        })\n\n        const taskState = store.state.tasks\n\n        assert(taskState.ids.length === 0)\n\n        // Load some data into the store\n        store\n          .dispatch('tasks/find', { query: {} })\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          .then(todos => {\n            // Remove the third item from the service\n            // @ts-ignore\n            delete tasksService.store[3]\n            // We went around using the store actions, so there will still be three items.\n            assert(\n              taskState.ids.length === 3,\n              'there are still three items in the store'\n            )\n\n            // Perform the same query again\n            return store.dispatch('tasks/find', { query: {} })\n          })\n          .then(todos => {\n            assert(todos.hasOwnProperty('total'), 'pagination is on')\n            assert(\n              taskState.ids.length === 3,\n              'there are still three items in the store'\n            )\n            done()\n          })\n          .catch(error => {\n            assert(!error, error.message)\n            done()\n          })\n      })\n\n      it(`does not remove missing items when autoRemove is off`, function (done) {\n        const {\n          makeServicePlugin,\n          Todo,\n          todosService\n        } = makeAutoRemoveContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: Todo,\n              service: todosService,\n              idField: '_id'\n            })\n          ]\n        })\n\n        const todoState = store.state.todos\n\n        assert(todoState.ids.length === 0)\n\n        // Load some data into the store\n        store\n          .dispatch('todos/find', { query: {} })\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          .then(todos => {\n            // Remove the third item from the service\n            // @ts-ignore\n            delete todosService.store[3]\n            // We went around using the store actions, so there will still be three items.\n            assert(\n              todoState.ids.length === 3,\n              'there are still three items in the store'\n            )\n\n            // Perform the same query again\n            return store.dispatch('todos/find', { query: {} })\n          })\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          .then(todos => {\n            assert(\n              todoState.ids.length === 3,\n              'there are still three items in the store'\n            )\n            done()\n          })\n          .catch(error => {\n            assert(!error, error.message)\n            done()\n          })\n      })\n    })\n  })\n\n  describe('Customizing Service Stores', function () {\n    describe('New \"extend\" method', () => {\n      it('allows access to the store and default module', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n\n        new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              extend: ({ store, module }) => {\n                assert.ok(store, 'should have received received the store')\n                assert.ok(module.state, 'should have default state')\n                assert.ok(module.getters, 'should have default getters')\n                assert.ok(module.mutations, 'should have default mutations')\n                assert.ok(module.actions, 'should have default actions')\n                assert.ok(module.namespaced, 'should have default namespaced')\n                return {}\n              }\n            })\n          ]\n        })\n      })\n\n      it('allows adding custom state', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n\n        const customState = {\n          test: true,\n          test2: {\n            test: true\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              extend: () => {\n                return {\n                  state: customState\n                }\n              }\n            })\n          ]\n        })\n\n        assert(store.state['service-todos'].test === true, 'added custom state')\n        assert(\n          store.state['service-todos'].test2.test === true,\n          'added custom state'\n        )\n      })\n\n      it('allows custom mutations', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n        const state = { test: true }\n        const customMutations = {\n          setTestToFalse(state) {\n            state.test = false\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              extend: () => ({\n                state,\n                mutations: customMutations\n              })\n            })\n          ]\n        })\n\n        store.commit('service-todos/setTestToFalse')\n        assert(\n          store.state['service-todos'].test === false,\n          'the custom state was modified by the custom mutation'\n        )\n      })\n\n      it('allows custom getters', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n        const customGetters = {\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          oneTwoThree(state) {\n            return 123\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              extend: () => ({\n                getters: customGetters\n              })\n            })\n          ]\n        })\n\n        assert(\n          store.getters['service-todos/oneTwoThree'] === 123,\n          'the custom getter was available'\n        )\n      })\n\n      it('allows adding custom actions', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              extend: () => ({\n                state: {\n                  isTrue: false\n                },\n                mutations: {\n                  setToTrue(state) {\n                    state.isTrue = true\n                  }\n                },\n                actions: {\n                  trigger(context) {\n                    context.commit('setToTrue')\n                  }\n                }\n              })\n            })\n          ]\n        })\n\n        store.dispatch('service-todos/trigger')\n        assert(\n          store.state['service-todos'].isTrue === true,\n          'the custom action was run'\n        )\n      })\n    })\n\n    describe('Deprecated options', () => {\n      it('allows adding custom state', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n\n        const customState = {\n          test: true,\n          test2: {\n            test: true\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              state: customState\n            })\n          ]\n        })\n\n        assert(store.state['service-todos'].test === true, 'added custom state')\n        assert(\n          store.state['service-todos'].test2.test === true,\n          'added custom state'\n        )\n      })\n\n      it('allows custom mutations', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n        const state = { test: true }\n        const customMutations = {\n          setTestToFalse(state) {\n            state.test = false\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              state,\n              mutations: customMutations\n            })\n          ]\n        })\n\n        store.commit('service-todos/setTestToFalse')\n        assert(\n          store.state['service-todos'].test === false,\n          'the custom state was modified by the custom mutation'\n        )\n      })\n\n      it('allows custom getters', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n        const customGetters = {\n          // eslint-disable-next-line @typescript-eslint/no-unused-vars\n          oneTwoThree(state) {\n            return 123\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              getters: customGetters\n            })\n          ]\n        })\n\n        assert(\n          store.getters['service-todos/oneTwoThree'] === 123,\n          'the custom getter was available'\n        )\n      })\n\n      it('allows adding custom actions', function () {\n        const { makeServicePlugin, ServiceTodo } = makeContext()\n        const config = {\n          state: {\n            isTrue: false\n          },\n          mutations: {\n            setToTrue(state) {\n              state.isTrue = true\n            }\n          },\n          actions: {\n            trigger(context) {\n              context.commit('setToTrue')\n            }\n          }\n        }\n        const store = new Vuex.Store<RootState>({\n          plugins: [\n            makeServicePlugin({\n              Model: ServiceTodo,\n              service: feathersClient.service('service-todos'),\n              ...config\n            })\n          ]\n        })\n\n        store.dispatch('service-todos/trigger')\n        assert(\n          store.state['service-todos'].isTrue === true,\n          'the custom action was run'\n        )\n      })\n    })\n  })\n\n  describe('Updates the Store on Events', function () {\n    let feathersSocketioClient,\n      debouncedQueue,\n      store,\n      debouncedService: Service<any>\n    beforeEach(() => {\n      const context = makeSocketIoContext()\n      feathersSocketioClient = context.feathersSocketioClient\n      debouncedQueue = context.debouncedQueue\n      store = context.store\n      debouncedService = feathersSocketioClient.service('things-debounced')\n    })\n\n    it('created', function (done) {\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const listener = item => {\n        assert(\n          // @ts-ignore\n          store.state.things.keyedById[0].test,\n          'the item received from the socket event was added to the store'\n        )\n\n        feathersSocketioClient.service('things').off('created', listener)\n\n        done()\n      }\n      feathersSocketioClient.service('things').on('created', listener)\n\n      feathersSocketioClient.service('things').create({ test: true })\n    })\n\n    it('created debounced', function (done) {\n      const { debounceEventsTime } = store.state['things-debounced']\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const listener = item => {\n        assert(\n          !store.state['things-debounced'].keyedById[0],\n          'the item is not added immediately'\n        )\n        setTimeout(() => {\n          assert(\n            store.state['things-debounced'].keyedById[0].test,\n            'the item received from the socket event was added to the store'\n          )\n          debouncedService.off('created', listener)\n          done()\n        }, debounceEventsTime * 2)\n      }\n      debouncedService.on('created', listener)\n      debouncedService.create({ test: true })\n    })\n\n    it('patched', function (done) {\n      store.commit('things/addItem', { id: 1, test: false })\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      feathersSocketioClient.service('things').on('patched', item => {\n        assert(\n          store.state.things.keyedById[1].test,\n          'the item received from the socket event was updated in the store'\n        )\n        done()\n      })\n\n      feathersSocketioClient.service('things').patch(1, { test: true })\n    })\n\n    it('patched debounced', function (done) {\n      const { debounceEventsTime } = store.state['things-debounced']\n\n      store.commit('things-debounced/clearAll')\n      store.commit('things-debounced/addItem', { id: 1, test: false })\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const listener = item => {\n        assert(\n          !store.state['things-debounced'].keyedById[1].test,\n          'the item is not updated immediately'\n        )\n        setTimeout(() => {\n          assert(\n            store.state['things-debounced'].keyedById[1].test,\n            'the item received from the socket event was updated in the store'\n          )\n        }, debounceEventsTime * 2)\n        debouncedService.off('patched', listener)\n        done()\n      }\n\n      debouncedService.on('patched', listener)\n      debouncedService.patch(1, { test: true })\n    })\n\n    it('updated', function (done) {\n      store.commit('things/addItem', { id: 1, test: false })\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      feathersSocketioClient.service('things').on('updated', item => {\n        assert(\n          store.state.things.keyedById[1].test,\n          'the item received from the socket event was updated in the store'\n        )\n        done()\n      })\n\n      feathersSocketioClient.service('things').update(1, { test: true })\n    })\n\n    it('updated debounced', function (done) {\n      const { debounceEventsTime } = store.state['things-debounced']\n\n      store.commit('things-debounced/clearAll')\n      store.commit('things-debounced/addItem', { id: 1, test: false })\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const listener = item => {\n        assert(\n          !store.state['things-debounced'].keyedById[1].test,\n          'the item is not updated immediately'\n        )\n        setTimeout(() => {\n          assert(\n            store.state['things-debounced'].keyedById[1].test,\n            'the item received from the socket event was updated in the store'\n          )\n          done()\n        }, debounceEventsTime * 2)\n        debouncedService.off('updated', listener)\n      }\n\n      debouncedService.on('updated', listener)\n      debouncedService.update(1, { test: true })\n    })\n\n    it('removed', function (done) {\n      store.commit('things/addItem', { id: 1, test: false })\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      feathersSocketioClient.service('things').on('removed', item => {\n        assert(\n          !store.state.things.keyedById[1],\n          'the item received from the socket event was removed from the store'\n        )\n        done()\n      })\n\n      feathersSocketioClient.service('things').remove(1)\n    })\n\n    it('removed debounced', function (done) {\n      const { debounceEventsTime } = store.state['things-debounced']\n\n      store.commit('things-debounced/clearAll')\n      store.commit('things-debounced/addItem', { id: 1, test: false })\n\n      // eslint-disable-next-line @typescript-eslint/no-unused-vars\n      const listener = item => {\n        assert(\n          store.state['things-debounced'].keyedById[1],\n          'the item is not removed immediately'\n        )\n\n        setTimeout(() => {\n          assert(\n            !store.state.things.keyedById[1],\n            'the item received from the socket event was removed from the store'\n          )\n          done()\n        }, debounceEventsTime * 2)\n        debouncedService.off('removed', listener)\n      }\n\n      debouncedService.on('removed', listener)\n      debouncedService.remove(1)\n    })\n\n    it('debounce works with plenty items', function (done) {\n      store.commit('things-debounced/clearAll')\n\n      const { debounceEventsTime, debounceEventsMaxWait } = store.state[\n        'things-debounced'\n      ]\n\n      const itemsCount = 100\n      let i = 0\n\n      assert(\n        Object.keys(store.state['things-debounced'].keyedById).length === 0,\n        'no items at start'\n      )\n\n      const now = performance.now()\n\n      const setTimeoutCreate = () => {\n        setTimeout(() => {\n          debouncedService.create({ test: true, i })\n          i++\n          if (i < itemsCount) {\n            if (performance.now() - now < debounceEventsMaxWait) {\n              assert(\n                Object.keys(store.state['things-debounced'].keyedById)\n                  .length === 0,\n                `no items at i: ${i}`\n              )\n            }\n            setTimeoutCreate()\n          } else {\n            setTimeout(() => {\n              assert(\n                Object.keys(\n                  store.state['things-debounced'].keyedById.length ===\n                    itemsCount\n                ),\n                'all items are in store'\n              )\n              done()\n            }, debounceEventsTime * 2)\n          }\n        }, debounceEventsTime / 4)\n      }\n      setTimeoutCreate()\n    })\n\n    it('debounced events get invoked during continuous events', function (done) {\n      store.commit('things-debounced/clearAll')\n\n      const { debounceEventsTime, debounceEventsMaxWait } = store.state[\n        'things-debounced'\n      ]\n\n      assert(\n        Object.keys(store.state['things-debounced'].keyedById).length === 0,\n        'no items at start'\n      )\n      assert(debounceEventsMaxWait > 0, 'maxWait is set')\n\n      const startedAt = performance.now()\n      let i = 0\n\n      const setTimeoutCreate = () => {\n        setTimeout(() => {\n          debouncedService.create({ test: true, i })\n          i++\n          const timePassed = Math.floor(\n            performance.now() - startedAt - debounceEventsTime\n          )\n          if (timePassed <= debounceEventsMaxWait) {\n            if (performance.now() - startedAt <= debounceEventsMaxWait) {\n              assert(\n                Object.keys(store.state['things-debounced'].keyedById)\n                  .length === 0,\n                `no items at i: ${i}, milliseconds passed: ${timePassed}`\n              )\n            }\n            setTimeoutCreate()\n          } else {\n            assert(\n              Object.keys(store.state['things-debounced'].keyedById).length ===\n                i - 1,\n              `items are inserted after maxWait`\n            )\n            done()\n          }\n        }, debounceEventsTime / 4)\n      }\n      setTimeoutCreate()\n    })\n\n    it('debounded remove after addOrUpdate also removes addOrUpdate queue and vise versa', function () {\n      const { idField } = store.state['todos-debounced']\n\n      assert(\n        Object.keys(debouncedQueue.addOrUpdateById).length === 0,\n        \"'addOrUpdateById' initially empty\"\n      )\n\n      assert(\n        Object.keys(debouncedQueue.removeItemById).length === 0,\n        \"'removeItemById' initially empty\"\n      )\n\n      debouncedQueue.enqueueAddOrUpdate({ [idField]: 1, test: true })\n\n      assert(\n        debouncedQueue.addOrUpdateById[1],\n        \"queued item for 'addOrUpdate' correctly\"\n      )\n\n      debouncedQueue.enqueueRemoval({ [idField]: 1, test: false })\n\n      assert(\n        !debouncedQueue.addOrUpdateById[1],\n        \"queued item for 'addOrUpdate' removed immediately\"\n      )\n      assert(\n        debouncedQueue.removeItemById[1],\n        'queued item for removal correctly'\n      )\n\n      debouncedQueue.enqueueAddOrUpdate({ [idField]: 1, test: true })\n\n      assert(\n        debouncedQueue.addOrUpdateById[1],\n        \"queued item for 'addOrUpdate' correctly again\"\n      )\n      assert(\n        !debouncedQueue.removeItemById[1],\n        \"queued item for 'remove' removed immediately\"\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "test/service-module/types.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nexport interface ServiceState {\n  options: {}\n  ids: (string | number)[]\n  autoRemove: boolean\n  errorOnFind: any\n  errorOnGet: any\n  errorOnCreate: any\n  errorOnPatch: any\n  errorOnUpdate: any\n  errorOnRemove: any\n  isFindPending: boolean\n  isGetPending: boolean\n  isCreatePending: boolean\n  isPatchPending: boolean\n  isUpdatePending: boolean\n  isRemovePending: boolean\n  idField: string\n  keyedById: {}\n  tempsById: {}\n  tempsByNewId: {}\n  whitelist: string[]\n  paramsForServer: string[]\n  namespace: string\n  nameStyle: string // Should be enum of 'short' or 'path'\n  pagination?: {\n    default: PaginationState\n  }\n  modelName: string\n}\n\nexport interface PaginationState {\n  ids: any\n  limit: number\n  skip: number\n  ip: number\n  total: number\n  mostRecent: any\n}\n\nexport interface Location {\n  coordinates: number[]\n}\n"
  },
  {
    "path": "test/test-utils.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\n\nexport function assertGetter(item, prop, value) {\n  assert(\n    typeof Object.getOwnPropertyDescriptor(item, prop).get === 'function',\n    'getter in place'\n  )\n  assert.equal(item[prop], value, 'returned value matches')\n}\n\nexport const makeStore = () => {\n  return {\n    0: { id: 0, description: 'Do the first', isComplete: false },\n    1: { id: 1, description: 'Do the second', isComplete: false },\n    2: { id: 2, description: 'Do the third', isComplete: false },\n    3: { id: 3, description: 'Do the fourth', isComplete: false },\n    4: { id: 4, description: 'Do the fifth', isComplete: false },\n    5: { id: 5, description: 'Do the sixth', isComplete: false },\n    6: { id: 6, description: 'Do the seventh', isComplete: false },\n    7: { id: 7, description: 'Do the eighth', isComplete: false },\n    8: { id: 8, description: 'Do the ninth', isComplete: false },\n    9: { id: 9, description: 'Do the tenth', isComplete: false }\n  }\n}\n\nexport const makeStoreWithAtypicalIds = () => {\n  return {\n    0: { someId: 0, description: 'Do the first', isComplete: false },\n    1: { someId: 1, description: 'Do the second', isComplete: false },\n    2: { someId: 2, description: 'Do the third', isComplete: false },\n    3: { someId: 3, description: 'Do the fourth', isComplete: false },\n    4: { someId: 4, description: 'Do the fifth', isComplete: false },\n    5: { someId: 5, description: 'Do the sixth', isComplete: false },\n    6: { someId: 6, description: 'Do the seventh', isComplete: false },\n    7: { someId: 7, description: 'Do the eighth', isComplete: false },\n    8: { someId: 8, description: 'Do the ninth', isComplete: false },\n    9: { someId: 9, description: 'Do the tenth', isComplete: false }\n  }\n}\n"
  },
  {
    "path": "test/use/InstrumentComponent.js",
    "content": "import useGet from '../../src/useGet'\n\nexport default {\n  name: 'InstrumentComponent',\n  template: '<div id=\"test\"> {{ instrument }} </div>',\n  props: {\n    id: {\n      type: String,\n      default: ''\n    }\n  },\n  setup(props, context) {\n    const { Instrument } = context.root.$FeathersVuex\n\n    const instrumentData = useGet({ model: Instrument, id: props.id })\n\n    return {\n      instrument: instrumentData.item\n    }\n  }\n}\n"
  },
  {
    "path": "test/use/find.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0,\n@typescript-eslint/no-empty-function: 0\n*/\nimport Vue from 'vue'\nimport VueCompositionApi from '@vue/composition-api'\nVue.use(VueCompositionApi)\n\nimport jsdom from 'jsdom-global'\nimport { assert } from 'chai'\nimport feathersVuex, { FeathersVuex } from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport useFind from '../../src/useFind'\nimport Vuex from 'vuex'\n// import { shallowMount } from '@vue/test-utils'\nimport { computed, isRef } from '@vue/composition-api'\njsdom()\nrequire('events').EventEmitter.prototype._maxListeners = 100\n\nVue.use(Vuex)\nVue.use(FeathersVuex)\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'useFind'\n  })\n\n  class Instrument extends BaseModel {\n    public static modelName = 'Instrument'\n  }\n\n  const serviceName = 'things'\n  const store = new Vuex.Store({\n    plugins: [\n      makeServicePlugin({\n        Model: Instrument,\n        service: feathersClient.service(serviceName)\n      })\n    ]\n  })\n  return { store, Instrument, BaseModel, makeServicePlugin }\n}\n\ndescribe('use/find', function () {\n  it('returns correct default data', function () {\n    const { Instrument } = makeContext()\n\n    const instrumentParams = computed(() => {\n      return {\n        query: {},\n        paginate: false\n      }\n    })\n    const instrumentsData = useFind({\n      model: Instrument,\n      params: instrumentParams\n    })\n\n    const {\n      debounceTime,\n      error,\n      haveBeenRequested,\n      haveLoaded,\n      isPending,\n      isLocal,\n      items,\n      latestQuery,\n      paginationData,\n      qid\n    } = instrumentsData\n\n    assert(isRef(debounceTime))\n    assert(debounceTime.value === null)\n\n    assert(isRef(error))\n    assert(error.value === null)\n\n    assert(isRef(haveBeenRequested))\n    assert(haveBeenRequested.value === true)\n\n    assert(isRef(haveLoaded))\n    assert(haveLoaded.value === false)\n\n    assert(isRef(isPending))\n    assert(isPending.value === true)\n\n    assert(isRef(isLocal))\n    assert(isLocal.value === false)\n\n    assert(isRef(items))\n    assert(Array.isArray(items.value))\n    assert(items.value.length === 0)\n\n    assert(isRef(latestQuery))\n    assert(latestQuery.value === null)\n\n    assert(isRef(paginationData))\n    assert.deepStrictEqual(paginationData.value, {\n      defaultLimit: null,\n      defaultSkip: null\n    })\n\n    assert(isRef(qid))\n    assert(qid.value === 'default')\n  })\n\n  it.skip('returns correct default data even when params is not reactive', function () {\n    const { Instrument } = makeContext()\n\n    const instrumentsData = useFind({\n      model: Instrument,\n      params: {\n        query: {},\n        paginate: false\n      }\n    })\n\n    const {\n      debounceTime,\n      error,\n      haveBeenRequested,\n      haveLoaded,\n      isPending,\n      isLocal,\n      items,\n      latestQuery,\n      paginationData,\n      qid\n    } = instrumentsData\n\n    assert(isRef(debounceTime))\n    assert(debounceTime.value === null)\n\n    assert(isRef(error))\n    assert(error.value === null)\n\n    assert(isRef(haveBeenRequested))\n    assert(haveBeenRequested.value === true)\n\n    assert(isRef(haveLoaded))\n    assert(haveLoaded.value === false)\n\n    assert(isRef(isPending))\n    assert(isPending.value === true)\n\n    assert(isRef(isLocal))\n    assert(isLocal.value === false)\n\n    assert(isRef(items))\n    assert(Array.isArray(items.value))\n    assert(items.value.length === 0)\n\n    assert(isRef(latestQuery))\n    assert(latestQuery.value === null)\n\n    assert(isRef(paginationData))\n    assert.deepStrictEqual(paginationData.value, {\n      defaultLimit: null,\n      defaultSkip: null\n    })\n\n    assert(isRef(qid))\n    assert(qid.value === 'default')\n  })\n\n  it('allows passing {immediate:false} to not query immediately', function () {\n    const { Instrument } = makeContext()\n\n    const instrumentParams = computed(() => {\n      return {\n        query: {},\n        paginate: false\n      }\n    })\n    const instrumentsData = useFind({\n      model: Instrument,\n      params: instrumentParams,\n      immediate: false\n    })\n    const { haveBeenRequested } = instrumentsData\n\n    assert(isRef(haveBeenRequested))\n    assert(haveBeenRequested.value === false)\n  })\n\n  it('params can return null to prevent the query', function () {\n    const { Instrument } = makeContext()\n\n    const instrumentParams = computed(() => {\n      return null\n    })\n    const instrumentsData = useFind({\n      model: Instrument,\n      params: instrumentParams,\n      immediate: true\n    })\n    const { haveBeenRequested } = instrumentsData\n\n    assert(isRef(haveBeenRequested))\n    assert(haveBeenRequested.value === false)\n  })\n\n  it('allows using `local: true` to prevent API calls from being made', function () {\n    const { Instrument } = makeContext()\n\n    const instrumentParams = computed(() => {\n      return {\n        query: {}\n      }\n    })\n    const instrumentsData = useFind({\n      model: Instrument,\n      params: instrumentParams,\n      local: true\n    })\n    const { haveBeenRequested, find } = instrumentsData\n\n    assert(isRef(haveBeenRequested))\n    assert(haveBeenRequested.value === false, 'no request during init')\n\n    find()\n\n    assert(haveBeenRequested.value === false, 'no request after find')\n  })\n})\n"
  },
  {
    "path": "test/use/get.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0,\n@typescript-eslint/no-empty-function: 0\n*/\nimport Vue from 'vue'\nimport VueCompositionApi from '@vue/composition-api'\nVue.use(VueCompositionApi)\n\nimport jsdom from 'jsdom-global'\nimport { assert } from 'chai'\nimport feathersVuex, { FeathersVuex } from '../../src/index'\nimport { feathersRestClient as feathersClient } from '../fixtures/feathers-client'\nimport useGet from '../../src/useGet'\nimport memory from 'feathers-memory'\nimport Vuex from 'vuex'\n// import { mount, shallowMount } from '@vue/test-utils'\n// import InstrumentComponent from './InstrumentComponent'\nimport { isRef } from '@vue/composition-api'\nimport { HookContext } from '@feathersjs/feathers'\njsdom()\nrequire('events').EventEmitter.prototype._maxListeners = 100\n\nVue.use(Vuex)\nVue.use(FeathersVuex)\n\n// function timeoutPromise(wait = 0) {\n//   return new Promise(resolve => {\n//     setTimeout(() => {\n//       resolve()\n//     }, wait)\n//   })\n// }\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'useGet'\n  })\n\n  class Instrument extends BaseModel {\n    public constructor(data, options?) {\n      super(data, options)\n    }\n    public static modelName = 'Instrument'\n    public static instanceDefaults(data) {\n      return {\n        name: ''\n      }\n    }\n  }\n\n  feathersClient.use(\n    'things',\n    memory({\n      store: {\n        0: { id: 0, name: 'trumpet' },\n        1: { id: 1, name: 'trombone' }\n      },\n      paginate: {\n        default: 10,\n        max: 50\n      }\n    })\n  )\n\n  const servicePath = 'instruments'\n  const store = new Vuex.Store({\n    plugins: [\n      makeServicePlugin({\n        Model: Instrument,\n        servicePath,\n        service: feathersClient.service(servicePath)\n      })\n    ]\n  })\n  return { store, Instrument, BaseModel, makeServicePlugin }\n}\n\ndescribe('use/get', function () {\n  it('returns correct default data', function () {\n    const { Instrument } = makeContext()\n\n    const id = 1\n\n    const existing = Instrument.getFromStore(id)\n    assert(!existing, 'the current instrument is not in the store.')\n\n    const instrumentData = useGet({ model: Instrument, id })\n\n    const {\n      error,\n      hasBeenRequested,\n      hasLoaded,\n      isPending,\n      isLocal,\n      item\n    } = instrumentData\n\n    assert(isRef(error))\n    assert(error.value === null)\n\n    assert(isRef(hasBeenRequested))\n    assert(hasBeenRequested.value === true)\n\n    assert(isRef(hasLoaded))\n    assert(hasLoaded.value === false)\n\n    assert(isRef(isPending))\n    assert(isPending.value === true)\n\n    assert(isRef(isLocal))\n    assert(isLocal.value === false)\n\n    assert(isRef(item))\n    assert(item.value === null)\n  })\n\n  it('allows passing {immediate:false} to not query immediately', function () {\n    const { Instrument } = makeContext()\n\n    const id = 1\n    const instrumentData = useGet({ model: Instrument, id, immediate: false })\n    const { hasBeenRequested } = instrumentData\n\n    assert(isRef(hasBeenRequested))\n    assert(hasBeenRequested.value === false)\n  })\n\n  it('id can return null id to prevent the query', function () {\n    const { Instrument } = makeContext()\n\n    const id = null\n    const instrumentData = useGet({ model: Instrument, id })\n    const { hasBeenRequested } = instrumentData\n\n    assert(isRef(hasBeenRequested))\n    assert(hasBeenRequested.value === false)\n  })\n\n  it('allows using `local: true` to prevent API calls from being made', function () {\n    const { Instrument } = makeContext()\n\n    const id = 1\n    const instrumentData = useGet({ model: Instrument, id, local: true })\n    const { hasBeenRequested, get } = instrumentData\n\n    assert(isRef(hasBeenRequested))\n    assert(hasBeenRequested.value === false, 'no request during init')\n\n    get(id)\n\n    assert(hasBeenRequested.value === false, 'no request after get')\n  })\n\n  it('API only hit once on initial render', async function () {\n    const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n      serverAlias: 'useGet'\n    })\n\n    class Dohickey extends BaseModel {\n      public static modelName = 'Dohickey'\n    }\n\n    const servicePath = 'dohickies'\n    const store = new Vuex.Store({\n      plugins: [\n        makeServicePlugin({\n          Model: Dohickey,\n          servicePath,\n          service: feathersClient.service(servicePath)\n        })\n      ]\n    })\n\n    let getCalls = 0\n    feathersClient.service(servicePath).hooks({\n      before: {\n        get: [\n          (ctx: HookContext) => {\n            getCalls += 1\n            ctx.result = { id: ctx.id }\n          }\n        ]\n      }\n    })\n\n    useGet({ model: Dohickey, id: 42 })\n    await new Promise((resolve) => setTimeout(resolve, 100))\n\n    assert(getCalls === 1, '`get` called once')\n  })\n})\n"
  },
  {
    "path": "test/utils.test.ts",
    "content": "import { assert } from 'chai'\nimport { AuthState } from '../src/auth-module/types'\nimport { ServiceState } from './service-module/types'\nimport { isNode, isBrowser } from '../src/utils'\nimport { diff as deepDiff } from 'deep-object-diff'\nimport {\n  getId,\n  initAuth,\n  hydrateApi,\n  getServicePrefix,\n  getServiceCapitalization,\n  getQueryInfo\n} from '../src/utils'\nimport feathersVuex from '../src/index'\nimport { feathersSocketioClient as feathersClient } from './fixtures/feathers-client'\nimport Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\ninterface RootState {\n  auth: AuthState\n  users: ServiceState\n}\n\ndescribe('Utils', function () {\n  describe('getId', () => {\n    const idField = '_id'\n    it('converts objects to strings', () => {\n      const _id = { test: true }\n      const id = getId({ _id }, idField)\n      assert.strictEqual(typeof id, 'string')\n      assert.strictEqual(id, _id.toString())\n    })\n    it('does not convert number ids', () => {\n      const _id = 1\n      const id = getId({ _id }, idField)\n      assert.strictEqual(typeof id, 'number')\n      assert.strictEqual(id, _id)\n    })\n    it('automatically finds _id', () => {\n      const _id = 1\n      const id = getId({ _id })\n      assert.strictEqual(id, _id)\n    })\n    it('automatically finds id', () => {\n      const referenceId = 1\n      const id = getId({ id: referenceId })\n      assert.strictEqual(id, referenceId)\n    })\n    it('prefers id over _id (only due to their order in the code)', () => {\n      const _id = 1\n      const referenceId = 2\n      const id = getId({ _id, id: referenceId })\n      assert.strictEqual(id, referenceId)\n    })\n  })\n\n  describe('Auth & SSR', () => {\n    before(function () {\n      const {\n        makeServicePlugin,\n        makeAuthPlugin,\n        BaseModel\n      } = feathersVuex(feathersClient, { serverAlias: 'utils' })\n\n      class User extends BaseModel {\n        public static modelName = 'User'\n        public static test = true\n      }\n\n      Object.assign(this, {\n        makeServicePlugin,\n        makeAuthPlugin,\n        BaseModel,\n        User\n      })\n    })\n    it('properly populates auth', function () {\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          this.makeServicePlugin({\n            Model: this.User,\n            servicePath: 'users',\n            service: feathersClient.service('users')\n          }),\n          this.makeAuthPlugin({})\n        ]\n      })\n      const accessToken =\n        'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoiOTk5OTk5OTk5OTkiLCJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.lUlEd3xH-TnlNRbKM3jnDVTNoIg10zgzaS6QyFZE-6g'\n      const req = {\n        headers: {\n          cookie: 'feathers-jwt=' + accessToken\n        }\n      }\n      return initAuth({\n        commit: store.commit,\n        req,\n        moduleName: 'auth',\n        cookieName: 'feathers-jwt',\n        feathersClient\n      })\n        .then(() => {\n          assert(\n            store.state.auth.accessToken === accessToken,\n            'the token was in place'\n          )\n          assert(store.state.auth.payload, 'the payload was set')\n          return feathersClient.authentication.getAccessToken()\n        })\n        .then(token => {\n          assert.isDefined(token, 'the feathers client storage was set')\n        })\n    })\n\n    it('properly hydrate SSR store', function () {\n      const {\n        makeServicePlugin,\n        BaseModel,\n        models\n      } = feathersVuex(feathersClient, { serverAlias: 'hydrate' })\n\n      class User extends BaseModel {\n        public static modelName = 'User'\n        public static test = true\n      }\n\n      const store = new Vuex.Store<RootState>({\n        plugins: [\n          makeServicePlugin({\n            Model: User,\n            servicePath: 'users',\n            service: feathersClient.service('users'),\n            mutations: {\n              addServerItem(state) {\n                state.keyedById['abcdefg'] = { id: 'abcdefg', name: 'Guzz' }\n              }\n            }\n          })\n        ]\n      })\n      store.commit('users/addServerItem')\n      assert(store.state.users.keyedById['abcdefg'], 'server document added')\n      assert(\n        store.state.users.keyedById['abcdefg'] instanceof Object,\n        'server document is pure javascript object'\n      )\n      hydrateApi({ api: models.hydrate })\n      assert(\n        store.state.users.keyedById['abcdefg'] instanceof User,\n        'document hydrated'\n      )\n    })\n  })\n\n  describe('Inflections', function () {\n    it('properly inflects the service prefix', function () {\n      const decisionTable = [\n        ['todos', 'todos'],\n        ['TODOS', 'tODOS'],\n        ['environment-Panos', 'environmentPanos'],\n        ['env-panos', 'envPanos'],\n        ['envPanos', 'envPanos'],\n        ['api/v1/env-panos', 'envPanos'],\n        ['very-long-service', 'veryLongService']\n      ]\n      decisionTable.forEach(([path, prefix]) => {\n        assert(\n          getServicePrefix(path) === prefix,\n          `The service prefix for path \"${path}\" was \"${getServicePrefix(\n            path\n          )}\", expected \"${prefix}\"`\n        )\n      })\n    })\n\n    it('properly inflects the service capitalization', function () {\n      const decisionTable = [\n        ['todos', 'Todos'],\n        ['TODOS', 'TODOS'],\n        ['environment-Panos', 'EnvironmentPanos'],\n        ['env-panos', 'EnvPanos'],\n        ['envPanos', 'EnvPanos'],\n        ['api/v1/env-panos', 'EnvPanos'],\n        ['very-long-service', 'VeryLongService']\n      ]\n      decisionTable.forEach(([path, prefix]) => {\n        assert(\n          getServiceCapitalization(path) === prefix,\n          `The service prefix for path \"${path}\" was \"${getServiceCapitalization(\n            path\n          )}\", expected \"${prefix}\"`\n        )\n      })\n    })\n  })\n\n  describe('Environments', () => {\n    it('sets isNode to true', () => {\n      assert(isNode, 'isNode was true')\n    })\n\n    it('sets isBrowser to false', () => {\n      assert(!isBrowser, 'isBrowser was false')\n    })\n  })\n})\n\ndescribe('Pagination', function () {\n  it('getQueryInfo', function () {\n    const params = {\n      qid: 'main-list',\n      query: {\n        test: true,\n        $limit: 10,\n        $skip: 0\n      }\n    }\n    const response = {\n      data: [],\n      limit: 10,\n      skip: 0,\n      total: 500\n    }\n    const info = getQueryInfo(params, response)\n    const expected = {\n      isOutdated: undefined,\n      qid: 'main-list',\n      query: {\n        test: true,\n        $limit: 10,\n        $skip: 0\n      },\n      queryId: '{\"test\":true}',\n      queryParams: {\n        test: true\n      },\n      pageParams: {\n        $limit: 10,\n        $skip: 0\n      },\n      pageId: '{\"$limit\":10,\"$skip\":0}',\n      response: undefined\n    }\n    const diff = deepDiff(info, expected)\n\n    assert.deepEqual(info, expected, 'query info formatted correctly')\n  })\n\n  it('getQueryInfo no limit or skip', function () {\n    const params = {\n      qid: 'main-list',\n      query: {\n        test: true\n      }\n    }\n    const response = {\n      data: [],\n      limit: 10,\n      skip: 0,\n      total: 500\n    }\n    const info = getQueryInfo(params, response)\n    const expected = {\n      isOutdated: undefined,\n      qid: 'main-list',\n      query: {\n        test: true\n      },\n      queryId: '{\"test\":true}',\n      queryParams: {\n        test: true\n      },\n      pageParams: {\n        $limit: 10,\n        $skip: 0\n      },\n      pageId: '{\"$limit\":10,\"$skip\":0}',\n      response: undefined\n    }\n    const diff = deepDiff(info, expected)\n\n    assert.deepEqual(info, expected, 'query info formatted correctly')\n  })\n})\n"
  },
  {
    "path": "test/vue-plugin.test.ts",
    "content": "/*\neslint\n@typescript-eslint/explicit-function-return-type: 0,\n@typescript-eslint/no-explicit-any: 0\n*/\nimport { assert } from 'chai'\nimport feathersVuex, { FeathersVuex } from '../src/index'\nimport { feathersRestClient as feathersClient } from './fixtures/feathers-client'\nimport Vue from 'vue/dist/vue'\nimport Vuex from 'vuex'\n\n// @ts-ignore\nVue.use(Vuex)\n// @ts-ignore\nVue.use(FeathersVuex)\n\ninterface VueWithFeathers {\n  $FeathersVuex: {}\n}\n\nfunction makeContext() {\n  const { makeServicePlugin, BaseModel } = feathersVuex(feathersClient, {\n    serverAlias: 'make-find-mixin'\n  })\n  class FindModel extends BaseModel {\n    public static modelName = 'FindModel'\n    public static test: boolean = true\n  }\n\n  const serviceName = 'todos'\n  const store = new Vuex.Store({\n    plugins: [\n      makeServicePlugin({\n        Model: FindModel,\n        service: feathersClient.service(serviceName)\n      })\n    ]\n  })\n  return {\n    store\n  }\n}\n\ndescribe('Vue Plugin', function () {\n  it('Adds the `$FeathersVuex` object to components', function () {\n    const { store } = makeContext()\n    const vm = new Vue({\n      name: 'todos-component',\n      store,\n      template: `<div></div>`\n    }).$mount()\n\n    assert(vm.$FeathersVuex, 'registeredPlugin correctly')\n  })\n})\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true,\n    \"outDir\": \"dist\",\n    \"moduleResolution\": \"node\",\n    \"target\": \"es6\",\n    \"sourceMap\": false,\n    \"declaration\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"node_modules\", \"**/*.test.js\"]\n}\n"
  },
  {
    "path": "tsconfig.test.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true,\n    \"outDir\": \"dist\",\n    \"moduleResolution\": \"node\",\n    \"target\": \"esnext\",\n    \"sourceMap\": true,\n    \"allowJs\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"node_modules\", \"**/*.test.js\"]\n}\n"
  }
]