[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\n\nexecutors:\n  my-executor:\n    docker:\n      - image: cimg/node:14.18.1\n        environment:\n          CI: true\n\norbs:\n  node: circleci/node@4.7.0\n\njobs:\n  # mobx-state-tree build\n  build:\n    executor: my-executor\n    steps:\n      - checkout\n\n      - run:\n          name: Install the latest version of bun\n          command: curl -fsSL https://bun.sh/install | bash\n      - run:\n          name: Link bun\n          command: sudo ln -s ~/.bun/bin/bun /usr/local/bin/\n      - run:\n          name: Install dependencies\n          command: bun install\n      - run:\n          name: Build MST\n          command: bun run build\n\n      - persist_to_workspace:\n          root: .\n          paths:\n            - ./*\n\n  # Add new prettier check job\n  check-prettier:\n    executor: my-executor\n    steps:\n      - attach_workspace:\n          at: .\n      - run:\n          name: Install the latest version of bun\n          command: curl -fsSL https://bun.sh/install | bash\n      - run:\n          name: Link bun\n          command: sudo ln -s ~/.bun/bin/bun /usr/local/bin/\n      - run:\n          name: Check code formatting\n          command: bun run prettier:check\n\n  # mobx-state-tree tests\n  test-mst-dev:\n    executor: my-executor\n    steps:\n      - attach_workspace:\n          at: .\n\n      - run:\n          name: Install the latest version of bun\n          command: curl -fsSL https://bun.sh/install | bash\n      - run:\n          name: Link bun\n          command: sudo ln -s ~/.bun/bin/bun /usr/local/bin/\n      - run: bun test:all\n\n  test-mst-prod:\n    executor: my-executor\n    steps:\n      - attach_workspace:\n          at: .\n      - run:\n          name: Install the latest version of bun\n          command: curl -fsSL https://bun.sh/install | bash\n      - run:\n          name: Link bun\n          command: sudo ln -s ~/.bun/bin/bun /usr/local/bin/\n      - run: bun test:prod\n\n  test-size:\n    executor: my-executor\n    steps:\n      - attach_workspace:\n          at: .\n      - run:\n          name: Install the latest version of bun\n          command: curl -fsSL https://bun.sh/install | bash\n      - run:\n          name: Link bun\n          command: sudo ln -s ~/.bun/bin/bun /usr/local/bin/\n      - run: bun run size\n\nworkflows:\n  version: 2\n  build-and-test:\n    jobs:\n      - build\n      \n      # Add prettier check to workflow\n      - check-prettier:\n          requires:\n            - build\n\n      - test-mst-dev:\n          requires:\n            - build\n      - test-mst-prod:\n          requires:\n            - build\n      # Temporarily disabled while we work to implement Bun testing\n      # - test-size:\n      #     requires:\n      #       - build\n"
  },
  {
    "path": ".dockerignore",
    "content": "*/node_modules\n*.log\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n\n*.ts text eol=lf\n*.json text eol=lf\n*.md text eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug report\nabout: I think something is not working as it should\n---\n\n<!-- Not following the template might result in your issue being closed without further notice -->\n\n**_Bug report_**\n\n-   [ ] I've checked documentation and searched for existing issues [and discussions](https://github.com/mobxjs/mobx-state-tree/discussions)\n-   [ ] I've made sure my project is based on the latest MST version\n-   [ ] Fork [this](https://codesandbox.io/p/sandbox/mobx-state-tree-todolist-forked-pj732k) code sandbox or another minimal reproduction.\n\n**Sandbox link or minimal reproduction code**\n\n<!-- link to your sandbox or alternatively minimal reproduction code-->\n\n**Describe the expected behavior**\n\n<!-- What should happen? -->\n\n**Describe the observed behavior**\n\n<!-- What happens instead? -->\n"
  },
  {
    "path": ".github/lock.yml",
    "content": "# Configuration for Lock Threads - https://github.com/dessant/lock-threads\n\n# Number of days of inactivity before a closed issue or pull request is locked\ndaysUntilLock: 60\n\n# Skip issues and pull requests created before a given timestamp. Timestamp must\n# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable\nskipCreatedBefore: 2019-01-01\n\n# Issues and pull requests with these labels will be ignored. Set to `[]` to disable\nexemptLabels: []\n\n# Label to add before locking, such as `outdated`. Set to `false` to disable\nlockLabel: false\n\n# Comment to post before locking. Set to `false` to disable\nlockComment: >\n  This thread has been automatically locked since there has not been\n  any recent activity after it was closed. Please open a new issue for\n  related bugs or questions.\n\n# Assign `resolved` as the reason for locking. Set to `false` to disable\nsetLockReason: true\n\n# Limit to only `issues` or `pulls`\nonly: issues\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "## What does this PR do and why?\n\n<!--\nTry to complete the sentence: \"If this pull request is approved, it will...\",\nand add some information telling us why you're requesting this change\n\nList any relevant GitHub issues, GitHub discussions, or other references.\n-->\n\n## Steps to validate locally\n\n<!--\nDescribe how you checked this in your local development environment,\nand how a reviewer might check it for themselves.\n\nConsider writing tests, providing code snippets or sandboxes, or at least\na step-by-step instruction that reviewers can use to validate the change and consider\nany unexpected behavior\n-->\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 10\n# Number of days of inactivity before a stale issue is closed\ndaysUntilClose: 4\n# Issues with these labels will never be considered stale\nexemptLabels:\n    - brainstorming/wild idea\n    - breaking change\n    - bug\n    - docs or examples\n    - enhancement\n    - has PR\n    - help/PR welcome\n    - require('@mweststrate')\n    - never-stale\n# Label to use when marking an issue as stale\nstaleLabel: stale\n# Comment to post when marking an issue as stale. Set to `false` to disable\nmarkComment: >\n    This issue has been automatically marked as stale because it has not had\n    recent activity in the last 10 days. It will be closed in 4 days if no further\n    activity occurs. Thank you for your contributions.\n# Comment to post when closing a stale issue. Set to `false` to disable\ncloseComment: false\n# Limit to only `issues` or `pulls`\nonly: issues\n# Comment to post when removing the stale label.\nunmarkComment: >\n    This issue has been automatically unmarked as stale. Please disregard previous warnings.\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n*.log\nlib\ndist\ncoverage\n.nyc_output\n.idea\npackage-lock.json\n.prettierignore\n.vscode\n.editorconfig\n/test-results/**/*.xml\n/website/build\n.DS_Store\njunit.xml"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nbun run lint-staged\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n-   Demonstrating empathy and kindness toward other people\n-   Being respectful of differing opinions, viewpoints, and experiences\n-   Giving and gracefully accepting constructive feedback\n-   Accepting responsibility and apologizing to those affected by our mistakes,\n    and learning from the experience\n-   Focusing on what is best not just for us as individuals, but for the\n    overall community\n\nExamples of unacceptable behavior include:\n\n-   The use of sexualized language or imagery, and sexual attention or\n    advances of any kind\n-   Trolling, insulting or derogatory comments, and personal or political attacks\n-   Public or private harassment\n-   Publishing others' private information, such as a physical or email\n    address, without their explicit permission\n-   Other conduct which could reasonably be considered inappropriate in a\n    professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to MobX-State-Tree\n\nWelcome to MobX-State-Tree! We're stoked that you want to contribute to our open-source project. Our community is essential in maintaining and improving the stability, test coverage, and documentation of MST. We really appreciate your time and interest in pitching in. Overall, we want to build useful software and have fun doing it - we hope you'll be able to join us!\n\n## Table of Contents\n\n1. [Getting Started](#getting-started)\n2. [Contributing Guidelines](#contributing-guidelines)\n3. [Reporting Bugs](#reporting-bugs)\n4. [Code of Conduct](#code-of-conduct)\n\n## Getting Started\n\nBefore you start contributing, please make sure you have:\n\n- [Bun](https://bun.sh/) installed on your local machine.\n- A [GitHub](https://github.com/) account, as you'll need it to create issues and submit pull requests.\n\nMost of the documentation and community assumes some amount of familiarity with:\n\n1. JavaScript\n2. TypeScript\n3. git\n4. Using the command line\n5. Experience with state management libraries (typically on the frontend, although there are plenty of applications using MST in other contexts).\n\nIf you don't feel comfortable with these concepts, we'd be happy to help you get started, but you may want to consider brushing up on them before digging into the codebase. Reach out in the [discussions section of our GitHub repository](https://github.com/mobxjs/mobx-state-tree/discussions) if you'd like pointers about where to start.\n\n## Contributing Guidelines\n\n### Prioritizing Stability Over New Features\n\nThe existing API for MobX-State-Tree is [already quite extensive](https://mobx-state-tree.js.org/intro/welcome). As such, issues and PRs about new features may take lower priority than bug fixes, improving existing features, and performance/TypeScript improvements. If you're looking to augment MST with new functionality, we encourage you to consider building your own third-party library around our project, or perhaps [contributing to the mst-middlewares package](https://github.com/coolsoftwaretyler/mst-middlewares). We'd be happy to help faciliate that work, especially if it keeps our API from expanding much further.\n\n### Tests\n\nTo maintain our library's stability, we are always striving to improve test coverage, even where more tests might be redundant. Every PR is _required to add at least one test that directly exercises your code change_, even if that test may be duplicative of existing tests. If you do not include tests with your PR, we will ask you to do so before reviewing. If you open a PR and are unwilling to write tests, we may either write tests on your behalf, or close the PR. If you are uncomfortable writing tests or working with Jest (our current testing library), we would be happy to guide you. This requirement is not intended as a barrier to entry, but as a way for us to enforce and improve the stability of our long-lived library here. It also serves as a way to externally communicate how your change will (or perhaps how it will not) modify the behavior of MST. There's no documentation quite as good as a comprehensive test suite!\n\n### Documentation Changes\n\nGood documentation is crucial for our users. If your contribution involves changes to the library's behavior, please update the documentation accordingly. Not every PR is necessarily going to require documentation updates, but we encourage you to consider touching up documentation related to your code changes. If you're unsure about where to make changes, feel free to reach out to us, and we'll be happy to guide you.\n\nWhen changing the API, commit the code changes first, then run `npm run build-docs` and commit the documentation separately.\n\n### Submitting a Pull Request\n\n1. Fork the MobX-State-Tree repository on GitHub to your own GitHub account.\n2. Clone your fork to your local machine.\n3. Create a new branch for your changes: `git checkout -b my-feature`.\n4. Before starting, it's not a bad idea to `bun install && bun run build && bun run test:all` to check that your machine can run the full test suite to start.\n5. Make your changes and ensure that all tests pass (including any new tests you have added).\n6. Update the documentation if necessary.\n7. Commit your changes: `git commit -m \"Add my feature\"`. Please consider [following conventional commit formatting](https://www.conventionalcommits.org/en/v1.0.0/).\n8. Push your changes to your fork: `git push origin my-feature`.\n9. Create a pull request on the MobX-State-Tree repository. We have a pull request template. If you fill that out and include examples of your changes, links to any issue(s) you're working on, and a good description of your PR, that would help us out a lot. If you skip those steps, we may ask you for clarification before reviewing your work.\n\nOur team will review your pull request as soon as possible and provide feedback. Please be patient, as it may take some time to review and merge your contribution.\n\n## Reporting Bugs\n\nIf you encounter a bug while using MobX-State-Tree, please help us by reporting it. To report a bug:\n\n1. Check if the bug has already been reported by searching our [issue tracker](https://github.com/mobxjs/mobx-state-tree/issues).\n2. If not, create a new issue, including as much detail as possible about the bug and steps to reproduce it. We have issue templates that will ask specific questions for you to help us understand the problem.\n\n## Bigger PRs\n\nIf you want to contribute a large or significant change to MST, we'd love to connect with you ahead of time to make sure it fits in with our overall road map, meets our stability requirements, and make sure you are set up for success. Please consider [asking around in the discussion forum](https://github.com/mobxjs/mobx-state-tree/discussions) if you have a big idea you want to implement, or if you want to work on some existing big ideas out therein the communit.\n\n## Code of Conduct\n\nWe strive to maintain a friendly and welcoming community. Please read and adhere to our [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions within the MobX-State-Tree project.\n\nThank you for considering contributing to MobX-State-Tree. Your contributions help us make our library better for everyone. We appreciate your support and look forward to collaborating with you!\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:8.11.4\n\nWORKDIR /app/website\n\nEXPOSE 3000 35729\nCOPY ./docs /app/docs\nCOPY ./website /app/website\nRUN yarn install\n\nCMD [\"yarn\", \"start\"]\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "content": "**If your issue isn't a bug report, please consider using discussion threads instead of opening an issue: https://github.com/mobxjs/mobx-state-tree/discussions**\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Michel Weststrate\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<img src=\"website/static/img/mobx-state-tree-logo-gradient.png\" alt=\"logo\" height=\"120\" align=\"right\" />\n\n# mobx-state-tree\n\n[![npm version](https://badge.fury.io/js/mobx-state-tree.svg)](https://badge.fury.io/js/mobx-state-tree)\n[![CircleCI](https://circleci.com/gh/mobxjs/mobx-state-tree.svg?style=svg)](https://circleci.com/gh/mobxjs/mobx-state-tree)\n[![Have a question? Ask on GitHub Discussions!](https://img.shields.io/badge/Have%20a%20question%3F-Ask%20on%20GitHub%20Discussions!-blue)](https://github.com/mobxjs/mobx-state-tree/discussions)\n\n## What is mobx-state-tree?\n\nTechnically speaking, mobx-state-tree (also known as MST) is a state container system built on [MobX](https://github.com/mobxjs/mobx), a functional reactive state library.\n\nThis may not mean much to you, and that’s okay. I’ll explain it like this: **MobX is a state management \"engine\", and MobX-State-Tree gives it structure and common tools you need for your app.** MST is valuable in a large team but also useful in smaller applications when you expect your code to scale rapidly. And if we compare it to Redux, MST offers better performance and much less boilerplate code than Redux!\n\nMobX is [one of the most popular Redux alternatives](https://2019.stateofjs.com/data-layer/mobx/) and is used (along with MobX-State-Tree) by companies worldwide. MST plays very well with TypeScript, React, and React Native, especially when paired with [mobx-react-lite](https://github.com/mobxjs/mobx/tree/main/packages/mobx-react-lite). It supports multiple stores, async actions and side effects, enables extremely targeted re-renders for React apps, and much more -- all in a package with _zero dependencies_ other than MobX itself.\n\n_Note: you don't need to know how to use MobX in order to use MST._\n\n# Getting started\n\nSee the [Getting started](https://mobx-state-tree.js.org/intro/getting-started) tutorial or follow the free [egghead.io course](https://egghead.io/courses/manage-application-state-with-mobx-state-tree).\n\n👉 Official docs can be found at [http://mobx-state-tree.js.org/](http://mobx-state-tree.js.org/)\n\n## Quick Code Example\n\nThere's nothing quite like looking at some code to get a feel for a library. Check out this small example of an author and list of tweets by that author.\n\n```js\nimport { types } from \"mobx-state-tree\" // alternatively: import { t } from \"mobx-state-tree\"\n\n// Define a couple models\nconst Author = types.model({\n  id: types.identifier,\n  firstName: types.string,\n  lastName: types.string\n})\nconst Tweet = types.model({\n  id: types.identifier,\n  author: types.reference(Author), // stores just the `id` reference!\n  body: types.string,\n  timestamp: types.number\n})\n\n// Define a store just like a model\nconst RootStore = types.model({\n  authors: types.array(Author),\n  tweets: types.array(Tweet)\n})\n\n// Instantiate a couple model instances\nconst jamon = Author.create({\n  id: \"jamon\",\n  firstName: \"Jamon\",\n  lastName: \"Holmgren\"\n})\n\nconst tweet = Tweet.create({\n  id: \"1\",\n  author: jamon.id, // just the ID needed here\n  body: \"Hello world!\",\n  timestamp: Date.now()\n})\n\n// Now instantiate the store!\nconst rootStore = RootStore.create({\n  authors: [jamon],\n  tweets: [tweet]\n})\n\n// Ready to use in a React component, if that's your target.\nimport { observer } from \"mobx-react-lite\"\nconst MyComponent = observer((props) => {\n  return <div>Hello, {rootStore.authors[0].firstName}!</div>\n})\n\n// Note: since this component is \"observed\", any changes to rootStore.authors[0].firstName\n// will result in a re-render! If you're not using React, you can also \"listen\" to changes\n// using `onSnapshot`: https://mobx-state-tree.js.org/concepts/snapshots\n```\n\n## Thanks!\n\n- [Michel Weststrate](https://twitter.com/mweststrate) for creating MobX, MobX-State-Tree, and MobX-React.\n- [Infinite Red](https://infinite.red) for supporting ongoing maintenance on MST.\n- [Mendix](https://mendix.com) for sponsoring and providing the opportunity to work on exploratory projects like MST.\n- [Dan Abramov](https://twitter.com/dan_abramov)'s work on [Redux](http://redux.js.org) has strongly influenced the idea of snapshots and transactional actions in MST.\n- [Giulio Canti](https://twitter.com/GiulioCanti)'s work on [tcomb](http://github.com/gcanti/tcomb) and type systems in general has strongly influenced the type system of MST.\n- All the early adopters encouraging to pursue this whole idea and proving it is something feasible.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "## Security contact information\n\nTo report a security vulnerability, please use the\n[Tidelift security contact](https://tidelift.com/security).\nTidelift will coordinate the fix and disclosure.\n"
  },
  {
    "path": "__tests__/core/1525.test.ts",
    "content": "import { types, Instance } from \"../../src/index\"\nimport { describe, it } from \"bun:test\"\n\ndescribe(\"1525. Model instance maybe fields becoming TypeScript optional fields when included in a types.union\", () => {\n    it(\"does not throw a typescript error\", () => {\n        const Model = types.model(\"myModel\", {\n            foo: types.string,\n            bar: types.maybe(types.integer)\n        })\n\n        const Store = types.model(\"store\", {\n            itemWithoutIssue: Model,\n            itemWithIssue: types.union(types.literal(\"anotherValue\"), Model)\n        })\n\n        interface IModel extends Instance<typeof Model> {}\n\n        interface FunctionArgs {\n            model1: IModel\n            model2: IModel\n        }\n\n        const store = Store.create({\n            itemWithoutIssue: { foo: \"works\" },\n            itemWithIssue: { foo: \"has ts error in a regression\" }\n        })\n\n        const f = (props: FunctionArgs) => {}\n\n        const itemWithoutIssueModel = store.itemWithoutIssue\n        const itemWithIssueModel =\n            store.itemWithIssue === \"anotherValue\" ? null : store.itemWithIssue\n        itemWithIssueModel && f({ model1: itemWithoutIssueModel, model2: itemWithIssueModel })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/1664.test.ts",
    "content": "import { types as t } from \"../../src/index\"\nimport { describe, test } from \"bun:test\"\n\ndescribe(\"1664. Array and model types are not inferred correctly when broken down into their components\", () => {\n    test(\"should not throw a typescript error\", () => {\n        // Simple concrete type with a creation type different than its instance type\n        const date = t.custom<string, Date>({\n            name: \"Date\",\n            fromSnapshot: snapshot => new Date(snapshot),\n            toSnapshot: dt => dt.toISOString(),\n            isTargetType: (val: unknown) => val instanceof Date,\n            getValidationMessage: (snapshot: unknown) =>\n                typeof snapshot !== \"string\" || isNaN(Date.parse(snapshot))\n                    ? `${snapshot} is not a valid Date string`\n                    : \"\"\n        })\n\n        //Wrap the date type in an array type. IArrayType is a sub-interface of IType.\n        const DateArray = t.array(date)\n\n        //Pass the array type to t.union, which infers the component types as <C, S, T>\n        const LoadableDateArray = t.union(t.literal(\"loading\"), DateArray)\n\n        //Instantiate the type\n        const lda = LoadableDateArray.create([])\n\n        //Try to use the array type as an instance\n        if (lda !== \"loading\") {\n            //Error: type of lda is essentially `(string | Date)[] | undefined`\n            //The creation type has been mixed together with the instance type\n            const dateArray: Date[] = lda\n        }\n    })\n})\n"
  },
  {
    "path": "__tests__/core/2230.test.ts",
    "content": "//github.com/mobxjs/mobx-state-tree/issues/2230\nimport { describe, test } from \"bun:test\"\nimport { types, Instance } from \"../../src/index\"\n\ndescribe(\"2230 - type instantiation is excessively deep and possibly infinite\", () => {\n    test(\"does not happen\", () => {\n        const ModelProps = types\n            .model({\n                prop01: \"\",\n                prop02: \"\",\n                prop03: \"\"\n            })\n            .props({\n                prop11: \"\",\n                prop12: \"\",\n                prop13: \"\"\n            })\n            .props({\n                prop21: \"\",\n                prop22: \"\",\n                prop23: \"\"\n            })\n            .props({\n                prop31: \"\",\n                prop32: \"\",\n                prop33: \"\"\n            })\n            .props({\n                prop41: \"\",\n                prop42: \"\",\n                prop43: \"\"\n            })\n            .props({\n                prop51: \"\",\n                prop52: \"\",\n                prop53: \"\"\n            })\n            .props({\n                prop61: \"\",\n                prop62: \"\",\n                prop63: \"\"\n            })\n            .props({\n                prop71: \"\",\n                prop72: \"\",\n                prop73: \"\"\n            })\n            .props({\n                prop81: \"\",\n                prop82: \"\",\n                prop83: \"\"\n            })\n            .props({\n                prop91: \"\",\n                prop92: \"\",\n                prop93: \"\"\n            })\n        interface IModelProps extends Instance<typeof ModelProps> {}\n\n        const ModelVolatile = ModelProps.volatile(() => ({\n            vol01: null,\n            vol02: null,\n            vol03: null\n        }))\n            .volatile(() => ({\n                vol11: null,\n                vol12: null,\n                vol13: null\n            }))\n            .volatile(() => ({\n                vol21: null,\n                vol22: null,\n                vol23: null\n            }))\n            .volatile(() => ({\n                vol31: null,\n                vol32: null,\n                vol33: null\n            }))\n            .volatile(() => ({\n                vol41: null,\n                vol42: null,\n                vol43: null\n            }))\n            .volatile(() => ({\n                vol51: null,\n                vol52: null,\n                vol53: null\n            }))\n            .volatile(() => ({\n                vol61: null,\n                vol62: null,\n                vol63: null\n            }))\n            .volatile(() => ({\n                vol71: null,\n                vol72: null,\n                vol73: null\n            }))\n            .volatile(() => ({\n                vol81: null,\n                vol82: null,\n                vol83: null\n            }))\n            .volatile(() => ({\n                vol91: null,\n                vol92: null,\n                vol93: null\n            }))\n        interface IModelVolatile extends Instance<typeof ModelVolatile> {}\n\n        const ModelViews = ModelVolatile.views((self: IModelVolatile) => ({\n            get vol01Var() {\n                return self.vol01\n            }\n        }))\n        interface IModelViews extends Instance<typeof ModelViews> {}\n\n        const Action1 = ModelViews.actions((self: IModelViews) => ({\n            getProp01(): string {\n                return self.prop01\n            }\n        }))\n        interface IAction1 extends Instance<typeof Action1> {}\n\n        const Action2 = Action1.actions((self: IAction1) => ({\n            getProp11(): string {\n                return self.prop11\n            }\n        }))\n        interface IAction2 extends Instance<typeof Action2> {}\n\n        const Action3 = Action2.actions((self: IAction2) => ({\n            getProp21(): string {\n                return self.prop21\n            }\n        }))\n        interface IAction3 extends Instance<typeof Action3> {}\n\n        const Action4 = Action3.actions((self: IAction3) => ({\n            getProp31(): string {\n                return self.prop31\n            }\n        }))\n        interface IAction4 extends Instance<typeof Action4> {}\n\n        const Action5 = Action4.actions((self: IAction4) => ({\n            getProp41(): string {\n                return self.prop41\n            }\n        }))\n            .actions((self: IAction4) => ({\n                getProp51(): string {\n                    return self.prop51\n                }\n            }))\n            .actions((self: IAction4) => ({\n                getProp61(): string {\n                    return self.prop61\n                }\n            }))\n            .actions((self: IAction4) => ({\n                getProp71(): string {\n                    return self.prop71\n                }\n            }))\n            .actions((self: IAction4) => ({\n                getProp81(): string {\n                    return self.prop81\n                }\n            }))\n            .actions((self: IAction4) => ({\n                getProp91(): string {\n                    return self.prop91\n                }\n            }))\n        interface IAction5 extends Instance<typeof Action5> {}\n    })\n})\n"
  },
  {
    "path": "__tests__/core/__snapshots__/async.test.ts.snap",
    "content": "// Bun Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`can handle erroring actions 1`] = `\n[\n  {\n    \"allParentIds\": [],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 1,\n    \"name\": \"startFetch\",\n    \"parentActionEvent\": undefined,\n    \"parentEvent\": undefined,\n    \"parentId\": 0,\n    \"rootId\": 1,\n    \"type\": \"action\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_throw\",\n  },\n]\n`;\n\nexports[`empty sequence works 1`] = `\n[\n  {\n    \"allParentIds\": [],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 1,\n    \"name\": \"startFetch\",\n    \"parentActionEvent\": undefined,\n    \"parentEvent\": undefined,\n    \"parentId\": 0,\n    \"rootId\": 1,\n    \"type\": \"action\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_return\",\n  },\n]\n`;\n\nexports[`typings 1`] = `\n[\n  {\n    \"allParentIds\": [],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 1,\n    \"name\": \"startFetch\",\n    \"parentActionEvent\": undefined,\n    \"parentEvent\": undefined,\n    \"parentId\": 0,\n    \"rootId\": 1,\n    \"type\": \"action\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"tea\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume_error\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"biscuit\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_return\",\n  },\n]\n`;\n\nexports[`typings 2`] = `\n[\n  {\n    \"allParentIds\": [],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 1,\n    \"name\": \"startFetch\",\n    \"parentActionEvent\": undefined,\n    \"parentEvent\": undefined,\n    \"parentId\": 0,\n    \"rootId\": 1,\n    \"type\": \"action\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"x\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume_error\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"x\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_throw\",\n  },\n]\n`;\n\nexports[`can handle nested async actions when using decorate 1`] = `\n[\n  {\n    \"allParentIds\": [],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 1,\n    \"name\": \"startFetch\",\n    \"parentActionEvent\": undefined,\n    \"parentEvent\": undefined,\n    \"parentId\": 0,\n    \"rootId\": 1,\n    \"type\": \"action\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"drinking coffee\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"awake\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_return\",\n  },\n]\n`;\n\nexports[`can handle nested async actions when using decorate 2`] = `\n[\n  {\n    \"allParentIds\": [],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 1,\n    \"name\": \"startFetch\",\n    \"parentActionEvent\": undefined,\n    \"parentEvent\": undefined,\n    \"parentId\": 0,\n    \"rootId\": 1,\n    \"type\": \"action\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"black\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n      2,\n    ],\n    \"args\": [\n      \"drinking black\",\n    ],\n    \"id\": 3,\n    \"name\": \"uppercase\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [\n        1,\n      ],\n      \"args\": [\n        undefined,\n      ],\n      \"id\": 2,\n      \"name\": \"fetchData\",\n      \"parentActionEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentId\": 1,\n      \"rootId\": 1,\n      \"type\": \"flow_resume\",\n    },\n    \"parentId\": 2,\n    \"rootId\": 1,\n    \"type\": \"flow_spawn\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n      2,\n    ],\n    \"args\": [\n      undefined,\n    ],\n    \"id\": 3,\n    \"name\": \"uppercase\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [\n        1,\n      ],\n      \"args\": [\n        undefined,\n      ],\n      \"id\": 2,\n      \"name\": \"fetchData\",\n      \"parentActionEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentId\": 1,\n      \"rootId\": 1,\n      \"type\": \"flow_resume\",\n    },\n    \"parentId\": 2,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n      2,\n    ],\n    \"args\": [\n      \"DRINKING BLACK\",\n    ],\n    \"id\": 3,\n    \"name\": \"uppercase\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [\n        1,\n      ],\n      \"args\": [\n        undefined,\n      ],\n      \"id\": 2,\n      \"name\": \"fetchData\",\n      \"parentActionEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentId\": 1,\n      \"rootId\": 1,\n      \"type\": \"flow_resume\",\n    },\n    \"parentId\": 2,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n      2,\n    ],\n    \"args\": [\n      \"DRINKING BLACK\",\n    ],\n    \"id\": 3,\n    \"name\": \"uppercase\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [\n        1,\n      ],\n      \"args\": [\n        undefined,\n      ],\n      \"id\": 2,\n      \"name\": \"fetchData\",\n      \"parentActionEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentEvent\": {\n        \"allParentIds\": [],\n        \"args\": [\n          \"black\",\n        ],\n        \"id\": 1,\n        \"name\": \"startFetch\",\n        \"parentActionEvent\": undefined,\n        \"parentEvent\": undefined,\n        \"parentId\": 0,\n        \"rootId\": 1,\n        \"type\": \"action\",\n      },\n      \"parentId\": 1,\n      \"rootId\": 1,\n      \"type\": \"flow_resume\",\n    },\n    \"parentId\": 2,\n    \"rootId\": 1,\n    \"type\": \"flow_return\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"DRINKING BLACK\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_resume\",\n  },\n  {\n    \"allParentIds\": [\n      1,\n    ],\n    \"args\": [\n      \"DRINKING BLACK\",\n    ],\n    \"id\": 2,\n    \"name\": \"fetchData\",\n    \"parentActionEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentEvent\": {\n      \"allParentIds\": [],\n      \"args\": [\n        \"black\",\n      ],\n      \"id\": 1,\n      \"name\": \"startFetch\",\n      \"parentActionEvent\": undefined,\n      \"parentEvent\": undefined,\n      \"parentId\": 0,\n      \"rootId\": 1,\n      \"type\": \"action\",\n    },\n    \"parentId\": 1,\n    \"rootId\": 1,\n    \"type\": \"flow_return\",\n  },\n]\n`;\n"
  },
  {
    "path": "__tests__/core/__snapshots__/custom-type.test.ts.snap",
    "content": "// Bun Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`reassignments will work 1`] = `\n[\n  {\n    \"balance\": \"2.5\",\n    \"lastTransaction\": null,\n  },\n  {\n    \"balance\": \"3.5\",\n    \"lastTransaction\": null,\n  },\n  {\n    \"balance\": \"4.5\",\n    \"lastTransaction\": null,\n  },\n  {\n    \"balance\": \"4.5\",\n    \"lastTransaction\": \"2.5\",\n  },\n  {\n    \"balance\": \"4.5\",\n    \"lastTransaction\": null,\n  },\n]\n`;\n\nexports[`reassignments will work 2`] = `\n[\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": \"2.5\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": \"3.5\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": \"4.5\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/lastTransaction\",\n    \"value\": \"2.5\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/lastTransaction\",\n    \"value\": null,\n  },\n]\n`;\n\nexports[`complex reassignments will work 1`] = `\n[\n  {\n    \"balance\": [\n      2,\n      5,\n    ],\n  },\n  {\n    \"balance\": [\n      2,\n      5,\n    ],\n  },\n  {\n    \"balance\": [\n      3,\n      5,\n    ],\n  },\n  {\n    \"balance\": [\n      4,\n      5,\n    ],\n  },\n]\n`;\n\nexports[`complex reassignments will work 2`] = `\n[\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": [\n      2,\n      5,\n    ],\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": [\n      2,\n      5,\n    ],\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": [\n      3,\n      5,\n    ],\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/balance\",\n    \"value\": [\n      4,\n      5,\n    ],\n  },\n]\n`;\n"
  },
  {
    "path": "__tests__/core/__snapshots__/reference-custom.test.ts.snap",
    "content": "// Bun Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`it should support custom references - adv 1`] = `\n[\n  \"1\",\n  \"2\",\n  \"1\",\n  null,\n  \"3\",\n]\n`;\n\nexports[`it should support custom references - adv 2`] = `\n[\n  {\n    \"selection\": \"Michel\",\n    \"users\": {\n      \"1\": {\n        \"id\": \"1\",\n        \"name\": \"Michel\",\n      },\n      \"2\": {\n        \"id\": \"2\",\n        \"name\": \"Mattia\",\n      },\n    },\n  },\n  {\n    \"selection\": \"Mattia\",\n    \"users\": {\n      \"1\": {\n        \"id\": \"1\",\n        \"name\": \"Michel\",\n      },\n      \"2\": {\n        \"id\": \"2\",\n        \"name\": \"Mattia\",\n      },\n    },\n  },\n  {\n    \"selection\": \"Michel\",\n    \"users\": {\n      \"1\": {\n        \"id\": \"1\",\n        \"name\": \"Michel\",\n      },\n      \"2\": {\n        \"id\": \"2\",\n        \"name\": \"Mattia\",\n      },\n    },\n  },\n  {\n    \"selection\": \"Michel\",\n    \"users\": {\n      \"2\": {\n        \"id\": \"2\",\n        \"name\": \"Mattia\",\n      },\n    },\n  },\n  {\n    \"selection\": \"Michel\",\n    \"users\": {\n      \"2\": {\n        \"id\": \"2\",\n        \"name\": \"Mattia\",\n      },\n      \"3\": {\n        \"id\": \"3\",\n        \"name\": \"Michel\",\n      },\n    },\n  },\n]\n`;\n\nexports[`it should support custom references - adv 3`] = `\n[\n  {\n    \"op\": \"replace\",\n    \"path\": \"/selection\",\n    \"value\": \"Michel\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/selection\",\n    \"value\": \"Mattia\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/selection\",\n    \"value\": \"Michel\",\n  },\n  {\n    \"op\": \"remove\",\n    \"path\": \"/users/1\",\n  },\n  {\n    \"op\": \"add\",\n    \"path\": \"/users/3\",\n    \"value\": {\n      \"id\": \"3\",\n      \"name\": \"Michel\",\n    },\n  },\n]\n`;\n\nexports[`it should support custom references - adv 4`] = `\n[\n  {\n    \"op\": \"replace\",\n    \"path\": \"/selection\",\n    \"value\": \"Mattia\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/selection\",\n    \"value\": \"Michel\",\n  },\n  {\n    \"op\": \"replace\",\n    \"path\": \"/selection\",\n    \"value\": \"Mattia\",\n  },\n  {\n    \"op\": \"add\",\n    \"path\": \"/users/1\",\n    \"value\": {\n      \"id\": \"1\",\n      \"name\": \"Michel\",\n    },\n  },\n  {\n    \"op\": \"remove\",\n    \"path\": \"/users/3\",\n  },\n]\n`;\n"
  },
  {
    "path": "__tests__/core/action.test.ts",
    "content": "import { configure } from \"mobx\"\nimport {\n    recordActions,\n    types,\n    getSnapshot,\n    onAction,\n    applyPatch,\n    applySnapshot,\n    addMiddleware,\n    getRoot,\n    cast,\n    IMiddlewareEvent,\n    ISerializedActionCall,\n    Instance\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\n/// Simple action replay and invocation\nconst Task = types\n    .model({\n        done: false\n    })\n    .actions(self => {\n        function toggle() {\n            self.done = !self.done\n            return self.done\n        }\n        return {\n            toggle\n        }\n    })\ntest(\"it should be possible to invoke a simple action\", () => {\n    const t1 = Task.create()\n    expect(t1.done).toBe(false)\n    expect(t1.toggle()).toBe(true)\n    expect(t1.done).toBe(true)\n})\ntest(\"it should be possible to record & replay a simple action\", () => {\n    const t1 = Task.create()\n    const t2 = Task.create()\n    expect(t1.done).toBe(false)\n    expect(t2.done).toBe(false)\n    const recorder = recordActions(t1)\n    t1.toggle()\n    t1.toggle()\n    t1.toggle()\n    expect(recorder.actions).toEqual([\n        { name: \"toggle\", path: \"\", args: [] },\n        { name: \"toggle\", path: \"\", args: [] },\n        { name: \"toggle\", path: \"\", args: [] }\n    ])\n    recorder.replay(t2)\n    expect(t2.done).toBe(true)\n})\ntest(\"applying patches should be recordable and replayable\", () => {\n    const t1 = Task.create()\n    const t2 = Task.create()\n    const recorder = recordActions(t1)\n    expect(t1.done).toBe(false)\n    applyPatch(t1, { op: \"replace\", path: \"/done\", value: true })\n    expect(t1.done).toBe(true)\n    expect(recorder.actions).toEqual([\n        {\n            name: \"@APPLY_PATCHES\",\n            path: \"\",\n            args: [[{ op: \"replace\", path: \"/done\", value: true }]]\n        }\n    ])\n    recorder.replay(t2)\n    expect(t2.done).toBe(true)\n})\ntest(\"applying patches should be replacing the root store\", () => {\n    const t1 = Task.create()\n    const recorder = recordActions(t1)\n    expect(t1.done).toBe(false)\n    applyPatch(t1, { op: \"replace\", path: \"\", value: { done: true } })\n    expect(t1.done).toBe(true)\n    expect(recorder.actions).toEqual([\n        {\n            name: \"@APPLY_PATCHES\",\n            path: \"\",\n            args: [[{ op: \"replace\", path: \"\", value: { done: true } }]]\n        }\n    ])\n})\ntest(\"applying snapshots should be recordable and replayable\", () => {\n    const t1 = Task.create()\n    const t2 = Task.create()\n    const recorder = recordActions(t1)\n    expect(t1.done).toBe(false)\n    applySnapshot(t1, { done: true })\n    expect(t1.done).toBe(true)\n    expect(recorder.actions).toEqual([\n        {\n            name: \"@APPLY_SNAPSHOT\",\n            path: \"\",\n            args: [{ done: true }]\n        }\n    ])\n    recorder.replay(t2)\n    expect(t2.done).toBe(true)\n})\n// Complex actions\nconst Customer = types.model(\"Customer\", {\n    id: types.identifierNumber,\n    name: types.string\n})\nconst Order = types\n    .model(\"Order\", {\n        customer: types.maybeNull(types.reference(Customer))\n    })\n    .actions(self => {\n        function setCustomer(customer: Instance<typeof Customer>) {\n            self.customer = customer\n        }\n        function noopSetCustomer(_: Instance<typeof Customer>) {\n            // noop\n        }\n        return {\n            setCustomer,\n            noopSetCustomer\n        }\n    })\nconst OrderStore = types.model(\"OrderStore\", {\n    customers: types.array(Customer),\n    orders: types.array(Order)\n})\nfunction createTestStore() {\n    const store = OrderStore.create({\n        customers: [{ id: 1, name: \"Mattia\" }],\n        orders: [\n            {\n                customer: null\n            }\n        ]\n    })\n    onAction(store, () => {})\n    return store\n}\ntest(\"it should not be possible to pass a complex object\", () => {\n    const store = createTestStore()\n    const recorder = recordActions(store)\n    expect(store.customers[0].name).toBe(\"Mattia\")\n    store.orders[0].setCustomer(store.customers[0])\n    expect(store.orders[0].customer!.name).toBe(\"Mattia\")\n    expect(store.orders[0].customer).toBe(store.customers[0])\n    expect(getSnapshot(store)).toEqual({\n        customers: [\n            {\n                id: 1,\n                name: \"Mattia\"\n            }\n        ],\n        orders: [\n            {\n                customer: 1\n            }\n        ]\n    })\n    expect(recorder.actions).toEqual([\n        {\n            name: \"setCustomer\",\n            path: \"/orders/0\",\n            args: [{ $MST_UNSERIALIZABLE: true, type: \"[MSTNode: Customer]\" }]\n        }\n    ])\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should not be possible to set the wrong type\", () => {\n        const store = createTestStore()\n        expect(() => {\n            store.orders[0].setCustomer(store.orders[0] as any)\n        }).toThrow(\n            \"Error while converting <Order@/orders/0> to `(reference(Customer) | null)`:\\n\\n    \" +\n                \"value of type Order: <Order@/orders/0> is not assignable to type: `(reference(Customer) | null)`, expected an instance of `(reference(Customer) | null)` or a snapshot like `(reference(Customer) | null?)` instead.\"\n        ) // wrong type!\n    })\n}\ntest(\"it should not be possible to pass the element of another tree\", () => {\n    const store1 = createTestStore()\n    const store2 = createTestStore()\n    const recorder = recordActions(store2)\n    store2.orders[0].setCustomer(store1.customers[0])\n    expect(recorder.actions).toEqual([\n        {\n            name: \"setCustomer\",\n            path: \"/orders/0\",\n            args: [\n                {\n                    $MST_UNSERIALIZABLE: true,\n                    type: \"[MSTNode: Customer]\"\n                }\n            ]\n        }\n    ])\n})\ntest(\"it should not be possible to pass an unserializable object\", () => {\n    const store = createTestStore()\n    const circular = { a: null as any }\n    circular.a = circular\n    const recorder = recordActions(store)\n    store.orders[0].noopSetCustomer(circular as any)\n    store.orders[0].noopSetCustomer(Buffer.from(\"bla\") as any)\n\n    expect(recorder.actions).toEqual([\n        {\n            args: [\n                {\n                    $MST_UNSERIALIZABLE: true,\n                    type: \"TypeError: JSON.stringify cannot serialize cyclic structures.\"\n                }\n            ],\n            name: \"noopSetCustomer\",\n            path: \"/orders/0\"\n        },\n        {\n            args: [\n                {\n                    $MST_UNSERIALIZABLE: true,\n                    type: \"[object Buffer]\"\n                }\n            ],\n            name: \"noopSetCustomer\",\n            path: \"/orders/0\"\n        }\n    ])\n})\ntest(\"it should be possible to pass a complex plain object\", () => {\n    const t1 = Task.create()\n    const t2 = Task.create()\n    const recorder = recordActions(t1)\n    ;(t1 as any).toggle({ bla: [\"nuff\", [\"said\"]] }) // nonsense, but serializable!\n    expect(recorder.actions).toEqual([\n        { name: \"toggle\", path: \"\", args: [{ bla: [\"nuff\", [\"said\"]] }] }\n    ])\n    recorder.replay(t2)\n    expect(t2.done).toBe(true)\n})\ntest(\"action should be bound\", () => {\n    const task = Task.create()\n    const f = task.toggle\n    expect(f()).toBe(true)\n    expect(task.done).toBe(true)\n})\ntest(\"snapshot should be available and updated during an action\", () => {\n    const Model = types\n        .model({\n            x: types.number\n        })\n        .actions(self => {\n            function inc() {\n                self.x += 1\n                const res = getSnapshot(self).x\n                self.x += 1\n                return res\n            }\n            return {\n                inc\n            }\n        })\n    const a = Model.create({ x: 2 })\n    expect(a.inc()).toBe(3)\n    expect(a.x).toBe(4)\n    expect(getSnapshot(a).x).toBe(4)\n})\n\ntest(\"indirectly called private functions should be able to modify state\", () => {\n    const Model = types\n        .model({\n            x: 3\n        })\n        .actions(self => {\n            function incrementBy(delta: number) {\n                self.x += delta\n            }\n            return {\n                inc() {\n                    incrementBy(1)\n                },\n                dec() {\n                    incrementBy(-1)\n                }\n            }\n        })\n    const cnt = Model.create()\n    expect(cnt.x).toBe(3)\n    cnt.dec()\n    expect(cnt.x).toBe(2)\n    expect((cnt as any).incrementBy).toBe(undefined)\n})\ntest(\"volatile state survives reonciliation\", () => {\n    const Model = types.model({ x: 3 }).actions(self => {\n        let incrementor = 1\n        return {\n            setIncrementor(value: number) {\n                incrementor = value\n            },\n            inc() {\n                self.x += incrementor\n            }\n        }\n    })\n    const Store = types.model({\n        cnt: types.optional(Model, {})\n    })\n    const store = Store.create()\n    store.cnt.inc()\n    expect(store.cnt.x).toBe(4)\n    store.cnt.setIncrementor(3)\n    store.cnt.inc()\n    expect(store.cnt.x).toBe(7)\n    applySnapshot(store, { cnt: { x: 2 } })\n    expect(store.cnt.x).toBe(2)\n    store.cnt.inc()\n    expect(store.cnt.x).toBe(5) // incrementor was not lost\n})\ntest(\"middleware events are correct\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const A = types.model({}).actions(self => ({\n        a(x: number) {\n            return this.b(x * 2)\n        },\n        b(y: number) {\n            return y + 1\n        }\n    }))\n    const a = A.create()\n    const events: IMiddlewareEvent[] = []\n    addMiddleware(a, function (call, next) {\n        events.push(call)\n        return next(call)\n    })\n    a.a(7)\n    const event1 = {\n        args: [7],\n        context: {},\n        id: 1,\n        name: \"a\",\n        parentId: 0,\n        rootId: 1,\n        allParentIds: [],\n        tree: {},\n        type: \"action\",\n        parentEvent: undefined,\n        parentActionEvent: undefined\n    } as IMiddlewareEvent\n    const event2 = {\n        args: [14],\n        context: {},\n        id: 2,\n        name: \"b\",\n        parentId: 1,\n        rootId: 1,\n        allParentIds: [1],\n        tree: {},\n        type: \"action\",\n        parentEvent: event1,\n        parentActionEvent: event1\n    } as IMiddlewareEvent\n    expect(events).toEqual([event1, event2])\n})\n\ntest(\"actions are mockable\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const M = types\n        .model()\n        .actions(self => ({\n            method(): number {\n                return 3\n            }\n        }))\n        .views(self => ({\n            view(): number {\n                return 3\n            }\n        }))\n    const m = M.create()\n    if (process.env.NODE_ENV === \"production\") {\n        expect(() => {\n            m.method = function () {\n                return 3\n            }\n        }).toThrow(TypeError)\n        expect(() => {\n            m.view = function () {\n                return 3\n            }\n        }).toThrow(TypeError)\n    } else {\n        m.method = function () {\n            return 4\n        }\n        expect(m.method()).toBe(4)\n        m.view = function () {\n            return 4\n        }\n        expect(m.view()).toBe(4)\n    }\n})\n\ntest(\"after attach action should work correctly\", () => {\n    const Todo = types\n        .model({\n            title: \"test\"\n        })\n        .actions(self => ({\n            remove() {\n                getRoot<typeof S>(self).remove(cast(self))\n            }\n        }))\n    const S = types\n        .model({\n            todos: types.array(Todo)\n        })\n        .actions(self => ({\n            remove(todo: Instance<typeof Todo>) {\n                self.todos.remove(todo)\n            }\n        }))\n\n    const s = S.create({\n        todos: [{ title: \"todo\" }]\n    })\n    const events: ISerializedActionCall[] = []\n    onAction(\n        s,\n        call => {\n            events.push(call)\n        },\n        true\n    )\n\n    s.todos[0].remove()\n\n    expect(events).toEqual([\n        {\n            args: [],\n            name: \"remove\",\n            path: \"/todos/0\"\n        }\n    ])\n})\n"
  },
  {
    "path": "__tests__/core/actionTrackingMiddleware2.test.ts",
    "content": "import {\n    addMiddleware,\n    createActionTrackingMiddleware2,\n    types,\n    flow,\n    IActionTrackingMiddleware2Call\n} from \"../../src\"\n\nimport { expect, it, test } from \"bun:test\"\n\nfunction createTestMiddleware(m: any, actionName: string, value: number, calls: string[]) {\n    function checkCall(call: IActionTrackingMiddleware2Call<any>) {\n        expect(call.name).toBe(actionName)\n        expect(call.args).toEqual([value])\n        expect(call.context).toBe(m)\n        expect(call.env).toBe(call.id)\n    }\n\n    const mware = createActionTrackingMiddleware2({\n        filter(call) {\n            return call.name === actionName\n        },\n        onStart(call) {\n            call.env = call.id // just to check env is copied properly down\n            calls.push(`${call.name} (${call.id}) - onStart`)\n            checkCall(call)\n        },\n        onFinish(call, error) {\n            calls.push(`${call.name} (${call.id}) - onFinish (error: ${!!error})`)\n            checkCall(call)\n        }\n    })\n\n    addMiddleware(m, mware, false)\n}\n\nasync function doTest(m: any, mode: \"success\" | \"fail\") {\n    const calls: string[] = []\n\n    createTestMiddleware(m, \"setX\", 10, calls)\n    createTestMiddleware(m, \"setY\", 9, calls)\n\n    try {\n        await m.setZ(8) // -> setY(9) -> setX(10)\n        if (mode === \"fail\") {\n            expect().fail(\"should have failed\")\n        }\n    } catch (e) {\n        if (mode === \"fail\") {\n            expect(e).toBe(\"error\")\n        } else {\n            throw e\n            // fail(\"should have succeeded\")\n        }\n    }\n\n    return calls\n}\n\nasync function syncTest(mode: \"success\" | \"fail\") {\n    const M = types\n        .model({\n            x: 1,\n            y: 2,\n            z: 3\n        })\n        .actions(self => ({\n            setX(v: number) {\n                self.x = v\n                if (mode === \"fail\") {\n                    throw \"error\"\n                }\n            },\n            setY(v: number) {\n                self.y = v\n                this.setX(v + 1)\n            },\n            setZ(v: number) {\n                self.z = v\n                this.setY(v + 1)\n            }\n        }))\n\n    const m = M.create()\n\n    const calls = await doTest(m, mode)\n\n    if (mode === \"success\") {\n        expect(calls).toEqual([\n            \"setY (2) - onStart\",\n            \"setX (3) - onStart\",\n            \"setX (3) - onFinish (error: false)\",\n            \"setY (2) - onFinish (error: false)\"\n        ])\n    } else {\n        expect(calls).toEqual([\n            \"setY (5) - onStart\",\n            \"setX (6) - onStart\",\n            \"setX (6) - onFinish (error: true)\",\n            \"setY (5) - onFinish (error: true)\"\n        ])\n    }\n}\n\n/**\n * This test checks that the middleware is called and\n */\ntest(\"sync action\", async () => {\n    await syncTest(\"success\")\n    await syncTest(\"fail\")\n})\n\nasync function flowTest(mode: \"success\" | \"fail\") {\n    const _subFlow = flow(function* subFlow() {\n        yield Promise.resolve()\n    })\n\n    const M = types\n        .model({\n            x: 1,\n            y: 2,\n            z: 3\n        })\n        .actions(self => ({\n            setX: flow(function* flowSetX(v: number) {\n                yield Promise.resolve()\n                yield _subFlow()\n                self.x = v\n                if (mode === \"fail\") {\n                    throw \"error\"\n                }\n            }),\n            setY: flow(function* flowSetY(v: number) {\n                self.y = v\n                yield (self as any).setX(v + 1)\n            }),\n            setZ: flow(function* flowSetZ(v: number) {\n                self.z = v\n                yield (self as any).setY(v + 1)\n            })\n        }))\n\n    const m = M.create()\n\n    const calls = await doTest(m, mode)\n\n    if (mode === \"success\") {\n        expect(calls).toEqual([\n            \"setY (3) - onStart\",\n            \"setX (5) - onStart\",\n            \"setX (5) - onFinish (error: false)\",\n            \"setY (3) - onFinish (error: false)\"\n        ])\n    } else {\n        expect(calls).toEqual([\n            \"setY (10) - onStart\",\n            \"setX (12) - onStart\",\n            \"setX (12) - onFinish (error: true)\",\n            \"setY (10) - onFinish (error: true)\"\n        ])\n    }\n}\n\ntest(\"flow action\", async () => {\n    await flowTest(\"success\")\n    await flowTest(\"fail\")\n})\n\ntest(\"#1250\", async () => {\n    const M = types\n        .model({\n            x: 0,\n            y: 0\n        })\n        .actions(self => ({\n            setX: flow(function* () {\n                self.x = 10\n                yield new Promise(resolve => setTimeout(resolve, 10))\n            }),\n            setY() {\n                self.y = 10\n            }\n        }))\n\n    const calls: string[] = []\n    const mware = createActionTrackingMiddleware2({\n        filter(call) {\n            calls.push(\n                `${call.name} (${call.id}) <- (${call.parentCall && call.parentCall.id}) - filter`\n            )\n            return true\n        },\n        onStart(call) {\n            calls.push(\n                `${call.name} (${call.id}) <- (${call.parentCall && call.parentCall.id}) - onStart`\n            )\n        },\n        onFinish(call, error) {\n            calls.push(\n                `${call.name} (${call.id}) <- (${\n                    call.parentCall && call.parentCall.id\n                }) - onFinish (error: ${!!error})`\n            )\n        }\n    })\n\n    const model = M.create({})\n\n    addMiddleware(model, mware, false)\n\n    expect(model.x).toBe(0)\n    expect(model.y).toBe(0)\n    expect(calls).toEqual([])\n\n    const p = model.setX()\n    expect(model.x).toBe(10)\n    expect(model.y).toBe(0)\n    expect(calls).toEqual([\"setX (1) <- (undefined) - filter\", \"setX (1) <- (undefined) - onStart\"])\n    calls.length = 0\n\n    await new Promise<void>(r =>\n        setTimeout(() => {\n            model.setY()\n            r()\n        }, 5)\n    )\n    expect(model.x).toBe(10)\n    expect(model.y).toBe(10)\n    expect(calls).toEqual([\n        \"setY (3) <- (undefined) - filter\",\n        \"setY (3) <- (undefined) - onStart\",\n        \"setY (3) <- (undefined) - onFinish (error: false)\"\n    ])\n    calls.length = 0\n\n    await p\n    expect(model.x).toBe(10)\n    expect(model.y).toBe(10)\n    expect(calls).toEqual([\"setX (1) <- (undefined) - onFinish (error: false)\"])\n    calls.length = 0\n})\n\n/**\n * Test that when createActionTrackingMiddleware2 is called with valid hooks and a synchronous action, it runs onStart and onFinish hooks.\n */\ntest(\"successful execution\", () => {\n    const M = types.model({}).actions(self => ({\n        test() {}\n    }))\n\n    const calls: string[] = []\n\n    const mware = createActionTrackingMiddleware2({\n        filter(call) {\n            calls.push(`${call.name} - filter`)\n            return true\n        },\n        onStart(call) {\n            calls.push(`${call.name} - onStart`)\n        },\n        onFinish(call, error) {\n            calls.push(`${call.name} - onFinish (error: ${!!error})`)\n        }\n    })\n\n    const model = M.create({})\n    addMiddleware(model, mware, false)\n\n    model.test()\n\n    expect(calls).toEqual([\"test - filter\", \"test - onStart\", \"test - onFinish (error: false)\"])\n})\n\n/**\n * Test that when createActionTrackingMiddleware2 is called with valid hooks and an asynchronous action, it runs onStart and onFinish hooks.\n */\ntest(\"successful execution with async action\", async () => {\n    const M = types.model({}).actions(self => ({\n        async test() {}\n    }))\n\n    const calls: string[] = []\n\n    const mware = createActionTrackingMiddleware2({\n        filter(call) {\n            calls.push(`${call.name} - filter`)\n            return true\n        },\n        onStart(call) {\n            calls.push(`${call.name} - onStart`)\n        },\n        onFinish(call, error) {\n            calls.push(`${call.name} - onFinish (error: ${!!error})`)\n        }\n    })\n\n    const model = M.create({})\n    addMiddleware(model, mware, false)\n\n    await model.test()\n\n    expect(calls).toEqual([\"test - filter\", \"test - onStart\", \"test - onFinish (error: false)\"])\n})\n\n/**\n * Test that when the filter returns true, the action is tracked. We check\n * this by checking that the onStart and onFinish hooks are called for `runThisOne`,\n * which is the name provided to the `filter` function.\n */\nit(\"calls onStart and onFinish hooks for actions that pass the filter\", () => {\n    const M2 = types.model({}).actions(self => ({\n        trackThisOne() {},\n        doNotTrackThisOne() {}\n    }))\n\n    const calls: string[] = []\n\n    const mware2 = createActionTrackingMiddleware2({\n        filter(call) {\n            return call.name === \"trackThisOne\"\n        },\n        onStart(call) {\n            calls.push(`${call.name} - onStart`)\n        },\n        onFinish(call, error) {\n            calls.push(`${call.name} - onFinish (error: ${!!error})`)\n        }\n    })\n\n    const model2 = M2.create({})\n    addMiddleware(model2, mware2, false)\n\n    model2.trackThisOne()\n    // We call this action to prove that it is not tracked since it fails - there's also a test for this below.\n    model2.doNotTrackThisOne()\n\n    expect(calls).toEqual([\"trackThisOne - onStart\", \"trackThisOne - onFinish (error: false)\"])\n})\n/**\n * Test that when the filter returns false, the action is not tracked. We check\n * this by checking that the onStart and onFinish hooks are not called for `doNotTrackThisOne`,\n */\nit(\"does not call onStart and onFinish hooks for actions that do not pass the filter\", () => {\n    const M = types.model({}).actions(self => ({\n        trackThisOne() {},\n        doNotTrackThisOne() {}\n    }))\n\n    const calls: string[] = []\n\n    const mware = createActionTrackingMiddleware2({\n        filter(call) {\n            return call.name === \"trackThisOne\"\n        },\n        onStart(call) {\n            calls.push(`${call.name} - onStart`)\n        },\n        onFinish(call, error) {\n            calls.push(`${call.name} - onFinish (error: ${!!error})`)\n        }\n    })\n\n    const model = M.create({})\n    addMiddleware(model, mware, false)\n\n    model.doNotTrackThisOne()\n\n    expect(calls).toEqual([])\n})\n\n/**\n * Test that parent actions and child actions have the expected order of operations -\n * if we had an action `a` that called an action `b1`, then `b2` inside `a`, the flow would be:\n *\n * - `filter(a)`\n * - `onStart(a)`\n *  - `filter(b1)`\n *  - `onStart(b1)`\n *  - `onFinish(b1)`\n *  - `filter(b2)`\n *  - `onStart(b2)`\n *  - `onFinish(b2)`\n * - `onFinish(a)`\n *\n * See https://mobx-state-tree.js.org/API/#createactiontrackingmiddleware2\n */\ntest(\"complete in the expected recursive order\", () => {\n    const M = types\n        .model({})\n        .actions(self => ({\n            childAction1() {},\n            childAction2() {}\n        }))\n        .actions(self => ({\n            parentAction() {\n                self.childAction1()\n                self.childAction2()\n            }\n        }))\n\n    const calls: string[] = []\n\n    const mware = createActionTrackingMiddleware2({\n        filter(call) {\n            calls.push(`${call.name} - filter`)\n            return true\n        },\n        onStart(call) {\n            calls.push(`${call.name} - onStart`)\n        },\n        onFinish(call, error) {\n            calls.push(`${call.name} - onFinish (error: ${!!error})`)\n        }\n    })\n\n    const model = M.create({})\n    addMiddleware(model, mware, false)\n\n    model.parentAction()\n\n    expect(calls).toEqual([\n        \"parentAction - filter\",\n        \"parentAction - onStart\",\n        \"childAction1 - filter\",\n        \"childAction1 - onStart\",\n        \"childAction1 - onFinish (error: false)\",\n        \"childAction2 - filter\",\n        \"childAction2 - onStart\",\n        \"childAction2 - onFinish (error: false)\",\n        \"parentAction - onFinish (error: false)\"\n    ])\n})\n"
  },
  {
    "path": "__tests__/core/api.test.ts",
    "content": "import { expect, test } from \"bun:test\"\nimport { readFileSync } from \"fs\"\nimport * as mst from \"../../src\"\n\nfunction stringToArray(s: string): string[] {\n    return s.split(\",\").map(str => str.trim())\n}\n\nconst METHODS_AND_INTERNAL_TYPES = stringToArray(`\n    typecheck,\n    escapeJsonPath,\n    unescapeJsonPath,\n    joinJsonPath,\n    splitJsonPath,\n    decorate,\n    addMiddleware,\n    isStateTreeNode,\n    flow,\n    castFlowReturn,\n    applyAction,\n    onAction,\n    recordActions,\n    createActionTrackingMiddleware,\n    createActionTrackingMiddleware2,\n    setLivelinessChecking,\n    getLivelinessChecking,\n    getType,\n    getChildType,\n    onPatch,\n    onSnapshot,\n    applyPatch,\n    recordPatches,\n    protect,\n    unprotect,\n    isProtected,\n    applySnapshot,\n    getSnapshot,\n    hasParent,\n    getParent,\n    hasParentOfType,\n    getParentOfType,\n    getRoot,\n    getPath,\n    getPathParts,\n    isRoot,\n    resolvePath,\n    resolveIdentifier,\n    getIdentifier,\n    tryResolve,\n    getRelativePath,\n    clone,\n    detach,\n    destroy,\n    isAlive,\n    addDisposer,\n    getEnv,\n    hasEnv,\n    walk,\n    getMembers,\n    getPropertyMembers,\n    cast,\n    castToSnapshot,\n    castToReferenceSnapshot,\n    isType,\n    isArrayType,\n    isFrozenType,\n    isIdentifierType,\n    isLateType,\n    isLiteralType,\n    isMapType,\n    isModelType,\n    isOptionalType,\n    isPrimitiveType,\n    isReferenceType,\n    isRefinementType,\n    isUnionType,\n    isValidReference,\n    tryReference,\n    types,\n    t,\n    getNodeId,\n    getRunningActionContext,\n    isActionContextChildOf,\n    isActionContextThisOrChildOf,\n    toGeneratorFunction,\n    toGenerator\n`)\n\nconst DEPRECATED_METHODS_AND_INTERNAL_TYPES = stringToArray(`\n    setLivelynessChecking,\n    process\n`)\n\nconst METHODS = METHODS_AND_INTERNAL_TYPES.filter(s => s[0].toLowerCase() === s[0])\n\nconst INTERNAL_TYPES = METHODS_AND_INTERNAL_TYPES.filter(s => s[0].toUpperCase() === s[0])\n\nconst TYPES = stringToArray(`\n    enumeration,\n    model,\n    compose,\n    custom,\n    reference,\n    safeReference,\n    union,\n    optional,\n    literal,\n    maybe,\n    maybeNull,\n    refinement,\n    string,\n    boolean,\n    number,\n    integer,\n    float,\n    finite,\n    bigint,\n    Date,\n    map,\n    array,\n    frozen,\n    identifier,\n    identifierNumber,\n    late,\n    lazy,\n    undefined,\n    null,\n    snapshotProcessor\n`)\n\ntest(\"correct api exposed\", () => {\n    expect(\n        Object.keys(mst)\n            .sort()\n            .filter(key => (mst as any)[key] !== undefined) // filter out interfaces\n            .filter(s => !DEPRECATED_METHODS_AND_INTERNAL_TYPES.includes(s))\n    ).toEqual([...METHODS, ...INTERNAL_TYPES].sort())\n})\n\ntest(\"correct types exposed\", () => {\n    expect(Object.keys(mst.types).sort()).toEqual(TYPES.sort())\n})\n\ntest(\"types also exposed on t module\", () => {\n    expect(Object.keys(mst.t).sort()).toEqual(TYPES.sort())\n})\n\ntest(\"all methods mentioned in API docs\", () => {\n    const apimd = readFileSync(__dirname + \"/../../docs/API/index.md\", \"utf8\")\n    const missing = TYPES.map(type => \"types.\" + type).filter(\n        identifier => apimd.indexOf(identifier) === -1\n    )\n    missing.push(\n        ...METHODS.filter(identifier => apimd.indexOf(\"#\" + identifier.toLowerCase()) === -1)\n    )\n    expect(missing).toEqual([\"types.lazy\", \"types\"])\n})\n\ntest(\"only accepted dependencies\", () => {\n    const validDeps: string[] = [\"ts-essentials\"]\n\n    const deps =\n        JSON.parse(readFileSync(__dirname + \"/../../package.json\", \"utf8\")).dependencies || {}\n\n    const depNames = Object.keys(deps) || []\n    expect(depNames.sort()).toEqual(validDeps.sort())\n})\n"
  },
  {
    "path": "__tests__/core/array.test.ts",
    "content": "import {\n    unprotect,\n    onSnapshot,\n    onPatch,\n    clone,\n    isAlive,\n    applyPatch,\n    getPath,\n    applySnapshot,\n    getSnapshot,\n    types,\n    IJsonPatch,\n    setLivelinessChecking,\n    detach,\n    cast\n} from \"../../src\"\nimport { observable, autorun, configure } from \"mobx\"\nimport { expect, test } from \"bun:test\"\n\nconst createTestFactories = () => {\n    const ItemFactory = types.optional(\n        types.model({\n            to: \"world\"\n        }),\n        {}\n    )\n    const Factory = types.array(ItemFactory)\n    return { Factory, ItemFactory }\n}\n// === FACTORY TESTS ===\ntest(\"it should create a factory\", () => {\n    const { Factory } = createTestFactories()\n    expect(getSnapshot(Factory.create())).toEqual([])\n})\ntest(\"it should succeed if not optional and no default provided\", () => {\n    const Factory = types.array(types.string)\n    expect(getSnapshot(Factory.create())).toEqual([])\n})\ntest(\"it should restore the state from the snapshot\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const { Factory } = createTestFactories()\n    const instance = Factory.create([{ to: \"universe\" }])\n    expect(getSnapshot(instance)).toEqual([{ to: \"universe\" }])\n    expect(\"\" + instance).toBe(\"AnonymousModel@/0\") // just the normal to string\n})\n// === SNAPSHOT TESTS ===\ntest(\"it should emit snapshots\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let snapshots: (typeof Factory.SnapshotType)[] = []\n    onSnapshot(doc, snapshot => snapshots.push(snapshot))\n    doc.push(ItemFactory.create())\n    expect(snapshots).toEqual([[{ to: \"world\" }]])\n})\ntest(\"it should apply snapshots\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applySnapshot(doc, [{ to: \"universe\" }])\n    expect(getSnapshot(doc)).toEqual([{ to: \"universe\" }])\n})\ntest(\"it should return a snapshot\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.push(ItemFactory.create())\n    expect(getSnapshot(doc)).toEqual([{ to: \"world\" }])\n})\n// === PATCHES TESTS ===\ntest(\"it should emit add patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc.push(ItemFactory.create({ to: \"universe\" }))\n    expect(patches).toEqual([{ op: \"add\", path: \"/0\", value: { to: \"universe\" } }])\n})\ntest(\"it should apply an add patch\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, { op: \"add\", path: \"/0\", value: { to: \"universe\" } })\n    expect(getSnapshot(doc)).toEqual([{ to: \"universe\" }])\n})\ntest(\"it should emit update patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.push(ItemFactory.create())\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc[0] = ItemFactory.create({ to: \"universe\" })\n    expect(patches).toEqual([{ op: \"replace\", path: \"/0\", value: { to: \"universe\" } }])\n})\ntest(\"it should apply an update patch\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, { op: \"replace\", path: \"/0\", value: { to: \"universe\" } })\n    expect(getSnapshot(doc)).toEqual([{ to: \"universe\" }])\n})\ntest(\"it should emit remove patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.push(ItemFactory.create())\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc.splice(0)\n    expect(patches).toEqual([{ op: \"replace\", path: \"\", value: [] }])\n})\ntest(\"it should apply a remove patch\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.push(ItemFactory.create())\n    doc.push(ItemFactory.create({ to: \"universe\" }))\n    applyPatch(doc, { op: \"remove\", path: \"/0\" })\n    expect(getSnapshot(doc)).toEqual([{ to: \"universe\" }])\n})\ntest(\"it should apply patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, [\n        { op: \"add\", path: \"/0\", value: { to: \"mars\" } },\n        { op: \"replace\", path: \"/0\", value: { to: \"universe\" } }\n    ])\n    expect(getSnapshot(doc)).toEqual([{ to: \"universe\" }])\n})\n// === TYPE CHECKS ===\ntest(\"it should check the type correctly\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    expect(Factory.is(doc)).toEqual(true)\n    expect(Factory.is([])).toEqual(true)\n    expect(Factory.is({})).toEqual(false)\n    expect(Factory.is([{ to: \"mars\" }])).toEqual(true)\n    expect(Factory.is([{ wrongKey: true }])).toEqual(true)\n    expect(Factory.is([{ to: true }])).toEqual(false)\n})\ntest(\"paths shoud remain correct when splicing\", () => {\n    const Task = types.model(\"Task\", {\n        done: false\n    })\n    const store = types\n        .model({\n            todos: types.array(Task)\n        })\n        .create({\n            todos: [{}]\n        })\n    unprotect(store)\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\"])\n    store.todos.push({})\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\"])\n    store.todos.unshift({})\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\", \"/todos/2\"])\n    store.todos.splice(0, 2)\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\"])\n    store.todos.splice(0, 1, {}, {}, {})\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\", \"/todos/2\"])\n    store.todos.remove(store.todos[1])\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\"])\n})\ntest(\"items should be reconciled correctly when splicing - 1\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Task = types.model(\"Task\", {\n        x: types.string\n    })\n    const a = Task.create({ x: \"a\" }),\n        b = Task.create({ x: \"b\" }),\n        c = Task.create({ x: \"c\" }),\n        d = Task.create({ x: \"d\" })\n    const store = types\n        .model({\n            todos: types.array(Task)\n        })\n        .create({\n            todos: [a]\n        })\n    unprotect(store)\n    expect(store.todos.slice()).toEqual([a])\n    expect(isAlive(a)).toBe(true)\n    store.todos.push(b)\n    expect(store.todos.slice()).toEqual([a, b])\n    store.todos.unshift(c)\n    expect(store.todos.slice()).toEqual([c, a, b])\n    store.todos.splice(0, 2)\n    expect(store.todos.slice()).toEqual([b])\n    expect(isAlive(a)).toBe(false)\n    expect(isAlive(b)).toBe(true)\n    expect(isAlive(c)).toBe(false)\n\n    setLivelinessChecking(\"error\")\n    expect(() => store.todos.splice(0, 1, a, c, d)).toThrow(\n        \"You are trying to read or write to an object that is no longer part of a state tree. (Object type: 'Task', Path upon death: '/todos/1', Subpath: '', Action: ''). Either detach nodes first, or don't use objects after removing / replacing them in the tree.\"\n    )\n    store.todos.splice(0, 1, clone(a), clone(c), clone(d))\n    expect(store.todos.map(_ => _.x)).toEqual([\"a\", \"c\", \"d\"])\n})\ntest(\"items should be reconciled correctly when splicing - 2\", () => {\n    const Task = types.model(\"Task\", {\n        x: types.string\n    })\n    const a = Task.create({ x: \"a\" }),\n        b = Task.create({ x: \"b\" }),\n        c = Task.create({ x: \"c\" }),\n        d = Task.create({ x: \"d\" })\n    const store = types\n        .model({\n            todos: types.array(Task)\n        })\n        .create({\n            todos: [a, b, c, d]\n        })\n    unprotect(store)\n    store.todos.splice(2, 1, { x: \"e\" }, { x: \"f\" })\n    // becomes, a, b, e, f, d\n    expect(store.todos.length).toBe(5)\n    expect(store.todos[0] === a).toBe(true)\n    expect(store.todos[1] === b).toBe(true)\n    expect(store.todos[2] !== c).toBe(true)\n    expect(store.todos[2].x).toBe(\"e\")\n    expect(store.todos[3] !== d).toBe(true)\n    expect(store.todos[3].x).toBe(\"f\")\n    expect(store.todos[4] === d).toBe(true) // preserved and moved\n    expect(store.todos[4].x).toBe(\"d\")\n    expect(store.todos.map(getPath)).toEqual([\n        \"/todos/0\",\n        \"/todos/1\",\n        \"/todos/2\",\n        \"/todos/3\",\n        \"/todos/4\"\n    ])\n    store.todos.splice(1, 3, { x: \"g\" })\n    // becomes a, g, d\n    expect(store.todos.length).toBe(3)\n    expect(store.todos[0] === a).toBe(true)\n    expect(store.todos[1].x).toBe(\"g\")\n    expect(store.todos[2].x).toBe(\"d\")\n    expect(store.todos[1] !== b).toBe(true)\n    expect(store.todos[2] === d).toBe(true) // still original d\n    expect(store.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\", \"/todos/2\"])\n})\ntest(\"it should reconciliate keyed instances correctly\", () => {\n    const Store = types.model({\n        todos: types.optional(\n            types.array(\n                types.model(\"Task\", {\n                    id: types.identifier,\n                    task: \"\",\n                    done: false\n                })\n            ),\n            []\n        )\n    })\n    const store = Store.create({\n        todos: [\n            { id: \"1\", task: \"coffee\", done: false },\n            { id: \"2\", task: \"tea\", done: false },\n            { id: \"3\", task: \"biscuit\", done: false }\n        ]\n    })\n    expect(store.todos.map(todo => todo.task)).toEqual([\"coffee\", \"tea\", \"biscuit\"])\n    expect(store.todos.map(todo => todo.done)).toEqual([false, false, false])\n    expect(store.todos.map(todo => todo.id)).toEqual([\"1\", \"2\", \"3\"])\n    const coffee = store.todos[0]\n    const tea = store.todos[1]\n    const biscuit = store.todos[2]\n    applySnapshot(store, {\n        todos: [\n            { id: \"2\", task: \"Tee\", done: true },\n            { id: \"1\", task: \"coffee\", done: true },\n            { id: \"4\", task: \"biscuit\", done: false },\n            { id: \"5\", task: \"stuffz\", done: false }\n        ]\n    })\n    expect(store.todos.map(todo => todo.task)).toEqual([\"Tee\", \"coffee\", \"biscuit\", \"stuffz\"])\n    expect(store.todos.map(todo => todo.done)).toEqual([true, true, false, false])\n    expect(store.todos.map(todo => todo.id)).toEqual([\"2\", \"1\", \"4\", \"5\"])\n    expect(store.todos[0] === tea).toBe(true)\n    expect(store.todos[1] === coffee).toBe(true)\n    expect(store.todos[2] === biscuit).toBe(false)\n})\ntest(\"it correctly reconciliate when swapping\", () => {\n    const Task = types.model(\"Task\", {})\n    const Store = types.model({\n        todos: types.optional(types.array(Task), [])\n    })\n    const s = Store.create()\n    unprotect(s)\n    const a = Task.create()\n    const b = Task.create()\n    s.todos.push(a, b)\n    s.todos.replace([b, a])\n    expect(s.todos[0] === b).toBe(true)\n    expect(s.todos[1] === a).toBe(true)\n    expect(s.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\"])\n})\ntest(\"it correctly reconciliate when swapping using snapshots\", () => {\n    const Task = types.model(\"Task\", {})\n    const Store = types.model({\n        todos: types.array(Task)\n    })\n    const s = Store.create()\n    unprotect(s)\n    const a = Task.create()\n    const b = Task.create()\n    s.todos.push(a, b)\n    s.todos.replace([getSnapshot(b), getSnapshot(a)])\n    expect(s.todos[0] === b).toBe(true)\n    expect(s.todos[1] === a).toBe(true)\n    expect(s.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\"])\n    s.todos.push({})\n    expect(s.todos[0] === b).toBe(true)\n    expect(s.todos[1] === a).toBe(true)\n    expect(s.todos.map(getPath)).toEqual([\"/todos/0\", \"/todos/1\", \"/todos/2\"])\n})\ntest(\"it should not be allowed to add the same item twice to the same store\", () => {\n    const Task = types.model(\"Task\", {})\n    const Store = types.model({\n        todos: types.optional(types.array(Task), [])\n    })\n    const s = Store.create()\n    unprotect(s)\n    const a = Task.create()\n    s.todos.push(a)\n    expect(() => {\n        s.todos.push(a)\n    }).toThrow(\n        \"Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '/todos/1', but it lives already at '/todos/0'\"\n    )\n    const b = Task.create()\n    expect(() => {\n        s.todos.push(b, b)\n    }).toThrow(\n        \"Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '/todos/2', but it lives already at '/todos/1'\"\n    )\n})\ntest(\"it should support observable arrays\", () => {\n    const TestArray = types.array(types.number)\n    const testArray = TestArray.create(observable([1, 2]))\n    expect(testArray[0] === 1).toBe(true)\n    expect(testArray.length === 2).toBe(true)\n    expect(Array.isArray(testArray.slice())).toBe(true)\n})\n\ntest(\"it should support observable arrays, array should be real when useProxies eq 'always'\", () => {\n    const TestArray = types.array(types.number)\n    const testArray = TestArray.create(observable([1, 2]))\n    expect(testArray[0] === 1).toBe(true)\n    expect(testArray.length === 2).toBe(true)\n    expect(Array.isArray(testArray)).toBe(true)\n})\n\ntest(\"it should support observable arrays, array should be not real when useProxies eq 'never'\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const TestArray = types.array(types.number)\n    const testArray = TestArray.create(observable([1, 2]))\n    expect(testArray[0] === 1).toBe(true)\n    expect(testArray.length === 2).toBe(true)\n    expect(Array.isArray(testArray.slice())).toBe(true)\n    expect(Array.isArray(testArray)).toBe(false)\n})\n\ntest(\"it should correctly handle re-adding of the same objects\", () => {\n    const Store = types\n        .model(\"Task\", {\n            objects: types.array(types.maybe(types.frozen()))\n        })\n        .actions(self => ({\n            setObjects(objects: {}[]) {\n                self.objects.replace(objects)\n            }\n        }))\n    const store = Store.create({\n        objects: []\n    })\n    expect(store.objects.slice()).toEqual([])\n    const someObject = {}\n    store.setObjects([someObject])\n    expect(store.objects.slice()).toEqual([someObject])\n    store.setObjects([someObject])\n    expect(store.objects.slice()).toEqual([someObject])\n})\ntest(\"it should work correctly for splicing primitive array\", () => {\n    const store = types.array(types.number).create([1, 2, 3])\n    unprotect(store)\n    store.splice(0, 1)\n    expect(store.slice()).toEqual([2, 3])\n    store.unshift(1)\n    expect(store.slice()).toEqual([1, 2, 3])\n    store.replace([4, 5])\n    expect(store.slice()).toEqual([4, 5])\n    store.clear()\n    expect(store.slice()).toEqual([])\n})\ntest(\"it should keep unchanged for structrual equalled snapshot\", () => {\n    const Store = types.model({\n        todos: types.array(\n            types.model(\"Task\", {\n                id: types.identifier,\n                task: \"\",\n                done: false\n            })\n        ),\n        numbers: types.array(types.number)\n    })\n    const store = Store.create({\n        todos: [\n            { id: \"1\", task: \"coffee\", done: false },\n            { id: \"2\", task: \"tea\", done: false },\n            { id: \"3\", task: \"biscuit\", done: false }\n        ],\n        numbers: [1, 2, 3]\n    })\n\n    const values: boolean[][] = []\n    autorun(() => {\n        values.push(store.todos.map(todo => todo.done))\n    })\n    applySnapshot(store.todos, [\n        { id: \"1\", task: \"coffee\", done: false },\n        { id: \"2\", task: \"tea\", done: false },\n        { id: \"3\", task: \"biscuit\", done: true }\n    ])\n    applySnapshot(store.todos, [\n        { id: \"1\", task: \"coffee\", done: false },\n        { id: \"2\", task: \"tea\", done: false },\n        { id: \"3\", task: \"biscuit\", done: true }\n    ])\n    expect(values).toEqual([\n        [false, false, false],\n        [false, false, true]\n    ])\n\n    const values1: number[][] = []\n    autorun(() => {\n        values1.push(store.numbers.slice())\n    })\n    applySnapshot(store.numbers, [1, 2, 4])\n    applySnapshot(store.numbers, [1, 2, 4])\n    expect(values1).toEqual([\n        [1, 2, 3],\n        [1, 2, 4]\n    ])\n})\n\n// === OPERATIONS TESTS ===\ntest(\"#1105 - it should return pop/shift'ed values for scalar arrays\", () => {\n    const ScalarArray = types\n        .model({\n            array: types.array(types.number)\n        })\n        .actions(self => {\n            return {\n                shift() {\n                    return self.array.shift()\n                }\n            }\n        })\n\n    const test = ScalarArray.create({ array: [3, 5] })\n\n    expect(test.shift()).toEqual(3)\n    expect(test.shift()).toEqual(5)\n})\n\ntest(\"it should return pop/shift'ed values for object arrays\", () => {\n    const TestObject = types.model({ id: types.string })\n    const ObjectArray = types\n        .model({\n            array: types.array(TestObject)\n        })\n        .actions(self => {\n            return {\n                shift() {\n                    return self.array.shift()\n                },\n                pop() {\n                    return self.array.pop()\n                }\n            }\n        })\n\n    const test = ObjectArray.create({\n        array: [{ id: \"foo\" }, { id: \"mid\" }, { id: \"bar\" }]\n    })\n\n    const foo = test.shift()!\n    expect(isAlive(foo)).toBe(false)\n    const bar = test.pop()!\n    expect(isAlive(bar)).toBe(false)\n\n    // we have to use clone or getSnapshot to access dead nodes data\n    expect(clone(foo)).toEqual({ id: \"foo\" })\n    expect(getSnapshot(bar)).toEqual({ id: \"bar\" })\n})\n\ntest(\"#1173 - detaching an array should not eliminate its children\", () => {\n    const M = types.model({})\n    const AM = types.array(M)\n    const Store = types.model({ items: AM })\n    const s = Store.create({ items: [{}, {}, {}] })\n    const n0 = s.items[0]\n\n    unprotect(s)\n\n    const detachedItems = detach(s.items)\n    expect(s.items).not.toBe(detachedItems)\n    expect(s.items.length).toBe(0)\n    expect(detachedItems.length).toBe(3)\n    expect(detachedItems[0]).toBe(n0)\n})\n\ntest(\"initializing an array instance from another array instance should end up in the same instance\", () => {\n    const A = types.array(types.number)\n    const a1 = A.create([1, 2, 3])\n    const a2 = A.create(a1)\n    expect(a1).toBe(a2)\n    expect(getSnapshot(a1)).toEqual([1, 2, 3])\n})\n\ntest(\"assigning filtered instances works\", () => {\n    const Task = types.model(\"Task\", {\n        done: false\n    })\n    const store = types\n        .model({\n            todos: types.array(Task)\n        })\n        .actions(self => ({\n            clearFinishedTodos() {\n                self.todos = cast(self.todos.filter(todo => !todo.done))\n            }\n        }))\n        .create({\n            todos: [{ done: true }, { done: false }, { done: true }]\n        })\n\n    expect(store.todos.length).toBe(3)\n    const done = store.todos.filter(t => t.done)\n    const notDone = store.todos.filter(t => !t.done)\n    expect(store.todos.every(t => isAlive(t)))\n    store.clearFinishedTodos()\n    expect(store.todos.length).toBe(1)\n    expect(store.todos[0]).toBe(notDone[0])\n    expect(done.every(t => !isAlive(t))).toBe(true)\n    expect(notDone.every(t => isAlive(t))).toBe(true)\n})\n\ntest(\"#1676 - should accept read-only arrays\", () => {\n    const ArrayType = types.array(types.string)\n    const data = [\"foo\", \"bar\"] as const\n    const instance = ArrayType.create(data)\n    expect(getSnapshot(instance)).toEqual([\"foo\", \"bar\"])\n})\n"
  },
  {
    "path": "__tests__/core/async.test.ts",
    "content": "import { configure, reaction } from \"mobx\"\nimport {\n    addMiddleware,\n    decorate,\n    destroy,\n    flow,\n    IMiddlewareEvent,\n    IMiddlewareEventType,\n    IMiddlewareHandler,\n    recordActions,\n    toGenerator,\n    // TODO: export IRawActionCall\n    toGeneratorFunction,\n    types\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\nimport type { Writable } from \"ts-essentials\"\n\nfunction delay<TV>(time: number, value: TV, shouldThrow = false): Promise<TV> {\n    return new Promise((resolve, reject) => {\n        setTimeout(() => {\n            if (shouldThrow) reject(value)\n            else resolve(value)\n        }, time)\n    })\n}\n\nasync function testCoffeeTodo(\n    generator: (\n        self: any\n    ) => (str: string) => Generator<Promise<any>, string | void | undefined, undefined>,\n    shouldError: boolean,\n    resultValue: string | undefined,\n    producedCoffees: any[]\n) {\n    const Todo = types\n        .model({\n            title: \"get coffee\"\n        })\n        .actions(self => ({\n            startFetch: flow(generator(self))\n        }))\n\n    const events: IMiddlewareEvent[] = []\n    const t1 = Todo.create({})\n    addMiddleware(t1, (c, next) => {\n        events.push(c)\n        return next(c)\n    })\n\n    const coffees: any[] = []\n    reaction(\n        () => t1.title,\n        coffee => coffees.push(coffee)\n    )\n\n    try {\n        configure({ enforceActions: \"observed\" })\n        const result = await t1.startFetch(\"black\")\n        expect(shouldError).toBe(false)\n        expect(result).toBe(resultValue)\n    } catch (error) {\n        expect(shouldError).toBe(true)\n    } finally {\n        configure({ enforceActions: \"never\" })\n    }\n\n    expect(coffees).toEqual(producedCoffees)\n    const filtered = filterRelevantStuff(events)\n    expect(filtered).toMatchSnapshot()\n}\ntest(\"flow happens in single ticks\", async () => {\n    const X = types\n        .model({\n            y: 1\n        })\n        .actions(self => ({\n            p: flow(function* () {\n                self.y++\n                self.y++\n                yield delay(1, true, false)\n                self.y++\n                self.y++\n            })\n        }))\n    const x = X.create()\n    const values: number[] = []\n    reaction(\n        () => x.y,\n        v => values.push(v)\n    )\n\n    await x.p()\n    expect(x.y).toBe(5)\n    expect(values).toEqual([3, 5])\n})\ntest(\"can handle async actions\", () => {\n    testCoffeeTodo(\n        self =>\n            function* fetchData(kind: string) {\n                self.title = \"getting coffee \" + kind\n                self.title = yield delay(100, \"drinking coffee\")\n                return \"awake\"\n            },\n        false,\n        \"awake\",\n        [\"getting coffee black\", \"drinking coffee\"]\n    )\n})\ntest(\"can handle erroring actions\", () => {\n    testCoffeeTodo(\n        self =>\n            function* fetchData(kind: string) {\n                throw kind\n            },\n        true,\n        \"black\",\n        []\n    )\n})\ntest(\"can handle try catch\", () => {\n    testCoffeeTodo(\n        self =>\n            function* fetchData(kind: string) {\n                try {\n                    yield delay(10, \"tea\", true)\n                    return undefined\n                } catch (e) {\n                    self.title = e\n                    return \"biscuit\"\n                }\n            },\n        false,\n        \"biscuit\",\n        [\"tea\"]\n    )\n})\ntest(\"empty sequence works\", () => {\n    testCoffeeTodo(() => function* fetchData(kind: string) {}, false, undefined, [])\n})\ntest(\"can handle throw from yielded promise works\", () => {\n    testCoffeeTodo(\n        () =>\n            function* fetchData(kind: string) {\n                yield delay(10, \"x\", true)\n            },\n        true,\n        \"x\",\n        []\n    )\n})\ntest(\"typings\", async () => {\n    const M = types.model({ title: types.string }).actions(self => {\n        function* a(x: string) {\n            yield delay(10, \"x\", false)\n            self.title = \"7\"\n            return 23\n        }\n        // tslint:disable-next-line:no-shadowed-variable\n        const b = flow(function* b(x: string) {\n            yield delay(10, \"x\", false)\n            self.title = \"7\"\n            return 24\n        })\n        return { a: flow(a), b }\n    })\n    const m1 = M.create({ title: \"test \" })\n    const resA = m1.a(\"z\")\n    const resB = m1.b(\"z\")\n    const [x1, x2] = await Promise.all([resA, resB])\n    expect(x1).toBe(23)\n    expect(x2).toBe(24)\n})\ntest(\"typings\", async () => {\n    const M = types.model({ title: types.string }).actions(self => {\n        function* a(x: string) {\n            yield delay(10, \"x\", false)\n            self.title = \"7\"\n            return 23\n        }\n        // tslint:disable-next-line:no-shadowed-variable\n        const b = flow(function* b(x: string) {\n            yield delay(10, \"x\", false)\n            self.title = \"7\"\n            return 24\n        })\n        return { a: flow(a), b }\n    })\n    const m1 = M.create({ title: \"test \" })\n    const resA = m1.a(\"z\")\n    const resB = m1.b(\"z\")\n    const [x1, x2] = await Promise.all([resA, resB])\n    expect(x1).toBe(23)\n    expect(x2).toBe(24)\n})\ntest(\"recordActions should only emit invocation\", async () => {\n    let calls = 0\n    const M = types\n        .model({\n            title: types.string\n        })\n        .actions(self => {\n            function* a(x: string) {\n                yield delay(10, \"x\", false)\n                calls++\n                return 23\n            }\n            return {\n                a: flow(a)\n            }\n        })\n    const m1 = M.create({ title: \"test \" })\n    const recorder = recordActions(m1)\n    await m1.a(\"x\")\n\n    recorder.stop()\n    expect(recorder.actions).toEqual([\n        {\n            args: [\"x\"],\n            name: \"a\",\n            path: \"\"\n        }\n    ])\n    expect(calls).toBe(1)\n    recorder.replay(m1)\n\n    await new Promise(resolve => setTimeout(resolve, 50))\n    expect(calls).toBe(2)\n})\ntest(\"can handle nested async actions\", () => {\n    // tslint:disable-next-line:no-shadowed-variable\n    const uppercase = flow(function* uppercase(value: string) {\n        const res = yield delay(20, value.toUpperCase())\n        return res\n    })\n    testCoffeeTodo(\n        self =>\n            function* fetchData(kind: string) {\n                self.title = yield uppercase(\"drinking \" + kind)\n                return self.title\n            },\n        false,\n        \"DRINKING BLACK\",\n        [\"DRINKING BLACK\"]\n    )\n})\ntest(\"can handle nested async actions when using decorate\", async () => {\n    const events: [IMiddlewareEventType, string][] = []\n    const middleware: IMiddlewareHandler = (call, next) => {\n        events.push([call.type, call.name])\n        return next(call)\n    }\n    // tslint:disable-next-line:no-shadowed-variable\n    const uppercase = flow(function* uppercase(value: string) {\n        const res = yield delay(20, value.toUpperCase())\n        return res\n    })\n    const Todo = types.model({}).actions(self => {\n        // tslint:disable-next-line:no-shadowed-variable\n        const act = flow(function* act(value: string) {\n            return yield uppercase(value)\n        })\n        return { act: decorate(middleware, act) }\n    })\n\n    const res = await Todo.create().act(\"x\")\n    expect(res).toBe(\"X\")\n    expect(events).toEqual([\n        [\"action\", \"act\"],\n        [\"flow_spawn\", \"act\"],\n        [\"flow_resume\", \"act\"],\n        [\"flow_resume\", \"act\"],\n        [\"flow_return\", \"act\"]\n    ])\n})\n\ntest(\"flow gain back control when node become not alive during yield\", async () => {\n    expect.assertions(2)\n    const rejectError = new Error(\"Reject Error\")\n    const MyModel = types.model({}).actions(() => {\n        return {\n            doAction() {\n                return flow(function* () {\n                    try {\n                        yield delay(20, \"\").then(() => Promise.reject(rejectError))\n                    } catch (e) {\n                        expect(e).toEqual(rejectError)\n                        throw e\n                    }\n                })()\n            }\n        }\n    })\n\n    const m = MyModel.create({})\n    const p = m.doAction()\n    destroy(m)\n    try {\n        await p\n    } catch (e) {\n        expect(e).toEqual(rejectError)\n    }\n})\n\nfunction filterRelevantStuff(stuff: Partial<Writable<IMiddlewareEvent>>[]) {\n    return stuff.map(x => {\n        delete x.context\n        delete x.tree\n        return x\n    })\n}\n\ntest(\"flow typings\", async () => {\n    const promise = Promise.resolve()\n\n    const M = types.model({ x: 5 }).actions(self => ({\n        // should be () => Promise<void>\n        voidToVoid: flow(function* () {\n            yield promise\n        }), // should be (val: number) => Promise<number>\n        numberToNumber: flow(function* (val: number) {\n            yield promise\n            return val\n        }), // should be () => Promise<number>\n        voidToNumber: flow(function* () {\n            yield promise\n            return Promise.resolve(2)\n        })\n    }))\n\n    const m = M.create()\n\n    // these should compile\n    const a: void = await m.voidToVoid()\n    expect(a).toBe(undefined)\n    const b: number = await m.numberToNumber(4)\n    expect(b).toBe(4)\n    const c: number = await m.voidToNumber()\n    expect(c).toBe(2)\n    await m.voidToNumber().then(d => {\n        const _d: number = d\n        expect(_d).toBe(2)\n    })\n})\n\n/**\n * Detect explicit `any` type.\n * https://stackoverflow.com/a/55541672/4289902\n */\ntype IfAny<T, Y, N> = 0 extends 1 & T ? Y : N\n\n/**\n * Ensure that the type of the passed value is of the expected type, and is NOT the TypeScript `any` type\n */\nfunction ensureNotAnyType<TExpected, TActual>(value: IfAny<TActual, never, TExpected>) {}\n\ntest(\"yield* typings for toGeneratorFunction\", async () => {\n    const voidPromise = () => Promise.resolve()\n    const numberPromise = () => Promise.resolve(7)\n    const stringWithArgsPromise = (input1: string, input2: boolean) =>\n        Promise.resolve(\"test-result\")\n\n    const voidGen = toGeneratorFunction(voidPromise)\n    const numberGen = toGeneratorFunction(numberPromise)\n    const stringWithArgsGen = toGeneratorFunction(stringWithArgsPromise)\n\n    const M = types.model({ x: 5 }).actions(self => {\n        function* testAction() {\n            const voidResult = yield* voidGen()\n            ensureNotAnyType<void, typeof voidResult>(voidResult)\n\n            const numberResult = yield* numberGen()\n            ensureNotAnyType<number, typeof numberResult>(numberResult)\n\n            const stringResult = yield* stringWithArgsGen(\"input\", true)\n            ensureNotAnyType<string, typeof stringResult>(stringResult)\n\n            return stringResult\n        }\n\n        return {\n            testAction: flow(testAction)\n        }\n    })\n\n    const m = M.create()\n\n    const result = await m.testAction()\n    ensureNotAnyType<string, typeof result>(result)\n    expect(result).toBe(\"test-result\")\n})\n\ntest(\"yield* typings for toGenerator\", async () => {\n    const voidPromise = () => Promise.resolve()\n    const numberPromise = () => Promise.resolve(7)\n    const stringWithArgsPromise = (input1: string, input2: boolean) =>\n        Promise.resolve(\"test-result\")\n\n    const M = types.model({ x: 5 }).actions(self => {\n        function* testAction() {\n            const voidResult = yield* toGenerator(voidPromise())\n            ensureNotAnyType<void, typeof voidResult>(voidResult)\n\n            const numberResult = yield* toGenerator(numberPromise())\n            ensureNotAnyType<number, typeof numberResult>(numberResult)\n\n            const stringResult = yield* toGenerator(stringWithArgsPromise(\"input\", true))\n            ensureNotAnyType<string, typeof stringResult>(stringResult)\n\n            return stringResult\n        }\n\n        return {\n            testAction: flow(testAction)\n        }\n    })\n\n    const m = M.create()\n\n    const result = await m.testAction()\n    ensureNotAnyType<string, typeof result>(result)\n    expect(result).toBe(\"test-result\")\n})\n"
  },
  {
    "path": "__tests__/core/bigint.test.ts",
    "content": "import { t } from \"../../src\"\nimport { Hook, NodeLifeCycle } from \"../../src/internal\"\nimport { describe, it, expect, test } from \"bun:test\"\n\ndescribe(\"types.bigint\", () => {\n    describe(\"methods\", () => {\n        describe(\"create\", () => {\n            describe(\"with no arguments\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    it(\"should throw an error in development\", () => {\n                        expect(() => {\n                            t.bigint.create()\n                        }).toThrow()\n                    })\n                }\n            })\n            describe(\"with a bigint argument\", () => {\n                it(\"should return a bigint\", () => {\n                    const n = t.bigint.create(BigInt(1))\n                    expect(typeof n).toBe(\"bigint\")\n                })\n            })\n            describe(\"with a number argument\", () => {\n                it(\"should return a bigint\", () => {\n                    const n = t.bigint.create(1)\n                    expect(typeof n).toBe(\"bigint\")\n                    expect(n).toBe(BigInt(1))\n                })\n            })\n            describe(\"with a string argument\", () => {\n                it(\"should return a bigint\", () => {\n                    const n = t.bigint.create(\"2\")\n                    expect(typeof n).toBe(\"bigint\")\n                    expect(n).toBe(BigInt(2))\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                if (process.env.NODE_ENV !== \"production\") {\n                    testCases.forEach(testCase => {\n                        it(`should throw an error when passed ${JSON.stringify(testCase)}`, () => {\n                            expect(() => {\n                                t.bigint.create(testCase as any)\n                            }).toThrow()\n                        })\n                    })\n                }\n            })\n        })\n        describe(\"describe\", () => {\n            it(\"should return the value 'bigint'\", () => {\n                const description = t.bigint.describe()\n                expect(description).toBe(\"bigint\")\n            })\n        })\n        describe(\"getSnapshot\", () => {\n            it(\"should return the value as string (JSON-safe)\", () => {\n                const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                const snapshot = t.bigint.getSnapshot(n)\n                expect(snapshot).toBe(\"1\")\n                expect(typeof snapshot).toBe(\"string\")\n            })\n        })\n        describe(\"getSubtype\", () => {\n            it(\"should return null\", () => {\n                const subtype = t.bigint.getSubTypes()\n                expect(subtype).toBe(null)\n            })\n        })\n        describe(\"instantiate\", () => {\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"with invalid arguments\", () => {\n                    it(\"should throw when passed undefined\", () => {\n                        expect(() => {\n                            t.bigint.instantiate(null, \"\", {}, undefined as any)\n                        }).toThrow()\n                    })\n                })\n            }\n            describe(\"with a bigint argument\", () => {\n                it(\"should return an object\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    expect(typeof n).toBe(\"object\")\n                })\n            })\n        })\n        describe(\"is\", () => {\n            describe(\"with a bigint argument\", () => {\n                it(\"should return true\", () => {\n                    const result = t.bigint.is(BigInt(1))\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = t.bigint.is(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n            describe(\"with a string argument\", () => {\n                it(\"should return true (string is valid snapshot input)\", () => {\n                    expect(t.bigint.is(\"1\")).toBe(true)\n                })\n            })\n            describe(\"with a number argument\", () => {\n                it(\"should return true (number is valid snapshot input)\", () => {\n                    expect(t.bigint.is(1)).toBe(true)\n                })\n            })\n        })\n        describe(\"isAssignableFrom\", () => {\n            describe(\"with a bigint argument\", () => {\n                it(\"should return true\", () => {\n                    const result = t.bigint.isAssignableFrom(t.bigint)\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    t.Date,\n                    t.boolean,\n                    t.finite,\n                    t.float,\n                    t.identifier,\n                    t.identifierNumber,\n                    t.integer,\n                    t.null,\n                    t.string,\n                    t.undefined\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = t.bigint.isAssignableFrom(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        describe(\"validate\", () => {\n            describe(\"with a bigint, string or number argument\", () => {\n                it(\"should return with no validation errors for bigint\", () => {\n                    const result = t.bigint.validate(BigInt(1), [])\n                    expect(result).toEqual([])\n                })\n                it(\"should return with no validation errors for string\", () => {\n                    const result = t.bigint.validate(\"1\", [])\n                    expect(result).toEqual([])\n                })\n                it(\"should return with no validation errors for number\", () => {\n                    const result = t.bigint.validate(1, [])\n                    expect(result).toEqual([])\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return with a validation error when passed ${JSON.stringify(\n                        testCase\n                    )}`, () => {\n                        const result = t.bigint.validate(testCase as any, [])\n                        expect(result).toEqual([\n                            {\n                                context: [],\n                                message: \"Value is not a bigint\",\n                                value: testCase\n                            }\n                        ])\n                    })\n                })\n            })\n        })\n    })\n    describe(\"properties\", () => {\n        describe(\"flags\", () => {\n            test(\"return the correct value\", () => {\n                const flags = t.bigint.flags\n                expect(flags).toBe(1 << 23)\n            })\n        })\n        describe(\"identifierAttribute\", () => {\n            test(\"returns undefined\", () => {\n                const identifierAttribute = t.bigint.identifierAttribute\n                expect(identifierAttribute).toBeUndefined()\n            })\n        })\n        describe(\"isType\", () => {\n            test(\"returns true\", () => {\n                const isType = t.bigint.isType\n                expect(isType).toBe(true)\n            })\n        })\n        describe(\"name\", () => {\n            test('returns \"bigint\"', () => {\n                const name = t.bigint.name\n                expect(name).toBe(\"bigint\")\n            })\n        })\n    })\n    describe(\"instance\", () => {\n        describe(\"methods\", () => {\n            describe(\"aboutToDie\", () => {\n                it(\"calls the beforeDetach hook\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    let called = false\n                    n.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n                    n.aboutToDie()\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"die\", () => {\n                it(\"kills the node\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    n.die()\n                    expect(n.isAlive).toBe(false)\n                })\n                it(\"should mark the node as dead\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    n.die()\n                    expect(n.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"finalizeCreation\", () => {\n                it(\"should mark the node as finalized\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    n.finalizeCreation()\n                    expect(n.state).toBe(NodeLifeCycle.FINALIZED)\n                })\n            })\n            describe(\"finalizeDeath\", () => {\n                it(\"should mark the node as dead\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    n.finalizeDeath()\n                    expect(n.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"getReconciliationType\", () => {\n                it(\"should return the correct type\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    const type = n.getReconciliationType()\n                    expect(type).toBe(t.bigint)\n                })\n            })\n            describe(\"getSnapshot\", () => {\n                it(\"should return the value as string (JSON-safe)\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    const snapshot = n.getSnapshot()\n                    expect(snapshot).toBe(\"1\")\n                    expect(typeof snapshot).toBe(\"string\")\n                })\n            })\n            describe(\"registerHook\", () => {\n                it(\"should register a hook and call it\", () => {\n                    const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                    let called = false\n                    n.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n\n                    n.die()\n\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"setParent\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    describe(\"with null\", () => {\n                        it(\"should throw an error\", () => {\n                            const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n                            expect(() => {\n                                n.setParent(null, \"foo\")\n                            }).toThrow()\n                        })\n                    })\n                    describe(\"with a parent object\", () => {\n                        it(\"should throw an error\", () => {\n                            const Parent = t.model({\n                                child: t.bigint\n                            })\n\n                            const parent = Parent.create({ child: BigInt(1) })\n\n                            const n = t.bigint.instantiate(null, \"\", {}, BigInt(1))\n\n                            expect(() => {\n                                // @ts-ignore\n                                n.setParent(parent, \"bar\")\n                            }).toThrow(\n                                \"[mobx-state-tree] assertion failed: scalar nodes cannot change their parent\"\n                            )\n                        })\n                    })\n                }\n            })\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/boolean.test.ts",
    "content": "import { t } from \"../../src\"\nimport { Hook, NodeLifeCycle } from \"../../src/internal\"\nimport { describe, expect, it, test } from \"bun:test\"\n\ndescribe(\"types.boolean\", () => {\n    describe(\"methods\", () => {\n        describe(\"create\", () => {\n            describe(\"with no arguments\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    it(\"should throw an error in development\", () => {\n                        expect(() => {\n                            t.boolean.create()\n                        }).toThrow()\n                    })\n                }\n            })\n            describe(\"with a boolean argument\", () => {\n                it(\"should return a boolean\", () => {\n                    const n = t.boolean.create(true)\n                    expect(typeof n).toBe(\"boolean\")\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    \"string\",\n                    1,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error(),\n                    Infinity,\n                    NaN\n                ]\n\n                if (process.env.NODE_ENV !== \"production\") {\n                    testCases.forEach(testCase => {\n                        it(`should throw an error when passed ${JSON.stringify(testCase)}`, () => {\n                            expect(() => {\n                                t.boolean.create(testCase as any)\n                            }).toThrow()\n                        })\n                    })\n                }\n            })\n        })\n        describe(\"describe\", () => {\n            it(\"should return the value 'boolean'\", () => {\n                const description = t.boolean.describe()\n                expect(description).toBe(\"boolean\")\n            })\n        })\n        describe(\"getSnapshot\", () => {\n            it(\"should return the value passed in\", () => {\n                const b = t.boolean.instantiate(null, \"\", {}, true)\n                const snapshot = t.boolean.getSnapshot(b)\n                expect(snapshot).toBe(true)\n            })\n        })\n        describe(\"getSubtype\", () => {\n            it(\"should return null\", () => {\n                const subtype = t.boolean.getSubTypes()\n                expect(subtype).toBe(null)\n            })\n        })\n        describe(\"instantiate\", () => {\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"with invalid arguments\", () => {\n                    it(\"should not throw an error\", () => {\n                        expect(() => {\n                            // @ts-ignore\n                            t.boolean.instantiate()\n                        }).not.toThrow()\n                    })\n                })\n            }\n            describe(\"with a boolean argument\", () => {\n                it(\"should return an object\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    expect(typeof b).toBe(\"object\")\n                })\n            })\n        })\n        describe(\"is\", () => {\n            describe(\"with a boolean argument\", () => {\n                it(\"should return true\", () => {\n                    const result = t.boolean.is(true)\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    \"string\",\n                    1,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error(),\n                    Infinity,\n                    NaN\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = t.boolean.is(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        describe(\"isAssignableFrom\", () => {\n            describe(\"with a boolean argument\", () => {\n                it(\"should return true\", () => {\n                    const result = t.boolean.isAssignableFrom(t.boolean)\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    t.Date,\n                    t.number,\n                    t.finite,\n                    t.float,\n                    t.identifier,\n                    t.identifierNumber,\n                    t.integer,\n                    t.null,\n                    t.string,\n                    t.undefined\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = t.boolean.isAssignableFrom(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        // TODO: we need to test this, but to be honest I'm not sure what the expected behavior is on single boolean nodes.\n        describe.skip(\"reconcile\", () => {})\n        describe(\"validate\", () => {\n            describe(\"with a boolean argument\", () => {\n                it(\"should return with no validation errors\", () => {\n                    const result = t.boolean.validate(true, [])\n                    expect(result).toEqual([])\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    \"string\",\n                    1,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error(),\n                    Infinity,\n                    NaN\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return with a validation error when passed ${JSON.stringify(\n                        testCase\n                    )}`, () => {\n                        const result = t.boolean.validate(testCase as any, [])\n                        expect(result).toEqual([\n                            {\n                                context: [],\n                                message: \"Value is not a boolean\",\n                                value: testCase\n                            }\n                        ])\n                    })\n                })\n            })\n        })\n    })\n    describe(\"properties\", () => {\n        describe(\"flags\", () => {\n            test(\"return the correct value\", () => {\n                const flags = t.boolean.flags\n                expect(flags).toBe(4)\n            })\n        })\n        describe(\"identifierAttribute\", () => {\n            // We don't have a way to set the identifierAttribute on a primitive type, so this should return undefined.\n            test(\"returns undefined\", () => {\n                const identifierAttribute = t.boolean.identifierAttribute\n                expect(identifierAttribute).toBeUndefined()\n            })\n        })\n        describe(\"isType\", () => {\n            test(\"returns true\", () => {\n                const isType = t.boolean.isType\n                expect(isType).toBe(true)\n            })\n        })\n        describe(\"name\", () => {\n            test('returns \"boolean\"', () => {\n                const name = t.boolean.name\n                expect(name).toBe(\"boolean\")\n            })\n        })\n    })\n    describe(\"instance\", () => {\n        describe(\"methods\", () => {\n            describe(\"aboutToDie\", () => {\n                it(\"calls the beforeDetach hook\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    let called = false\n                    b.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n                    b.aboutToDie()\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"die\", () => {\n                it(\"kills the node\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    b.die()\n                    expect(b.isAlive).toBe(false)\n                })\n                it(\"should mark the node as dead\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    b.die()\n                    expect(b.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"finalizeCreation\", () => {\n                it(\"should mark the node as finalized\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    b.finalizeCreation()\n                    expect(b.state).toBe(NodeLifeCycle.FINALIZED)\n                })\n            })\n            describe(\"finalizeDeath\", () => {\n                it(\"should mark the node as dead\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    b.finalizeDeath()\n                    expect(b.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"getReconciliationType\", () => {\n                it(\"should return the correct type\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    const type = b.getReconciliationType()\n                    expect(type).toBe(t.boolean)\n                })\n            })\n            describe(\"getSnapshot\", () => {\n                it(\"should return the value passed in\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    const snapshot = b.getSnapshot()\n                    expect(snapshot).toBe(true)\n                })\n            })\n            describe(\"registerHook\", () => {\n                it(\"should register a hook and call it\", () => {\n                    const b = t.boolean.instantiate(null, \"\", {}, true)\n                    let called = false\n                    b.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n\n                    b.die()\n\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"setParent\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    describe(\"with null\", () => {\n                        it(\"should throw an error\", () => {\n                            const b = t.boolean.instantiate(null, \"\", {}, true)\n                            expect(() => {\n                                b.setParent(null, \"foo\")\n                            }).toThrow()\n                        })\n                    })\n                    describe(\"with a parent object\", () => {\n                        it(\"should throw an error\", () => {\n                            const Parent = t.model({\n                                child: t.boolean\n                            })\n\n                            const parent = Parent.create({ child: true })\n\n                            const b = t.boolean.instantiate(null, \"\", {}, true)\n\n                            expect(() => {\n                                // @ts-ignore\n                                b.setParent(parent, \"bar\")\n                            }).toThrow(\n                                \"[mobx-state-tree] assertion failed: scalar nodes cannot change their parent\"\n                            )\n                        })\n                    })\n                }\n            })\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/boxes-store.test.ts",
    "content": "import { values } from \"mobx\"\nimport {\n    types,\n    getParent,\n    hasParent,\n    recordPatches,\n    unprotect,\n    getSnapshot,\n    Instance\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nexport const Box = types\n    .model(\"Box\", {\n        id: types.identifier,\n        name: \"\",\n        x: 0,\n        y: 0\n    })\n    .views(self => ({\n        get width() {\n            return self.name.length * 15\n        },\n        get isSelected(): boolean {\n            if (!hasParent(self)) return false\n            return getParent<typeof Store>(getParent(self)).selection === self\n        }\n    }))\n    .actions(self => {\n        function move(dx: number, dy: number) {\n            self.x += dx\n            self.y += dy\n        }\n        function setName(newName: string) {\n            self.name = newName\n        }\n        return {\n            move,\n            setName\n        }\n    })\nexport const Arrow = types.model(\"Arrow\", {\n    id: types.identifier,\n    from: types.reference(Box),\n    to: types.reference(Box)\n})\nexport const Store = types\n    .model(\"Store\", {\n        boxes: types.map(Box),\n        arrows: types.array(Arrow),\n        selection: types.reference(Box)\n    })\n    .actions(self => {\n        function afterCreate() {\n            unprotect(self)\n        }\n        function addBox(id: string, name: string, x: number, y: number) {\n            const box = Box.create({ name, x, y, id })\n            self.boxes.put(box)\n            return box\n        }\n        function addArrow(id: string, from: string, to: string) {\n            self.arrows.push(Arrow.create({ id, from, to }))\n        }\n        function setSelection(selection: Instance<typeof Box>) {\n            self.selection = selection\n        }\n        function createBox(\n            id: string,\n            name: string,\n            x: number,\n            y: number,\n            source: Instance<typeof Box> | null | undefined,\n            arrowId: string | null\n        ) {\n            const box = addBox(id, name, x, y)\n            setSelection(box)\n            if (source) addArrow(arrowId!, source.id, box.id)\n        }\n        return {\n            afterCreate,\n            addBox,\n            addArrow,\n            setSelection,\n            createBox\n        }\n    })\nfunction createStore() {\n    return Store.create({\n        boxes: {\n            cc: { id: \"cc\", name: \"Rotterdam\", x: 100, y: 100 },\n            aa: { id: \"aa\", name: \"Bratislava\", x: 650, y: 300 }\n        },\n        arrows: [{ id: \"dd\", from: \"cc\", to: \"aa\" }],\n        selection: \"aa\"\n    })\n}\ntest(\"store is deserialized correctly\", () => {\n    const s = createStore()\n    expect(s.boxes.size).toBe(2)\n    expect(s.arrows.length).toBe(1)\n    expect(s.selection === s.boxes.get(\"aa\")).toBe(true)\n    expect(s.arrows[0].from.name).toBe(\"Rotterdam\")\n    expect(s.arrows[0].to.name).toBe(\"Bratislava\")\n    expect(values(s.boxes).map(b => b.isSelected)).toEqual([false, true])\n})\ntest(\"store emits correct patch paths\", () => {\n    const s = createStore()\n    const recorder1 = recordPatches(s)\n    const recorder2 = recordPatches(s.boxes)\n    const recorder3 = recordPatches(s.boxes.get(\"cc\")!)\n    s.arrows[0].from.x += 117\n    expect(recorder1.patches).toEqual([{ op: \"replace\", path: \"/boxes/cc/x\", value: 217 }])\n    expect(recorder2.patches).toEqual([{ op: \"replace\", path: \"/cc/x\", value: 217 }])\n    expect(recorder3.patches).toEqual([{ op: \"replace\", path: \"/x\", value: 217 }])\n})\ntest(\"box operations works correctly\", () => {\n    const s = createStore()\n    s.createBox(\"a\", \"A\", 0, 0, null, null)\n    s.createBox(\"b\", \"B\", 100, 100, s.boxes.get(\"aa\"), \"aa2b\")\n    expect(getSnapshot(s)).toEqual({\n        boxes: {\n            cc: { id: \"cc\", name: \"Rotterdam\", x: 100, y: 100 },\n            aa: { id: \"aa\", name: \"Bratislava\", x: 650, y: 300 },\n            a: { id: \"a\", name: \"A\", x: 0, y: 0 },\n            b: { id: \"b\", name: \"B\", x: 100, y: 100 }\n        },\n        arrows: [\n            { id: \"dd\", from: \"cc\", to: \"aa\" },\n            { id: \"aa2b\", from: \"aa\", to: \"b\" }\n        ],\n        selection: \"b\"\n    })\n    s.boxes.get(\"a\")!.setName(\"I'm groot\")\n    expect(getSnapshot(s)).toEqual({\n        boxes: {\n            cc: { id: \"cc\", name: \"Rotterdam\", x: 100, y: 100 },\n            aa: { id: \"aa\", name: \"Bratislava\", x: 650, y: 300 },\n            a: { id: \"a\", name: \"I'm groot\", x: 0, y: 0 },\n            b: { id: \"b\", name: \"B\", x: 100, y: 100 }\n        },\n        arrows: [\n            { id: \"dd\", from: \"cc\", to: \"aa\" },\n            { id: \"aa2b\", from: \"aa\", to: \"b\" }\n        ],\n        selection: \"b\"\n    })\n    expect(JSON.stringify(s)).toEqual(JSON.stringify(getSnapshot(s)))\n    s.boxes.get(\"a\")!.move(50, 50)\n    expect(getSnapshot(s)).toEqual({\n        boxes: {\n            cc: { id: \"cc\", name: \"Rotterdam\", x: 100, y: 100 },\n            aa: { id: \"aa\", name: \"Bratislava\", x: 650, y: 300 },\n            a: { id: \"a\", name: \"I'm groot\", x: 50, y: 50 },\n            b: { id: \"b\", name: \"B\", x: 100, y: 100 }\n        },\n        arrows: [\n            { id: \"dd\", from: \"cc\", to: \"aa\" },\n            { id: \"aa2b\", from: \"aa\", to: \"b\" }\n        ],\n        selection: \"b\"\n    })\n    expect(s.boxes.get(\"b\")!.width).toBe(15)\n    expect(Box.create({ id: \"hello\" }).isSelected).toBe(false)\n})\n"
  },
  {
    "path": "__tests__/core/circular1.test.ts",
    "content": "import { types } from \"../../src\"\nimport { LateTodo2, LateStore2 } from \"./circular2.test\"\nimport { expect, test } from \"bun:test\"\n// combine function hosting with types.late to support circular refs between files!\nexport function LateStore1() {\n    return types.model({\n        todo: types.late(LateTodo2)\n    })\n}\nexport function LateTodo1() {\n    return types.model({\n        done: types.boolean\n    })\n}\n\ntest(\"circular test 1 should work\", () => {\n    const Store1 = types.late(LateStore1)\n    const Store2 = types.late(LateStore2)\n    expect(Store1.is({})).toBe(false)\n    expect(Store1.is({ todo: { done: true } })).toBe(true)\n    const s1 = Store1.create({ todo: { done: true } })\n    expect(s1.todo.done).toBe(true)\n    expect(Store2.is({})).toBe(false)\n    expect(Store2.is({ todo: { done: true } })).toBe(true)\n    const s2 = Store2.create({ todo: { done: true } })\n    expect(s2.todo.done).toBe(true)\n})\n"
  },
  {
    "path": "__tests__/core/circular2.test.ts",
    "content": "import { types } from \"../../src\"\nimport { LateTodo1, LateStore1 } from \"./circular1.test\"\nimport { expect, test } from \"bun:test\"\n// combine function hosting with types.late to support circular refs between files!\nexport function LateTodo2() {\n    return types.model({\n        done: types.boolean\n    })\n}\nexport function LateStore2() {\n    return types.model({\n        todo: types.late(LateTodo1)\n    })\n}\n\ntest(\"circular test 2 should work\", () => {\n    const Store1 = types.late(LateStore1)\n    const Store2 = types.late(LateStore2)\n    expect(Store1.is({})).toBe(false)\n    expect(Store1.is({ todo: { done: true } })).toBe(true)\n    const s1 = Store1.create({ todo: { done: true } })\n    expect(s1.todo.done).toBe(true)\n    expect(Store2.is({})).toBe(false)\n    expect(Store2.is({ todo: { done: true } })).toBe(true)\n    const s2 = Store2.create({ todo: { done: true } })\n    expect(s2.todo.done).toBe(true)\n})\n"
  },
  {
    "path": "__tests__/core/custom-type.test.ts",
    "content": "import {\n    types,\n    recordPatches,\n    onSnapshot,\n    unprotect,\n    applySnapshot,\n    applyPatch,\n    SnapshotOut\n} from \"../../src\"\nimport { expect, jest, test } from \"bun:test\"\n\nclass Decimal {\n    public number: number\n    public fraction: number\n\n    constructor(value: string) {\n        const parts = value.split(\".\")\n        this.number = Number(parts[0])\n        this.fraction = Number(parts[1])\n    }\n\n    toNumber() {\n        return this.number + Number(\"0.\" + this.fraction)\n    }\n\n    toString() {\n        return `${this.number}.${this.fraction}`\n    }\n}\n\n{\n    const DecimalPrimitive = types.custom<string, Decimal>({\n        name: \"Decimal\",\n        fromSnapshot(value: string, env: any) {\n            if (env && env.test) env.test(value)\n            return new Decimal(value)\n        },\n        toSnapshot(value: Decimal) {\n            return value.toString()\n        },\n        isTargetType(value: string | Decimal): value is Decimal {\n            return value instanceof Decimal\n        },\n        getValidationMessage(value: string): string {\n            if (/^-?\\d+\\.\\d+$/.test(value)) return \"\" // OK\n            return `'${value}' doesn't look like a valid decimal number`\n        }\n    })\n\n    const Wallet = types.model({\n        balance: DecimalPrimitive,\n        lastTransaction: types.maybeNull(DecimalPrimitive)\n    })\n\n    test(\"it should allow for custom primitive types\", () => {\n        const w1 = Wallet.create({\n            balance: new Decimal(\"2.5\")\n        })\n\n        expect(w1.balance.number).toBe(2)\n        expect(w1.balance.fraction).toBe(5)\n\n        const w2 = Wallet.create({ balance: \"3.5\" })\n        expect(w2.balance.number).toBe(3)\n        expect(w2.balance.fraction).toBe(5)\n\n        if (process.env.NODE_ENV !== \"production\")\n            expect(() => Wallet.create({ balance: \"two point one\" })).toThrow(\n                \"(Invalid value for type 'Decimal': 'two point one' doesn't look like a valid decimal number)\"\n            )\n    })\n\n    // test reassignment / reconcilation / conversion works\n    test(\"reassignments will work\", () => {\n        const w1 = Wallet.create({ balance: \"2.5\" })\n        unprotect(w1)\n\n        const p = recordPatches(w1)\n        const snapshots: SnapshotOut<typeof Wallet>[] = []\n        onSnapshot(w1, s => {\n            snapshots.push(s)\n        })\n\n        const b1 = w1.balance\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.balance = \"2.5\" as any // TODO: make cast work with custom types\n        expect(b1).toBeInstanceOf(Decimal)\n        expect(w1.balance).toBe(b1) // reconciled\n\n        w1.balance = new Decimal(\"2.5\") // not reconciling! // TODO: introduce custom hook for that?\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.balance = new Decimal(\"3.5\")\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.balance = \"4.5\" as any\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.lastTransaction = b1\n        expect(w1.lastTransaction).toBe(b1)\n\n        w1.lastTransaction = null\n        expect(w1.lastTransaction).toBe(null)\n\n        // patches & snapshots\n        expect(snapshots).toMatchSnapshot()\n        p.stop()\n        expect(p.patches).toMatchSnapshot()\n    })\n\n    test(\"passes environment to fromSnapshot\", () => {\n        const env = { test: jest.fn() }\n        Wallet.create({ balance: \"3.0\" }, env)\n        expect(env.test).toHaveBeenCalledWith(\"3.0\")\n    })\n}\n\n{\n    test(\"complex representation\", () => {})\n\n    const DecimalTuple = types.custom<[number, number], Decimal>({\n        name: \"DecimalTuple\",\n        fromSnapshot(value: [number, number]) {\n            return new Decimal(value[0] + \".\" + value[1])\n        },\n        toSnapshot(value: Decimal) {\n            return [value.number, value.fraction]\n        },\n        isTargetType(value: [number, number] | Decimal): value is Decimal {\n            return value instanceof Decimal\n        },\n        getValidationMessage(value: [number, number]): string {\n            if (Array.isArray(value) && value.length === 2) return \"\" // OK\n            return `'${JSON.stringify(value)}' doesn't look like a valid decimal number`\n        }\n    })\n\n    const Wallet = types.model({ balance: DecimalTuple })\n\n    test(\"it should allow for complex custom primitive types\", () => {\n        const w1 = Wallet.create({\n            balance: new Decimal(\"2.5\")\n        })\n\n        expect(w1.balance.number).toBe(2)\n        expect(w1.balance.fraction).toBe(5)\n\n        const w2 = Wallet.create({ balance: [3, 5] })\n        expect(w2.balance.number).toBe(3)\n        expect(w2.balance.fraction).toBe(5)\n\n        if (process.env.NODE_ENV !== \"production\")\n            expect(() => Wallet.create({ balance: \"two point one\" } as any)).toThrow(\n                \"(Invalid value for type 'DecimalTuple': '\\\"two point one\\\"' doesn't look like a valid decimal number)\"\n            )\n    })\n\n    // test reassignment / reconcilation / conversion works\n    test(\"complex reassignments will work\", () => {\n        const w1 = Wallet.create({ balance: [2, 5] })\n        unprotect(w1)\n\n        const p = recordPatches(w1)\n        const snapshots: SnapshotOut<typeof Wallet>[] = []\n        onSnapshot(w1, s => {\n            snapshots.push(s)\n        })\n\n        const b1 = w1.balance\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.balance = [2, 5] as any\n        expect(b1).toBeInstanceOf(Decimal)\n        expect(w1.balance).not.toBe(b1) // not reconciled, balance is not deep equaled (TODO: future feature?)\n\n        w1.balance = new Decimal(\"2.5\") // not reconciling!\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.balance = new Decimal(\"3.5\")\n        expect(b1).toBeInstanceOf(Decimal)\n\n        w1.balance = [4, 5] as any\n        expect(b1).toBeInstanceOf(Decimal)\n\n        // patches & snapshots\n        expect(snapshots).toMatchSnapshot()\n        p.stop()\n        expect(p.patches).toMatchSnapshot()\n    })\n\n    test(\"can apply snapshot and patch\", () => {\n        const w1 = Wallet.create({ balance: [3, 0] })\n        applySnapshot(w1, { balance: [4, 5] })\n        expect(w1.balance).toBeInstanceOf(Decimal)\n        expect(w1.balance.toString()).toBe(\"4.5\")\n\n        applyPatch(w1, {\n            op: \"replace\",\n            path: \"/balance\",\n            value: [5, 0]\n        })\n        expect(w1.balance.toString()).toBe(\"5.0\")\n    })\n}\n"
  },
  {
    "path": "__tests__/core/date.test.ts",
    "content": "import { t } from \"../../src\"\nimport { Hook, NodeLifeCycle } from \"../../src/internal\"\nimport { describe, expect, it, test } from \"bun:test\"\n\ndescribe(\"types.date\", () => {\n    describe(\"methods\", () => {\n        describe(\"create\", () => {\n            describe(\"with no arguments\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    it(\"should throw an error in development\", () => {\n                        expect(() => {\n                            t.Date.create()\n                        }).toThrow()\n                    })\n                }\n            })\n            describe(\"with a number argument\", () => {\n                it(\"should return a Date object\", () => {\n                    const d = t.Date.create(1701369873059)\n                    expect(d instanceof Date).toBe(true)\n                })\n            })\n            describe(\"with a Date argument\", () => {\n                it(\"should return a Date object\", () => {\n                    const input = new Date()\n                    const d = t.Date.create(input)\n                    expect(d instanceof Date).toBe(true)\n                })\n            })\n        })\n        describe(\"with argument of different types\", () => {\n            const testCases = [\n                null,\n                undefined,\n                true,\n                [],\n                function () {},\n                \"2022-01-01T00:00:00.000Z\",\n                /a/,\n                new Map(),\n                new Set(),\n                Symbol(),\n                new Error()\n            ]\n\n            if (process.env.NODE_ENV !== \"production\") {\n                testCases.forEach(testCase => {\n                    it(`should throw an error when passed ${JSON.stringify(testCase)}`, () => {\n                        expect(() => {\n                            t.Date.create(testCase as any)\n                        }).toThrow()\n                    })\n                })\n            }\n        })\n    })\n    describe(\"describe\", () => {\n        it(\"should return the value 'Date'\", () => {\n            const description = t.Date.describe()\n            expect(description).toBe(\"Date\")\n        })\n    })\n    describe(\"getSnapshot\", () => {\n        it(\"should return a number from a date\", () => {\n            const date = new Date(\"2022-01-01T00:00:00.000Z\")\n            const d = t.Date.instantiate(null, \"\", {}, date)\n            const snapshot = t.Date.getSnapshot(d)\n            expect(snapshot).toBe(1640995200000)\n        })\n        it(\"should return a number from a number\", () => {\n            const d = t.Date.instantiate(null, \"\", {}, 1701369873059)\n            const snapshot = t.Date.getSnapshot(d)\n            expect(snapshot).toBe(1701369873059)\n        })\n    })\n    describe(\"getSubtype\", () => {\n        it(\"should return null\", () => {\n            const subtype = t.Date.getSubTypes()\n            expect(subtype).toBe(null)\n        })\n    })\n    describe(\"instantiate\", () => {\n        if (process.env.NODE_ENV !== \"production\") {\n            describe(\"with invalid arguments\", () => {\n                it(\"should not throw an error\", () => {\n                    expect(() => {\n                        // @ts-ignore\n                        t.Date.instantiate()\n                    }).not.toThrow()\n                })\n            })\n        }\n        describe(\"with a Date argument\", () => {\n            it(\"should return an object\", () => {\n                const s = t.Date.instantiate(null, \"\", {}, new Date())\n                expect(typeof s).toBe(\"object\")\n            })\n        })\n        describe(\"with a number argument\", () => {\n            it(\"should return an object\", () => {\n                const s = t.Date.instantiate(null, \"\", {}, 1701369873059)\n                expect(typeof s).toBe(\"object\")\n            })\n        })\n    })\n    describe(\"is\", () => {\n        describe(\"with a Date argument\", () => {\n            it(\"should return true\", () => {\n                const result = t.Date.is(new Date())\n                expect(result).toBe(true)\n            })\n        })\n        describe(\"with a number argument\", () => {\n            it(\"should return true\", () => {\n                const result = t.Date.is(1701369873059)\n                expect(result).toBe(true)\n            })\n        })\n        describe(\"with argument of different types\", () => {\n            const testCases = [\n                null,\n                undefined,\n                true,\n                [],\n                function () {},\n                \"2022-01-01T00:00:00.000Z\",\n                /a/,\n                new Map(),\n                new Set(),\n                Symbol(),\n                new Error()\n            ]\n\n            testCases.forEach(testCase => {\n                it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                    const result = t.Date.is(testCase as any)\n                    expect(result).toBe(false)\n                })\n            })\n        })\n    })\n    describe(\"isAssignableFrom\", () => {\n        describe(\"with a Date argument\", () => {\n            it(\"should return true\", () => {\n                const result = t.Date.isAssignableFrom(t.Date)\n                expect(result).toBe(true)\n            })\n        })\n        describe(\"with argument of different types\", () => {\n            const testCases = [\n                t.string,\n                t.boolean,\n                t.finite,\n                t.float,\n                t.identifier,\n                t.identifierNumber,\n                t.integer,\n                t.null,\n                t.number,\n                t.undefined\n            ]\n\n            testCases.forEach(testCase => {\n                it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                    const result = t.Date.isAssignableFrom(testCase as any)\n                    expect(result).toBe(false)\n                })\n            })\n        })\n\n        // TODO: we need to test this, but to be honest I'm not sure what the expected behavior is on single date nodes.\n        describe.skip(\"reconcile\", () => {})\n        describe(\"validate\", () => {\n            describe(\"with a Date argument\", () => {\n                it(\"should return with no validation errors\", () => {\n                    const result = t.Date.validate(new Date(), [])\n                    expect(result).toEqual([])\n                })\n            })\n            describe(\"with a number argument\", () => {\n                it(\"should return with no validation errors\", () => {\n                    const result = t.Date.validate(1701369873059, [])\n                    expect(result).toEqual([])\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    \"2022-01-01T00:00:00.000Z\",\n                    true,\n                    [],\n                    function () {},\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return with a validation error when passed ${JSON.stringify(\n                        testCase\n                    )}`, () => {\n                        const result = t.Date.validate(testCase as any, [])\n                        expect(result).toEqual([\n                            {\n                                context: [],\n                                message: \"Value is not a Date or a unix milliseconds timestamp\",\n                                value: testCase\n                            }\n                        ])\n                    })\n                })\n            })\n        })\n    })\n    describe(\"properties\", () => {\n        describe(\"flags\", () => {\n            test(\"return the correct value\", () => {\n                const flags = t.Date.flags\n                expect(flags).toBe(8)\n            })\n        })\n\n        describe(\"identifierAttribute\", () => {\n            // We don't have a way to set the identifierAttribute on a primitive type, so this should return undefined.\n            test(\"returns undefined\", () => {\n                const identifierAttribute = t.Date.identifierAttribute\n                expect(identifierAttribute).toBeUndefined()\n            })\n        })\n        describe(\"isType\", () => {\n            test(\"returns true\", () => {\n                const isType = t.Date.isType\n                expect(isType).toBe(true)\n            })\n        })\n        describe(\"name\", () => {\n            test('returns \"Date\"', () => {\n                const name = t.Date.name\n                expect(name).toBe(\"Date\")\n            })\n        })\n    })\n    describe(\"instance\", () => {\n        describe(\"methods\", () => {\n            describe(\"aboutToDie\", () => {\n                it(\"calls the beforeDetach hook\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    let called = false\n                    d.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n                    d.aboutToDie()\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"die\", () => {\n                it(\"kills the node\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    d.die()\n                    expect(d.isAlive).toBe(false)\n                })\n                it(\"should mark the node as dead\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    d.die()\n                    expect(d.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"finalizeCreation\", () => {\n                it(\"should mark the node as finalized\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    d.finalizeCreation()\n                    expect(d.state).toBe(NodeLifeCycle.FINALIZED)\n                })\n            })\n            describe(\"finalizeDeath\", () => {\n                it(\"should mark the node as dead\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    d.finalizeDeath()\n                    expect(d.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"getReconciliationType\", () => {\n                it(\"should return the correct type\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    const type = d.getReconciliationType()\n                    expect(type).toBe(t.Date)\n                })\n            })\n            describe(\"getSnapshot\", () => {\n                it(\"should return the value passed in with a number\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, 1701373349399)\n                    const snapshot = d.getSnapshot()\n                    expect(snapshot).toBe(1701373349399)\n                })\n\n                it(\"should return the correct date getTime value with a date object\", () => {\n                    const date = new Date(\"2022-01-01T00:00:00.000Z\")\n                    const d = t.Date.instantiate(null, \"\", {}, date)\n                    const snapshot = d.getSnapshot()\n                    const expected = date.getTime()\n                    expect(snapshot).toBe(expected)\n                })\n            })\n            describe(\"registerHook\", () => {\n                it(\"should register a hook and call it\", () => {\n                    const d = t.Date.instantiate(null, \"\", {}, new Date())\n                    let called = false\n                    d.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n\n                    d.die()\n\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"setParent\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    describe(\"with null\", () => {\n                        it(\"should throw an error\", () => {\n                            const d = t.Date.instantiate(null, \"\", {}, new Date())\n                            expect(() => {\n                                d.setParent(null, \"foo\")\n                            }).toThrow()\n                        })\n                    })\n                    describe(\"with a parent object\", () => {\n                        it(\"should throw an error\", () => {\n                            const Parent = t.model({\n                                child: t.Date\n                            })\n\n                            const parent = Parent.create({ child: new Date() })\n\n                            const d = t.Date.instantiate(null, \"\", {}, new Date())\n\n                            expect(() => {\n                                // @ts-ignore\n                                d.setParent(parent, \"bar\")\n                            }).toThrow(\n                                \"[mobx-state-tree] assertion failed: scalar nodes cannot change their parent\"\n                            )\n                        })\n                    })\n                }\n            })\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/deprecated.test.ts",
    "content": "import { deprecated } from \"../../src/utils\"\nimport { flow, createFlowSpawner } from \"../../src/core/flow\"\nimport { process as mstProcess, createProcessSpawner } from \"../../src/core/process\"\nimport { expect, jest, test } from \"bun:test\"\n\nfunction createDeprecationListener() {\n    // clear previous deprecation dedupe keys\n    deprecated.ids = {}\n    // save console.warn native implementation\n    const originalWarn = console.warn\n    // create spy to track warning call\n    const spyWarn = (console.warn = jest.fn())\n    // return callback to check if warn was called properly\n    return function isDeprecated() {\n        // replace original implementation\n        console.warn = originalWarn\n        // test for correct log message, if in development\n        if (process.env.NODE_ENV !== \"production\") {\n            expect(spyWarn).toHaveBeenCalledTimes(1)\n            expect(spyWarn.mock.calls[0][0].message).toMatch(/Deprecation warning:/)\n        }\n    }\n}\ntest(\"`process` should mirror `flow`\", () => {\n    const isDeprecated = createDeprecationListener()\n    const generator = function* () {}\n    const flowResult = flow(generator)\n    const processResult = mstProcess(generator)\n    expect(processResult.name).toBe(flowResult.name)\n    isDeprecated()\n})\ntest(\"`createProcessSpawner` should mirror `createFlowSpawner`\", () => {\n    const isDeprecated = createDeprecationListener()\n    const alias = \"generatorAlias\"\n    const generator = function* (): IterableIterator<void> {}\n    const flowSpawnerResult = createFlowSpawner(alias, generator)\n    const processSpawnerResult = createProcessSpawner(alias, generator)\n    expect(processSpawnerResult.name).toBe(flowSpawnerResult.name)\n    isDeprecated()\n})\n"
  },
  {
    "path": "__tests__/core/enum.test.ts",
    "content": "import { types, unprotect } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nenum ColorEnum {\n    Red = \"Red\",\n    Orange = \"Orange\",\n    Green = \"Green\"\n}\nconst colorEnumValues = Object.values(ColorEnum) as ColorEnum[]\n\ntest(\"should support enums\", () => {\n    const TrafficLight = types.model({ color: types.enumeration(\"Color\", colorEnumValues) })\n    expect(TrafficLight.is({ color: ColorEnum.Green })).toBe(true)\n    expect(TrafficLight.is({ color: \"Blue\" })).toBe(false)\n    expect(TrafficLight.is({ color: undefined })).toBe(false)\n    const l = TrafficLight.create({ color: ColorEnum.Orange })\n    unprotect(l)\n    l.color = ColorEnum.Red\n    expect(TrafficLight.describe()).toBe('{ color: (\"Red\" | \"Orange\" | \"Green\") }')\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => (l.color = \"Blue\" as any)).toThrow(\n            /Error while converting `\"Blue\"` to `Color`/\n        )\n    }\n})\ntest(\"should support anonymous enums\", () => {\n    const TrafficLight = types.model({ color: types.enumeration(colorEnumValues) })\n    const l = TrafficLight.create({ color: ColorEnum.Orange })\n    unprotect(l)\n    l.color = ColorEnum.Red\n    expect(TrafficLight.describe()).toBe('{ color: (\"Red\" | \"Orange\" | \"Green\") }')\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => (l.color = \"Blue\" as any)).toThrow(\n            /Error while converting `\"Blue\"` to `\"Red\" | \"Orange\" | \"Green\"`/\n        )\n    }\n})\ntest(\"should support optional enums\", () => {\n    const TrafficLight = types.optional(types.enumeration(colorEnumValues), ColorEnum.Orange)\n    const l = TrafficLight.create()\n    expect(l).toBe(ColorEnum.Orange)\n})\ntest(\"should support optional enums inside a model\", () => {\n    const TrafficLight = types.model({\n        color: types.optional(types.enumeration(colorEnumValues), ColorEnum.Orange)\n    })\n    const l = TrafficLight.create({})\n    expect(l.color).toBe(ColorEnum.Orange)\n})\ntest(\"should support plain string[] arrays\", () => {\n    const colorOptions: string[] = [\"Red\", \"Orange\", \"Green\"]\n    const TrafficLight = types.model({ color: types.enumeration(colorOptions) })\n    const l = TrafficLight.create({ color: \"Orange\" })\n    unprotect(l)\n    l.color = \"Red\"\n    expect(TrafficLight.describe()).toBe('{ color: (\"Red\" | \"Orange\" | \"Green\") }')\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => (l.color = \"Blue\" as any)).toThrow(\n            /Error while converting `\"Blue\"` to `\"Red\" | \"Orange\" | \"Green\"`/\n        )\n    }\n})\ntest(\"should support readonly enums as const\", () => {\n    const colorOptions = [\"Red\", \"Orange\", \"Green\"] as const\n    const TrafficLight = types.model({ color: types.enumeration(colorOptions) })\n    const l = TrafficLight.create({ color: \"Orange\" })\n    unprotect(l)\n    l.color = \"Red\"\n    expect(TrafficLight.describe()).toBe('{ color: (\"Red\" | \"Orange\" | \"Green\") }')\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => (l.color = \"Blue\" as any)).toThrow(\n            /Error while converting `\"Blue\"` to `\"Red\" | \"Orange\" | \"Green\"`/\n        )\n    }\n})\n"
  },
  {
    "path": "__tests__/core/env.test.ts",
    "content": "import { configure } from \"mobx\"\nimport {\n    types,\n    hasEnv,\n    getEnv,\n    clone,\n    detach,\n    unprotect,\n    walk,\n    getPath,\n    castToSnapshot,\n    hasParent,\n    Instance,\n    destroy,\n    getParent,\n    IAnyStateTreeNode,\n    isStateTreeNode,\n    isAlive\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\n// tslint:disable: no-unused-expression\n\nconst Todo = types\n    .model({\n        title: \"test\"\n    })\n    .views(self => ({\n        get description() {\n            return getEnv(self).useUppercase ? self.title.toUpperCase() : self.title\n        }\n    }))\nconst Store = types.model({\n    todos: types.array(Todo)\n})\n\nfunction createEnvironment() {\n    return {\n        useUppercase: true\n    }\n}\n\ntest(\"it should be possible to use environments\", () => {\n    const env = createEnvironment()\n    const todo = Todo.create({}, env)\n    expect(hasEnv(todo)).toBe(true)\n    expect(todo.description).toBe(\"TEST\")\n    env.useUppercase = false\n    expect(todo.description).toBe(\"test\")\n})\ntest(\"it should be possible to inherit environments\", () => {\n    const env = createEnvironment()\n    const store = Store.create({ todos: [{}] }, env)\n    expect(hasEnv(store.todos[0])).toBe(true)\n    expect(store.todos[0].description).toBe(\"TEST\")\n    env.useUppercase = false\n    expect(store.todos[0].description).toBe(\"test\")\n})\ntest(\"getEnv throws error without environment\", () => {\n    const todo = Todo.create()\n    expect(hasEnv(todo)).toBe(false)\n    expect(() => getEnv(todo)).toThrow(\"Failed to find the environment of AnonymousModel@<root>\")\n})\ntest(\"detach should preserve environment\", () => {\n    const env = createEnvironment()\n    const store = Store.create({ todos: [{}] }, env)\n    unprotect(store)\n    const todo = detach(store.todos[0])\n    expect(hasEnv(todo)).toBe(true)\n    expect(todo.description).toBe(\"TEST\")\n    env.useUppercase = false\n    expect(todo.description).toBe(\"test\")\n})\ntest(\"it is possible to assign instance with the same environment as the parent to a tree\", () => {\n    const env = createEnvironment()\n    const store = Store.create({ todos: [] }, env)\n    const todo = Todo.create({}, env)\n    unprotect(store)\n    store.todos.push(todo)\n    expect(store.todos.length === 1).toBe(true)\n    expect(getEnv(store.todos) === getEnv(store.todos[0])).toBe(true)\n    expect(getEnv(todo) === getEnv(store.todos[0])).toBe(true)\n})\ntest(\"it is not possible to assign instance with a different environment than the parent to a tree\", () => {\n    if (process.env.NODE_ENV !== \"production\") {\n        const env1 = createEnvironment()\n        const env2 = createEnvironment()\n        const store = Store.create({ todos: [] }, env1)\n        const todo = Todo.create({}, env2)\n        unprotect(store)\n        expect(() => store.todos.push(todo)).toThrow(\n            \"[mobx-state-tree] A state tree cannot be made part of another state tree as long as their environments are different.\"\n        )\n    }\n})\ntest(\"it is possible to set a value inside a map of a map when using the same environment\", () => {\n    const env = createEnvironment()\n    const EmptyModel = types.model({})\n    const MapOfEmptyModel = types.model({\n        map: types.map(EmptyModel)\n    })\n    const MapOfMapOfEmptyModel = types.model({\n        map: types.map(MapOfEmptyModel)\n    })\n    const mapOfMap = MapOfMapOfEmptyModel.create(\n        {\n            map: {\n                whatever: {\n                    map: {}\n                }\n            }\n        },\n        env\n    )\n    unprotect(mapOfMap)\n    // this should not throw\n    mapOfMap.map.get(\"whatever\")!.map.set(\"1234\", EmptyModel.create({}, env))\n    expect(getEnv(mapOfMap) === env).toBe(true)\n    expect(getEnv(mapOfMap.map.get(\"whatever\")!.map.get(\"1234\")!) === env).toBe(true)\n})\ntest(\"clone preserves environment\", () => {\n    const env = createEnvironment()\n    const store = Store.create({ todos: [{}] }, env)\n    {\n        const todo = clone(store.todos[0])\n\n        expect(getEnv(todo) === env).toBe(true)\n    }\n    {\n        const todo = clone(store.todos[0], true)\n        expect(getEnv(todo) === env).toBe(true)\n        expect(getEnv(todo) === env).toBe(true)\n    }\n    {\n        const todo = clone(store.todos[0], false)\n        expect(hasEnv(todo)).toBe(false)\n        expect(() => {\n            getEnv(todo)\n        }).toThrow(\"Failed to find the environment of AnonymousModel@<root>\")\n    }\n    {\n        const env2 = createEnvironment()\n        const todo = clone(store.todos[0], env2)\n        expect(env2 === getEnv(todo)).toBe(true)\n    }\n})\n\ntest(\"#1231\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const envObj = createEnvironment()\n    const logs: string[] = []\n\n    function nofParents(node: IAnyStateTreeNode) {\n        let parents = 0\n        let parent = node\n        while (hasParent(parent)) {\n            parents++\n            parent = getParent(parent)\n        }\n        return parents\n    }\n\n    function leafsFirst(root: IAnyStateTreeNode) {\n        const nodes: IAnyStateTreeNode[] = []\n        walk(root, i => {\n            if (isStateTreeNode(i)) {\n                nodes.push(i)\n            }\n        })\n        // sort by number of parents\n        nodes.sort((a, b) => {\n            return nofParents(b) - nofParents(a)\n        })\n        return nodes\n    }\n\n    function check(root: Instance<typeof RS>, name: string, mode: \"detach\" | \"destroy\") {\n        function logFail(operation: string, n: any) {\n            logs.push(`fail: (${name}) ${operation}: ${getPath(n)}, ${n}`)\n        }\n        function log(operation: string, n: any) {\n            logs.push(`ok: (${name}) ${operation}: ${getPath(n)}, ${n}`)\n        }\n\n        // make sure all nodes are there\n        root.s1.arr[0].title\n        root.s1.m.get(\"one\")!.title\n        root.s2\n        const nodes = leafsFirst(root)\n        expect(nodes.length).toBe(7)\n\n        nodes.forEach(i => {\n            const env = getEnv(i)\n            const parent = hasParent(i)\n            if (!parent && i !== root) {\n                logFail(\"expected a parent, but none found\", i)\n            } else {\n                log(\"had parent or was root\", i)\n            }\n            if (env !== envObj) {\n                logFail(\"expected same env as root, but was different\", i)\n            } else {\n                log(\"same env as root\", i)\n            }\n        })\n\n        unprotect(root)\n        nodes.forEach(i => {\n            const optional = optionalPaths.includes(getPath(i))\n            if (mode === \"detach\") {\n                log(\"detaching node\", i)\n                detach(i)\n            } else {\n                log(\"destroying node\", i)\n                destroy(i)\n            }\n            const env = hasEnv(i) ? getEnv(i) : undefined\n            const parent = hasParent(i)\n            const alive = isAlive(i)\n            if (mode === \"detach\") {\n                if (parent) {\n                    logFail(`expected no parent after detach, but one was found`, i)\n                } else {\n                    log(`no parent after detach`, i)\n                }\n                if (env !== envObj) {\n                    logFail(\"expected same env as root after detach, but it was not\", i)\n                } else {\n                    log(\"env kept after detach\", i)\n                }\n                if (!alive) {\n                    logFail(\"expected to be alive after detach, but it was not\", i)\n                } else {\n                    log(\"alive after detach\", i)\n                }\n            } else {\n                // destroy might or might not keep the env, but doesn't matter so we don't check\n                if (optional) {\n                    // optional (undefined) nodes will be assigned undefined and reconciled, therefore they will be kept alive\n                    if (!parent) {\n                        logFail(\n                            `expected a parent after destroy (since it is optional), but none was found`,\n                            i\n                        )\n                    } else {\n                        log(`had parent after destroy (since it is optional)`, i)\n                    }\n                    if (!alive) {\n                        logFail(\n                            \"expected to be alive after destroy (since it is optional), but it was not\",\n                            i\n                        )\n                    } else {\n                        log(\"alive after destroy (since it is optional)\", i)\n                    }\n                } else {\n                    if (parent) {\n                        logFail(`expected no parent after destroy, but one was found`, i)\n                    } else {\n                        log(`no parent after destroy`, i)\n                    }\n                    if (alive) {\n                        logFail(\"expected to be dead after destroy, but it was not\", i)\n                    } else {\n                        log(\"dead after destroy\", i)\n                    }\n                }\n            }\n        })\n    }\n\n    const T = types.model(\"T\", { title: \"some title\" })\n\n    const S1Arr = types.array(T)\n    const S1Map = types.map(T)\n    const S1 = types.model(\"S1\", {\n        arr: S1Arr,\n        m: S1Map\n    })\n\n    const S2 = types.model(\"S2\", {})\n\n    const RS = types.model(\"RS\", {\n        s1: types.optional(S1, {}),\n        s2: types.optional(S2, {})\n    })\n\n    const optionalPaths = [\"/s1\", \"/s2\", \"/s1/m\", \"/s1/arr\"]\n\n    const data = {\n        s1: castToSnapshot(\n            S1.create({\n                arr: S1Arr.create([T.create({})]),\n                m: castToSnapshot(S1Map.create({ one: T.create({}) }))\n            })\n        ),\n        s2: S2.create()\n    }\n    const rsCreate = RS.create(data, envObj)\n    const rsCreate2 = clone(rsCreate, true)\n\n    const rsSnap = RS.create(\n        {\n            s1: {\n                arr: [{}],\n                m: { one: {} }\n            },\n            s2: {}\n        },\n        envObj\n    )\n    const rsSnap2 = clone(rsCreate, true)\n\n    check(rsCreate, \"using create\", \"detach\")\n    check(rsSnap, \"using snapshot\", \"detach\")\n    check(rsCreate2, \"using create\", \"destroy\")\n    check(rsSnap2, \"using snapshot\", \"destroy\")\n\n    const fails = logs.filter(l => l.startsWith(\"fail:\"))\n    if (fails.length > 0) {\n        expect().fail(`\\n${fails.join(\"\\n\")}`)\n    }\n})\n"
  },
  {
    "path": "__tests__/core/frozen.test.ts",
    "content": "import { getSnapshot, types, unprotect } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"it should accept any serializable value\", () => {\n    const Factory = types.model({\n        value: types.frozen<{ a: number; b: number } | undefined>()\n    })\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.value = { a: 1, b: 2 }\n    expect(getSnapshot(doc)).toEqual({ value: { a: 1, b: 2 } })\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should throw if value is not serializable\", () => {\n        const Factory = types.model({\n            value: types.frozen<Function | undefined>()\n        })\n        const doc = Factory.create()\n        unprotect(doc)\n        expect(() => {\n            doc.value = function IAmUnserializable() {}\n        }).toThrow(/Error while converting <function IAmUnserializable> to `frozen`/)\n    })\n}\n\ntest(\"it should accept any default value value\", () => {\n    const Factory = types.model({\n        value: types.frozen(3)\n    })\n    const doc = Factory.create()\n    expect(Factory.is({})).toBeTruthy()\n    expect(getSnapshot(doc)).toEqual({ value: 3 })\n})\n\ntest(\"it should type strongly\", () => {\n    type Point = { x: number; y: number }\n    const Mouse = types\n        .model({\n            loc: types.frozen<Point>()\n        })\n        .actions(self => ({\n            moveABit() {\n                // self.loc.x += 1; // compile error, x is readonly!\n                ;(self.loc as any).x += 1 // throws, frozen!\n            }\n        }))\n\n    expect(Mouse.is({})).toBeTruthy() // any value is acceptable to frozen, even undefined...\n\n    const m = Mouse.create({\n        // loc: 3 // type error!\n        loc: { x: 2, y: 3 }\n    })\n\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => {\n            m.moveABit()\n        }).toThrow(\"Attempted to assign to readonly property.\")\n    }\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should be capable of using another MST type\", () => {\n        const Point = types.model(\"Point\", { x: types.number, y: types.number })\n        const Mouse = types.model({\n            loc: types.frozen(Point)\n        })\n\n        expect(Mouse.is({})).toBeFalsy()\n        expect(Mouse.is({ loc: {} })).toBeFalsy()\n        expect(Mouse.is({ loc: { x: 3, y: 2 } })).toBeTruthy()\n\n        expect(() => {\n            ;(Mouse.create as any)()\n        }).toThrow(\n            'at path \"/loc\" value `undefined` is not assignable to type: `frozen(Point)` (Value is not a plain object)'\n        )\n        expect(() => {\n            Mouse.create({ loc: { x: 4 } } as any)\n        }).toThrow(\n            'at path \"/loc/y\" value `undefined` is not assignable to type: `number` (Value is not a number)'\n        )\n\n        const m = Mouse.create({\n            loc: { x: 3, y: 2 }\n        })\n\n        const x = m.loc.x\n        expect(x).toBe(3)\n    })\n}\n"
  },
  {
    "path": "__tests__/core/hooks.test.ts",
    "content": "import {\n    addDisposer,\n    destroy,\n    detach,\n    types,\n    unprotect,\n    getSnapshot,\n    applySnapshot,\n    onSnapshot,\n    isAlive,\n    hasParent,\n    cast,\n    resolvePath,\n    getParent\n} from \"../../src\"\nimport { expect, jest, test } from \"bun:test\"\n\nfunction createTestStore(listener: (s: string) => void) {\n    const Todo = types\n        .model(\"Todo\", {\n            title: \"\"\n        })\n        .actions(self => {\n            function afterCreate() {\n                listener(\"new todo: \" + self.title)\n                addDisposer(self, () => {\n                    listener(\"custom disposer 1 for \" + self.title)\n                })\n                addDisposer(self, () => {\n                    listener(\"custom disposer 2 for \" + self.title)\n                })\n            }\n            function beforeDestroy() {\n                listener(\"destroy todo: \" + self.title)\n            }\n            function afterAttach() {\n                listener(\"attach todo: \" + self.title)\n            }\n            function beforeDetach() {\n                listener(\"detach todo: \" + self.title)\n            }\n            return {\n                afterCreate,\n                beforeDestroy,\n                afterAttach,\n                beforeDetach\n            }\n        })\n    const Store = types\n        .model(\"Store\", {\n            todos: types.array(Todo)\n        })\n        .actions(self => {\n            function afterCreate() {\n                unprotect(self)\n                listener(\"new store: \" + self.todos.length)\n                addDisposer(self, () => {\n                    listener(\"custom disposer for store\")\n                })\n            }\n            function beforeDestroy() {\n                listener(\"destroy store: \" + self.todos.length)\n            }\n            return {\n                afterCreate,\n                beforeDestroy\n            }\n        })\n    return {\n        store: Store.create({\n            todos: [{ title: \"Get coffee\" }, { title: \"Get biscuit\" }, { title: \"Give talk\" }]\n        }),\n        Store,\n        Todo\n    }\n}\n\n// NOTE: as we defer creation (and thus, hooks) till first real access,\n// some of original hooks do not fire at all\ntest(\"it should trigger lifecycle hooks\", () => {\n    const events: string[] = []\n    // new store: 3\n    const { store, Todo } = createTestStore(e => events.push(e))\n\n    events.push(\"-\")\n    // access (new, attach), then detach \"Give Talk\"\n    const talk = detach(store.todos[2])\n    expect(isAlive(talk)).toBe(true)\n    expect(hasParent(talk)).toBe(false)\n\n    events.push(\"--\")\n\n    // access (new, attach), then destroy biscuit\n    const oldBiscuit = store.todos.pop()!\n    expect(isAlive(oldBiscuit)).toBe(false)\n\n    events.push(\"---\")\n    // new and then attach \"add sugar\"\n    const sugar = Todo.create({\n        title: \"add sugar\"\n    })\n    store.todos.push(sugar)\n\n    events.push(\"----\")\n    // destroy elements in the array (\"add sugar\"), then store\n    destroy(store)\n    expect(isAlive(store)).toBe(false)\n\n    events.push(\"-----\")\n    // destroy \"Give talk\"\n    destroy(talk)\n    expect(isAlive(talk)).toBe(false)\n\n    expect(events).toEqual([\n        \"new store: 3\",\n        \"-\",\n        \"new todo: Give talk\",\n        \"attach todo: Give talk\",\n        \"detach todo: Give talk\",\n        \"--\",\n        \"new todo: Get biscuit\",\n        \"attach todo: Get biscuit\",\n        \"destroy todo: Get biscuit\",\n        \"custom disposer 2 for Get biscuit\",\n        \"custom disposer 1 for Get biscuit\",\n        \"---\",\n        \"new todo: add sugar\",\n        \"attach todo: add sugar\",\n        \"----\",\n        \"destroy todo: add sugar\",\n        \"custom disposer 2 for add sugar\",\n        \"custom disposer 1 for add sugar\",\n        \"destroy store: 2\",\n        \"custom disposer for store\",\n        \"-----\",\n        \"destroy todo: Give talk\",\n        \"custom disposer 2 for Give talk\",\n        \"custom disposer 1 for Give talk\"\n    ])\n})\n\ntest(\"lifecycle hooks can access their children\", () => {\n    const events: string[] = []\n    function listener(e: string) {\n        events.push(e)\n    }\n\n    const Child = types\n        .model(\"Todo\", {\n            title: \"\"\n        })\n        .actions(self => ({\n            afterCreate() {\n                listener(\"new child: \" + self.title)\n            },\n            afterAttach() {\n                listener(\"parent available: \" + !!getParent(self))\n            }\n        }))\n\n    const Parent = types\n        .model(\"Parent\", {\n            child: Child\n        })\n        .actions(self => ({\n            afterCreate() {\n                // **This the key line**: it is trying to access the child\n                listener(\"new parent, child.title: \" + self.child?.title)\n            }\n        }))\n\n    const Store = types.model(\"Store\", {\n        parent: types.maybe(Parent)\n    })\n\n    const store = Store.create({\n        parent: {\n            child: { title: \"Junior\" }\n        }\n    })\n    // As expected no hooks are called.\n    // The `parent` is not accessed it is just loaded.\n    events.push(\"-\")\n\n    // Simple access does a sensible thing\n    const parent = store.parent\n    expect(events).toEqual([\n        \"-\",\n        \"new child: Junior\",\n        \"new parent, child.title: Junior\",\n        \"parent available: true\"\n    ])\n\n    // Clear the events and make a new store\n    events.length = 0\n    const store2 = Store.create({\n        parent: {\n            child: { title: \"Junior\" }\n        }\n    })\n    events.push(\"-\")\n\n    // Previously resolvePath would cause problems because the parent hooks\n    // would be called before the child was fully created\n    const child = resolvePath(store2, \"/parent/child\")\n    expect(events).toEqual([\n        \"-\",\n        \"new child: Junior\",\n        \"new parent, child.title: Junior\",\n        \"parent available: true\"\n    ])\n})\n\ntype CarSnapshot = { id: string }\nconst Car = types\n    .model(\"Car\", {\n        id: types.number\n    })\n    .preProcessSnapshot<CarSnapshot>(snapshot =>\n        Object.assign({}, snapshot, { id: Number(snapshot.id) * 2 })\n    )\n    .postProcessSnapshot<CarSnapshot>(snapshot =>\n        Object.assign({}, snapshot, { id: \"\" + snapshot.id / 2 })\n    )\n\nconst Factory = types.model(\"Factory\", {\n    car: Car\n})\n\nconst Motorcycle = types\n    .model(\"Motorcycle\", {\n        id: types.string\n    })\n    .preProcessSnapshot<CarSnapshot>(snapshot =>\n        Object.assign({}, snapshot, { id: snapshot.id.toLowerCase() })\n    )\n    .postProcessSnapshot<CarSnapshot>(snapshot =>\n        Object.assign({}, snapshot, { id: snapshot.id.toUpperCase() })\n    )\nconst MotorcycleFactory = types.model(\"MotorcycleFactory\", {\n    motorcycles: types.array(Motorcycle)\n})\n\ntest(\"it should preprocess snapshots when creating\", () => {\n    const car = Car.create({ id: \"1\" })\n    expect(car.id).toBe(2)\n})\ntest(\"it should preprocess snapshots when updating\", () => {\n    const car = Car.create({ id: \"1\" })\n    expect(car.id).toBe(2)\n    applySnapshot(car, { id: \"6\" })\n    expect(car.id).toBe(12)\n})\ntest(\"it should postprocess snapshots when generating snapshot - 1\", () => {\n    const car = Car.create({ id: \"1\" })\n    expect(car.id).toBe(2)\n    expect(getSnapshot(car)).toEqual({ id: \"1\" })\n})\ntest(\"it should not apply postprocessor to snapshot on getSnapshot\", () => {\n    const car = Car.create({ id: \"1\" })\n    let error = false\n    onSnapshot(car, snapshot => {\n        error = true\n    })\n    expect(getSnapshot(car)).toEqual({ id: \"1\" })\n    // @ts-expect-error - we are testing what happens when we explicitly do not want to apply the postprocessor, expect incorrect types\n    expect(getSnapshot(car, false)).toEqual({ id: 2 })\n    expect(error).toBeFalsy()\n})\ntest(\"it should preprocess snapshots when creating as property type\", () => {\n    const f = Factory.create({ car: { id: \"1\" } })\n    expect(f.car.id).toBe(2)\n})\ntest(\"it should preprocess snapshots when updating\", () => {\n    const f = Factory.create({ car: { id: \"1\" } })\n    expect(f.car.id).toBe(2)\n    applySnapshot(f, { car: { id: \"6\" } })\n    expect(f.car.id).toBe(12)\n})\ntest(\"it should postprocess snapshots when generating snapshot - 2\", () => {\n    const f = Factory.create({ car: { id: \"1\" } })\n    expect(f.car.id).toBe(2)\n    expect(getSnapshot(f)).toEqual({ car: { id: \"1\" } })\n})\n\ntest(\"it should postprocess non-initialized children\", () => {\n    const f = MotorcycleFactory.create({ motorcycles: [{ id: \"a\" }, { id: \"b\" }] })\n    expect(getSnapshot(f)).toEqual({ motorcycles: [{ id: \"A\" }, { id: \"B\" }] })\n})\n\ntest(\"base hooks can be composed\", () => {\n    const events: string[] = []\n    function listener(message: string) {\n        events.push(message)\n    }\n    const Todo = types\n        .model(\"Todo\", { title: \"\" })\n        .actions(self => {\n            function afterCreate() {\n                listener(\"aftercreate1\")\n            }\n            function beforeDestroy() {\n                listener(\"beforedestroy1\")\n            }\n            function afterAttach() {\n                listener(\"afterattach1\")\n            }\n            function beforeDetach() {\n                listener(\"beforedetach1\")\n            }\n            return { afterCreate, beforeDestroy, afterAttach, beforeDetach }\n        })\n        .actions(self => {\n            function afterCreate() {\n                listener(\"aftercreate2\")\n            }\n            function beforeDestroy() {\n                listener(\"beforedestroy2\")\n            }\n            function afterAttach() {\n                listener(\"afterattach2\")\n            }\n            function beforeDetach() {\n                listener(\"beforedetach2\")\n            }\n            return { afterCreate, beforeDestroy, afterAttach, beforeDetach }\n        })\n    const Store = types.model(\"Store\", { todos: types.array(Todo) })\n    const store = Store.create({ todos: [] })\n    const todo = Todo.create()\n    unprotect(store)\n    store.todos.push(todo)\n    detach(todo)\n    destroy(todo)\n    expect(events).toEqual([\n        \"aftercreate1\",\n        \"aftercreate2\",\n        \"afterattach1\",\n        \"afterattach2\",\n        \"beforedetach1\",\n        \"beforedetach2\",\n        \"beforedestroy1\",\n        \"beforedestroy2\"\n    ])\n})\ntest(\"snapshot processors can be composed\", () => {\n    const X = types\n        .model({\n            x: 1\n        })\n        .preProcessSnapshot(s => ({\n            x: s.x! - 3\n        }))\n        .preProcessSnapshot(s => ({\n            x: s.x! / 5\n        }))\n        .postProcessSnapshot(s => {\n            return { x: s.x + 3 }\n        })\n        .postProcessSnapshot(s => {\n            return { x: s.x * 5 }\n        })\n\n    const x = X.create({ x: 25 })\n    expect(x.x).toBe(2)\n    expect(getSnapshot(x).x).toBe(25)\n})\n\ntest(\"addDisposer must return the passed disposer\", () => {\n    const listener = jest.fn()\n    const M = types.model({}).actions(self => {\n        expect(addDisposer(self, listener)).toBe(listener)\n        return {}\n    })\n    M.create()\n})\n\ntest(\"array calls all hooks\", () => {\n    const events: string[] = []\n    function listener(message: string) {\n        events.push(message)\n    }\n    const Item = types.model(\"Item\", { id: types.string })\n    const Collection = types.array(Item).hooks(self => ({\n        afterCreate() {\n            listener(\"afterCreate\")\n        },\n        afterAttach() {\n            listener(\"afterAttach\")\n        },\n        beforeDetach() {\n            listener(\"beforeDetach\")\n        },\n        beforeDestroy() {\n            listener(\"beforeDestroy\")\n        }\n    }))\n    const Holder = types.model(\"Holder\", { items: types.maybe(Collection) })\n\n    const collection = Collection.create([{ id: \"1\" }, { id: \"2\" }, { id: \"3\" }])\n    expect(events).toStrictEqual([\"afterCreate\"])\n    const holder = Holder.create({ items: collection })\n    unprotect(holder)\n    expect(events).toStrictEqual([\"afterCreate\", \"afterAttach\"])\n    detach(holder.items!)\n    expect(events).toStrictEqual([\"afterCreate\", \"afterAttach\", \"beforeDetach\"])\n    holder.items = collection\n    expect(events).toStrictEqual([\"afterCreate\", \"afterAttach\", \"beforeDetach\", \"afterAttach\"])\n    holder.items = undefined\n    expect(events).toStrictEqual([\n        \"afterCreate\",\n        \"afterAttach\",\n        \"beforeDetach\",\n        \"afterAttach\",\n        \"beforeDestroy\"\n    ])\n})\n\ntest(\"map calls all hooks\", () => {\n    const events: string[] = []\n    function listener(message: string) {\n        events.push(message)\n    }\n    const Item = types.model(\"Item\", { id: types.string })\n    const Collection = types.map(Item).hooks(self => ({\n        afterCreate() {\n            listener(\"afterCreate\")\n        },\n        afterAttach() {\n            listener(\"afterAttach\")\n        },\n        beforeDetach() {\n            listener(\"beforeDetach\")\n        },\n        beforeDestroy() {\n            listener(\"beforeDestroy\")\n        }\n    }))\n    const Holder = types.model(\"Holder\", { items: types.maybe(Collection) })\n\n    const collection = Collection.create({ \"1\": { id: \"1\" }, \"2\": { id: \"2\" }, \"3\": { id: \"3\" } })\n    expect(events).toStrictEqual([\"afterCreate\"])\n    const holder = Holder.create({ items: cast(collection) })\n    unprotect(holder)\n    expect(events).toStrictEqual([\"afterCreate\", \"afterAttach\"])\n    detach(holder.items!)\n    expect(events).toStrictEqual([\"afterCreate\", \"afterAttach\", \"beforeDetach\"])\n    holder.items = collection\n    expect(events).toStrictEqual([\"afterCreate\", \"afterAttach\", \"beforeDetach\", \"afterAttach\"])\n    holder.items = undefined\n    expect(events).toStrictEqual([\n        \"afterCreate\",\n        \"afterAttach\",\n        \"beforeDetach\",\n        \"afterAttach\",\n        \"beforeDestroy\"\n    ])\n})\n"
  },
  {
    "path": "__tests__/core/identifier.test.ts",
    "content": "import {\n    types,\n    tryResolve,\n    resolvePath,\n    SnapshotOrInstance,\n    IAnyModelType,\n    IAnyComplexType,\n    resolveIdentifier,\n    getIdentifier,\n    detach\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"#275 - Identifiers should check refinement\", () => {\n        const Model = types\n            .model(\"Model\", {\n                id: types.refinement(\n                    \"id\",\n                    types.string,\n                    identifier => identifier.indexOf(\"Model_\") === 0\n                )\n            })\n            .actions(self => ({\n                setId(id: string) {\n                    self.id = id\n                }\n            }))\n        const ParentModel = types\n            .model(\"ParentModel\", {\n                models: types.array(Model)\n            })\n            .actions(self => ({\n                addModel(model: SnapshotOrInstance<typeof Model>) {\n                    self.models.push(model)\n                }\n            }))\n        expect(() => {\n            ParentModel.create({ models: [{ id: \"WrongId_1\" }] })\n        }).toThrow()\n        expect(() => {\n            const parentStore = ParentModel.create({ models: [] })\n            parentStore.addModel({ id: \"WrongId_2\" })\n        }).toThrow()\n        expect(() => {\n            const model = Model.create({ id: \"Model_1\" })\n            model.setId(\"WrongId_3\")\n        }).toThrow()\n        expect(() => {\n            Model.create({ id: \"WrongId_4\" })\n        }).toThrow()\n    })\n}\ntest(\"#158 - #88 - Identifiers should accept any string character\", () => {\n    const Todo = types.model(\"Todo\", {\n        id: types.identifier,\n        title: types.string\n    })\n    expect(() => {\n        ;[\"coffee\", \"cof$fee\", \"cof|fee\", \"cof/fee\"].forEach(id => {\n            Todo.create({\n                id: id,\n                title: \"Get coffee\"\n            })\n        })\n    }).not.toThrow()\n})\ntest(\"#187 - identifiers should not break late types\", () => {\n    expect(() => {\n        const MstNode = types.model(\"MstNode\", {\n            value: types.number,\n            next: types.maybe(types.late((): IAnyModelType => MstNode))\n        })\n    }).not.toThrow()\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"should throw if multiple identifiers provided\", () => {\n        expect(() => {\n            const Model = types.model(\"Model\", {\n                id: types.identifierNumber,\n                pk: types.identifierNumber\n            })\n            Model.create({ id: 1, pk: 2 })\n        }).toThrow(\n            `[mobx-state-tree] Cannot define property 'pk' as object identifier, property 'id' is already defined as identifier property`\n        )\n    })\n    test(\"should throw if identifier of wrong type\", () => {\n        expect(() => {\n            const Model = types.model(\"Model\", { id: types.identifier })\n            Model.create({ id: 1 as any as string })\n        }).toThrow(\n            'at path \"/id\" value `1` is not assignable to type: `identifier` (Value is not a valid identifier, expected a string).'\n        )\n    })\n    test(\"identifier should be used only on model types - no parent provided\", () => {\n        expect(() => {\n            const Model = types.identifierNumber\n            Model.create(1)\n        }).toThrow(\n            `[mobx-state-tree] Identifier types can only be instantiated as direct child of a model type`\n        )\n    })\n}\n\n{\n    const Foo = types.model(\"Foo\", {\n        id: types.identifier,\n        name: types.string\n    })\n\n    const Bar = types.model(\"Bar\", {\n        mimi: types.string,\n        fooRef: types.reference(Foo)\n    })\n\n    const Root = types.model(\"Root\", {\n        foos: types.map(Foo),\n        bar: Bar\n    })\n\n    const root = Root.create({\n        foos: {},\n        bar: {\n            mimi: \"mimi\",\n            fooRef: \"123\"\n        }\n    })\n\n    test(\"try resolve doesn't work #686\", () => {\n        expect(tryResolve(root, \"/bar/fooRef\")).toBe(undefined)\n        expect(tryResolve(root, \"/bar/fooRef/name\")).toBe(undefined)\n    })\n\n    test(\"try resolve shouldn't fail with a complex object node #2071\", () => {\n        const array = types.array(types.number).create([])\n        const model = types.model().create({})\n        const map = types.map(types.number).create({})\n        expect(tryResolve(array, \"/boop\")).toBeUndefined()\n        expect(tryResolve(model, \"/boop\")).toBeUndefined()\n        expect(tryResolve(map, \"/boop\")).toBeUndefined()\n    })\n\n    test(\"failing to resolve throws sane errors\", () => {\n        expect(() => {\n            resolvePath(root, \"/bar/mimi/oopsie\")\n        }).toThrow(\n            \"[mobx-state-tree] Could not resolve 'oopsie' in path '/bar/mimi' while resolving '/bar/mimi/oopsie'\"\n        )\n\n        expect(() => {\n            resolvePath(root, \"/zoomba/moomba\")\n        }).toThrow(\n            \"[mobx-state-tree] Could not resolve 'zoomba' in path '/' while resolving '/zoomba/moomba'\"\n        )\n\n        expect(() => resolvePath(root, \"/bar/fooRef\")).toThrow(\n            \"[mobx-state-tree] Failed to resolve reference '123' to type 'Foo' (from node: /bar/fooRef)\"\n        )\n        expect(() => resolvePath(root, \"/bar/fooRef/name\")).toThrow(\n            \"[mobx-state-tree] Failed to resolve reference '123' to type 'Foo' (from node: /bar/fooRef)\"\n        )\n    })\n}\n\ntest(\"it can resolve through references\", () => {\n    const Folder = types.model(\"Folder\", {\n        type: types.literal(\"folder\"),\n        name: types.identifier,\n        children: types.array(types.late((): IAnyComplexType => types.union(Folder, SymLink)))\n    })\n    const SymLink = types.model({\n        type: types.literal(\"link\"),\n        target: types.reference(Folder)\n    })\n\n    const root = Folder.create({\n        type: \"folder\",\n        name: \"root\",\n        children: [\n            {\n                type: \"folder\",\n                name: \"a\",\n                children: []\n            },\n            {\n                type: \"folder\",\n                name: \"b\",\n                children: [\n                    {\n                        type: \"folder\",\n                        name: \"c\",\n                        children: []\n                    }\n                ]\n            },\n            {\n                type: \"link\",\n                target: \"b\"\n            },\n            {\n                type: \"link\",\n                target: \"e\"\n            }\n        ]\n    })\n\n    expect(resolvePath(root, \"/children/1/children/0\").name).toBe(\"c\")\n\n    expect(resolvePath(root, \"/children/2/target/children/0\").name).toBe(\"c\")\n\n    expect(resolvePath(root, \"/children/2/target/children/../children/./0\").name).toBe(\"c\")\n\n    expect(() => resolvePath(root, \"/children/3/target/children/0\").name).toThrow(\n        \"[mobx-state-tree] Failed to resolve reference 'e' to type 'Folder' (from node: /children/3/target)\"\n    )\n})\n\ntest(\"#1019\", () => {\n    let calls = 0\n    function randomUuid() {\n        calls++\n        let pattern = \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\"\n        return pattern.replace(/[xy]/g, function (cc) {\n            const r = (Math.random() * 16) | 0,\n                v = cc === \"x\" ? r : (r & 0x3) | 0x8\n            return v.toString(16)\n        })\n    }\n\n    const TOptionalId = types.optional(\n        types.refinement(types.identifier, identifier =>\n            /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(identifier)\n        ),\n        randomUuid\n    )\n\n    const CommentModel = types.model(\"CommentModel\", {\n        uid: TOptionalId\n    })\n\n    const CommentStore = types\n        .model(\"CommentStore\", {\n            items: types.array(CommentModel)\n        })\n        .actions(self => ({\n            test1() {\n                expect(calls).toBe(0)\n                const comment = CommentModel.create({})\n                expect(calls).toBe(1)\n\n                self.items.push(comment)\n                const item = resolveIdentifier(CommentModel, self.items, comment.uid)\n                const item2 = self.items.find(i => i.uid === comment.uid)\n                expect(item).toBe(item2!)\n            }\n        }))\n\n    const c = CommentStore.create({})\n    c.test1()\n})\n\ntest(\"#1019-2\", () => {\n    const Item = types.model({\n        id: types.optional(types.identifier, \"dummykey\")\n    })\n    expect(getIdentifier(Item.create())).toBe(\"dummykey\")\n    expect(Item.create().id).toBe(\"dummykey\")\n})\n\ntest(\"identifierAttribute of the type\", () => {\n    const M1 = types.model({})\n    expect(M1.identifierAttribute).toBeUndefined()\n\n    const M2 = types.model({ myId: types.identifier })\n    expect(M2.identifierAttribute).toBe(\"myId\")\n\n    const M3 = types.model({ myId: types.optional(types.identifier, () => \"hi\") })\n    expect(M3.identifierAttribute).toBe(\"myId\")\n})\n\ntest(\"items detached from arrays don't corrupt identifierCache\", () => {\n    const Item = types.model(\"Item\", { id: types.identifier })\n    const ItemArray = types.model(\"ItemArray\", { items: types.array(Item) }).actions(self => ({\n        removeSecondItemWithDetach() {\n            detach(self.items[1])\n        }\n    }))\n\n    const smallArray = ItemArray.create({\n        items: [{ id: \"A\" }, { id: \"B\" }, { id: \"C\" }, { id: \"D\" }, { id: \"E\" }]\n    })\n    expect(resolveIdentifier(Item, smallArray, \"E\")).toBeDefined()\n    smallArray.removeSecondItemWithDetach()\n    expect(smallArray.items.length).toBe(4)\n    expect(resolveIdentifier(Item, smallArray, \"B\")).toBeUndefined()\n    expect(resolveIdentifier(Item, smallArray, \"E\")).toBeDefined()\n\n    const largeArray = ItemArray.create({\n        items: [\n            { id: \"A\" },\n            { id: \"B\" },\n            { id: \"C\" },\n            { id: \"D\" },\n            { id: \"E\" },\n            { id: \"F\" },\n            { id: \"G\" },\n            { id: \"H\" },\n            { id: \"I\" },\n            { id: \"J\" },\n            { id: \"K\" }\n        ]\n    })\n    expect(resolveIdentifier(Item, largeArray, \"K\")).toBeDefined()\n    largeArray.removeSecondItemWithDetach()\n    expect(largeArray.items.length).toBe(10)\n    expect(resolveIdentifier(Item, largeArray, \"B\")).toBeUndefined()\n    expect(resolveIdentifier(Item, largeArray, \"J\")).toBeDefined()\n    // The following expectation was failing in version 5.1.8 and earlier\n    expect(resolveIdentifier(Item, largeArray, \"K\")).toBeDefined()\n})\n"
  },
  {
    "path": "__tests__/core/jsonpatch.test.ts",
    "content": "import {\n    getSnapshot,\n    unprotect,\n    recordPatches,\n    types,\n    IType,\n    IJsonPatch,\n    Instance,\n    cast,\n    IAnyModelType,\n    IMSTMap,\n    escapeJsonPath,\n    getPath,\n    resolvePath,\n    splitJsonPath,\n    joinJsonPath\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nfunction testPatches<C, S, T extends object>(\n    type: IType<C, S, T>,\n    snapshot: C,\n    fn: any,\n    expectedPatches: IJsonPatch[]\n) {\n    const instance = type.create(snapshot)\n    const baseSnapshot = getSnapshot(instance)\n    const recorder = recordPatches(instance)\n    unprotect(instance)\n    fn(instance)\n    recorder.stop()\n    expect(recorder.patches).toEqual(expectedPatches)\n    const clone = type.create(snapshot)\n    recorder.replay(clone)\n    expect(getSnapshot(clone)).toEqual(getSnapshot(instance))\n    recorder.undo()\n    expect(getSnapshot(instance)).toEqual(baseSnapshot)\n}\nconst Node = types.model(\"Node\", {\n    id: types.identifierNumber,\n    text: \"Hi\",\n    children: types.optional(types.array(types.late((): IAnyModelType => Node)), [])\n})\n\ntest(\"it should apply simple patch\", () => {\n    testPatches(\n        Node,\n        { id: 1 },\n        (n: Instance<typeof Node>) => {\n            n.text = \"test\"\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/text\",\n                value: \"test\"\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to arrays\", () => {\n    testPatches(\n        Node,\n        { id: 1, children: [{ id: 2 }] },\n        (n: Instance<typeof Node>) => {\n            const children = n.children as unknown as Instance<typeof Node>[]\n            children[0].text = \"test\" // update\n            children[0] = cast({ id: 2, text: \"world\" }) // this reconciles; just an update\n            children[0] = cast({ id: 4, text: \"coffee\" }) // new object\n            children[1] = cast({ id: 3, text: \"world\" }) // addition\n            children.splice(0, 1) // removal\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"world\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0\",\n                value: {\n                    id: 4,\n                    text: \"coffee\",\n                    children: []\n                }\n            },\n            {\n                op: \"add\",\n                path: \"/children/1\",\n                value: {\n                    id: 3,\n                    text: \"world\",\n                    children: []\n                }\n            },\n            {\n                op: \"remove\",\n                path: \"/children/0\"\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to arrays with object instances\", () => {\n    testPatches(\n        Node,\n        { id: 1, children: [{ id: 2 }] },\n        (n: Instance<typeof Node>) => {\n            const children = n.children as unknown as Instance<typeof Node>[]\n            children[0].text = \"test\" // update\n            children[0] = Node.create({ id: 2, text: \"world\" }) // this does not reconcile, new instance is provided\n            children[0] = Node.create({ id: 4, text: \"coffee\" }) // new object\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0\",\n                value: {\n                    id: 2,\n                    text: \"world\",\n                    children: []\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0\",\n                value: {\n                    id: 4,\n                    text: \"coffee\",\n                    children: []\n                }\n            }\n        ]\n    )\n})\n\ntest(\"it should apply non flat patches\", () => {\n    testPatches(\n        Node,\n        { id: 1 },\n        (n: Instance<typeof Node>) => {\n            const children = n.children as unknown as Instance<typeof Node>[]\n            children.push(\n                cast({\n                    id: 2,\n                    children: [{ id: 4 }, { id: 5, text: \"Tea\" }]\n                })\n            )\n        },\n        [\n            {\n                op: \"add\",\n                path: \"/children/0\",\n                value: {\n                    id: 2,\n                    text: \"Hi\",\n                    children: [\n                        {\n                            id: 4,\n                            text: \"Hi\",\n                            children: []\n                        },\n                        {\n                            id: 5,\n                            text: \"Tea\",\n                            children: []\n                        }\n                    ]\n                }\n            }\n        ]\n    )\n})\n\ntest(\"it should apply non flat patches with object instances\", () => {\n    testPatches(\n        Node,\n        { id: 1 },\n        (n: Instance<typeof Node>) => {\n            const children = n.children as unknown as Instance<typeof Node>[]\n            children.push(\n                Node.create({\n                    id: 2,\n                    children: [{ id: 5, text: \"Tea\" }]\n                })\n            )\n        },\n        [\n            {\n                op: \"add\",\n                path: \"/children/0\",\n                value: {\n                    id: 2,\n                    text: \"Hi\",\n                    children: [\n                        {\n                            id: 5,\n                            text: \"Tea\",\n                            children: []\n                        }\n                    ]\n                }\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to maps\", () => {\n    // If user does not transpile const/let to var, trying to call Late' subType\n    // property getter during map's tryCollectModelTypes() will throw ReferenceError.\n    // But if it's transpiled to var, then subType will become 'undefined'.\n    const NodeMap = types.model(\"NodeMap\", {\n        id: types.identifierNumber,\n        text: \"Hi\",\n        children: types.optional(types.map(types.late((): IAnyModelType => NodeMap)), {})\n    })\n    testPatches(\n        NodeMap,\n        { id: 1, children: { 2: { id: 2 } } },\n        (n: Instance<typeof NodeMap>) => {\n            const children = n.children as IMSTMap<typeof NodeMap>\n            children.get(\"2\")!.text = \"test\" // update\n            children.put({ id: 2, text: \"world\" }) // this reconciles; just an update\n            children.set(\n                \"4\",\n                NodeMap.create({ id: 4, text: \"coffee\", children: { 23: { id: 23 } } })\n            ) // new object\n            children.put({ id: 3, text: \"world\", children: { 7: { id: 7 } } }) // addition\n            children.delete(\"2\") // removal\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children/2/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/2/text\",\n                value: \"world\"\n            },\n            {\n                op: \"add\",\n                path: \"/children/4\",\n                value: {\n                    children: {\n                        23: {\n                            children: {},\n                            id: 23,\n                            text: \"Hi\"\n                        }\n                    },\n                    id: 4,\n                    text: \"coffee\"\n                }\n            },\n            {\n                op: \"add\",\n                path: \"/children/3\",\n                value: {\n                    children: {\n                        7: {\n                            children: {},\n                            id: 7,\n                            text: \"Hi\"\n                        }\n                    },\n                    id: 3,\n                    text: \"world\"\n                }\n            },\n            {\n                op: \"remove\",\n                path: \"/children/2\"\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to objects\", () => {\n    const NodeObject = types.model(\"NodeObject\", {\n        id: types.identifierNumber,\n        text: \"Hi\",\n        child: types.maybe(types.late((): IAnyModelType => NodeObject))\n    })\n    testPatches(\n        NodeObject,\n        { id: 1, child: { id: 2 } },\n        (n: Instance<typeof NodeObject>) => {\n            n.child!.text = \"test\" // update\n            n.child = cast({ id: 2, text: \"world\" }) // this reconciles; just an update\n            n.child = NodeObject.create({ id: 2, text: \"coffee\", child: { id: 23 } })\n            n.child = cast({ id: 3, text: \"world\", child: { id: 7 } }) // addition\n            n.child = undefined // removal\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/child/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/child/text\",\n                value: \"world\"\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: {\n                        child: undefined,\n                        id: 23,\n                        text: \"Hi\"\n                    },\n                    id: 2,\n                    text: \"coffee\"\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: {\n                        child: undefined,\n                        id: 7,\n                        text: \"Hi\"\n                    },\n                    id: 3,\n                    text: \"world\"\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: undefined\n            }\n        ]\n    )\n})\n\ntest(\"it should correctly split/join json patches\", () => {\n    function isValid(str: string, array: string[], altStr?: string) {\n        expect(splitJsonPath(str)).toEqual(array)\n        expect(joinJsonPath(array)).toBe(altStr !== undefined ? altStr : str)\n    }\n\n    isValid(\"\", [])\n    isValid(\"/\", [\"\"])\n    isValid(\"//\", [\"\", \"\"])\n    isValid(\"/a\", [\"a\"])\n    isValid(\"/a/\", [\"a\", \"\"])\n    isValid(\"/a//\", [\"a\", \"\", \"\"])\n    isValid(\".\", [\".\"])\n    isValid(\"..\", [\"..\"])\n    isValid(\"./a\", [\".\", \"a\"])\n    isValid(\"../a\", [\"..\", \"a\"])\n    isValid(\"/.a\", [\".a\"])\n    isValid(\"/..a\", [\"..a\"])\n\n    // rooted relatives are equivalent to plain relatives\n    isValid(\"/.\", [\".\"], \".\")\n    isValid(\"/..\", [\"..\"], \"..\")\n    isValid(\"/./a\", [\".\", \"a\"], \"./a\")\n    isValid(\"/../a\", [\"..\", \"a\"], \"../a\")\n\n    function isInvalid(str: string) {\n        expect(() => {\n            splitJsonPath(str)\n        }).toThrow(\"a json path must be either rooted, empty or relative\")\n    }\n\n    isInvalid(\"a\")\n    isInvalid(\"a/\")\n    isInvalid(\"a//\")\n    isInvalid(\".a\")\n    isInvalid(\".a/\")\n    isInvalid(\"..a\")\n    isInvalid(\"..a/\")\n})\n\ntest(\"it should correctly escape/unescape json patches\", () => {\n    expect(escapeJsonPath(\"http://example.com\")).toBe(\"http:~1~1example.com\")\n\n    const AppStore = types.model({\n        items: types.map(types.frozen<any>())\n    })\n    testPatches(\n        AppStore,\n        { items: {} },\n        (store: typeof AppStore.Type) => {\n            store.items.set(\"with/slash~tilde\", 1)\n        },\n        [{ op: \"add\", path: \"/items/with~1slash~0tilde\", value: 1 }]\n    )\n})\n\ntest(\"weird keys are handled correctly\", () => {\n    const Store = types.model({\n        map: types.map(\n            types.model({\n                model: types.model({\n                    value: types.string\n                })\n            })\n        )\n    })\n\n    const store = Store.create({\n        map: {\n            \"\": { model: { value: \"val1\" } },\n            \"/\": { model: { value: \"val2\" } },\n            \"~\": { model: { value: \"val3\" } }\n        }\n    })\n\n    {\n        const target = store.map.get(\"\")!.model\n        const path = getPath(target)\n        expect(path).toBe(\"/map//model\")\n        expect(resolvePath(store, path)).toBe(target)\n    }\n    {\n        const target = store.map.get(\"/\")!.model\n        const path = getPath(target)\n        expect(path).toBe(\"/map/~1/model\")\n        expect(resolvePath(store, path)).toBe(target)\n    }\n    {\n        const target = store.map.get(\"~\")!.model\n        const path = getPath(target)\n        expect(path).toBe(\"/map/~0/model\")\n        expect(resolvePath(store, path)).toBe(target)\n    }\n})\n\ntest(\"relativePath with a different base than the root works correctly\", () => {\n    const Store = types.model({\n        map: types.map(\n            types.model({\n                model: types.model({\n                    value: types.string\n                })\n            })\n        )\n    })\n\n    const store = Store.create({\n        map: {\n            \"1\": { model: { value: \"val1\" } },\n            \"2\": { model: { value: \"val2\" } }\n        }\n    })\n\n    {\n        const target = store.map.get(\"1\")!.model\n        expect(resolvePath(store.map, \"./1/model\")).toBe(target)\n        expect(resolvePath(store.map, \"../map/1/model\")).toBe(target)\n        // rooted relative should resolve to the given base as root\n        expect(resolvePath(store.map, \"/./1/model\")).toBe(target)\n        expect(resolvePath(store.map, \"/../map/1/model\")).toBe(target)\n    }\n    {\n        const target = store.map.get(\"2\")!.model\n        expect(resolvePath(store.map, \"./2/model\")).toBe(target)\n        expect(resolvePath(store.map, \"../map/2/model\")).toBe(target)\n        // rooted relative should resolve to the given base as root\n        expect(resolvePath(store.map, \"/./2/model\")).toBe(target)\n        expect(resolvePath(store.map, \"/../map/2/model\")).toBe(target)\n    }\n})\ntest(\"it should emit one patch for array clear\", () => {\n    testPatches(\n        Node,\n        { id: 1, children: [{ id: 2 }, { id: 3 }] },\n        (n: Instance<typeof Node>) => {\n            n.children.clear()\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children\",\n                value: []\n            }\n        ]\n    )\n})\n\ntest(\"it should emit one patch for array replace\", () => {\n    testPatches(\n        Node,\n        { id: 1, children: [{ id: 2 }, { id: 3 }] },\n        (n: Instance<typeof Node>) => {\n            n.children.replace([{ id: 4 }, { id: 5 }])\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children\",\n                value: [\n                    {\n                        id: 4,\n                        text: \"Hi\",\n                        children: []\n                    },\n                    {\n                        id: 5,\n                        text: \"Hi\",\n                        children: []\n                    }\n                ]\n            }\n        ]\n    )\n})\n"
  },
  {
    "path": "__tests__/core/late.test.ts",
    "content": "import { types, typecheck, IAnyModelType } from \"../../src\"\nimport { expect, spyOn, test } from \"bun:test\"\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should throw if late doesnt received a function as parameter\", () => {\n        expect(() => {\n            types.model({\n                after: types.late(1 as any)\n            })\n        }).toThrow()\n    })\n}\ntest(\"it should accept a type and infer it correctly\", () => {\n    const Before = types.model({\n        after: types.late(() => After)\n    })\n    const After = types.model({\n        name: types.maybe(types.string)\n    })\n    expect(() => Before.create({ after: { name: \"Hello, it's me.\" } })).not.toThrow()\n})\ntest(\"late should allow circular references\", () => {\n    // TypeScript isn't smart enough to infer self referencing types.\n    const Node = types.model({\n        childs: types.optional(types.array(types.late((): IAnyModelType => Node)), [])\n    })\n    expect(() => Node.create()).not.toThrow()\n    expect(() => Node.create({ childs: [{}, { childs: [] }] })).not.toThrow()\n})\ntest(\"late should describe correctly circular references\", () => {\n    // TypeScript isn't smart enough to infer self referencing types.\n    const Node = types.model(\"Node\", {\n        childs: types.array(types.late((): IAnyModelType => Node))\n    })\n    expect(Node.describe()).toEqual(\"{ childs: late(() => Node)[]? }\")\n})\ntest(\"should typecheck\", () => {\n    const NodeObject = types.model(\"NodeObject\", {\n        id: types.identifierNumber,\n        text: \"Hi\",\n        child: types.maybe(types.late((): IAnyModelType => NodeObject))\n    })\n    const x = NodeObject.create({ id: 1 })\n    try {\n        ;(x as any).child = 3\n        ;(x as any).floepie = 3\n    } catch (e) {\n        // ignore, this is about TS\n    }\n})\n\ntest(\"typecheck should throw an Error when called at runtime, but not log the error\", () => {\n    const consoleSpy = spyOn(console, \"error\")\n\n    const NodeObject = types.model(\"NodeObject\", {\n        id: types.identifierNumber,\n        text: types.string\n    })\n\n    expect(() => {\n        typecheck(NodeObject, { id: 1, text: 1 } as any)\n    }).toThrow()\n\n    try {\n        typecheck(NodeObject, { id: 1, text: 1 } as any)\n    } catch (error) {\n        expect(error).toBeDefined()\n        expect(consoleSpy).not.toHaveBeenCalled()\n    }\n})\n\ntest(\"#825, late type checking \", () => {\n    const Product = types.model({\n        details: types.late(() => types.optional(Details, {}))\n    })\n    const Details = types.model({\n        name: types.maybe(types.string)\n    })\n\n    const p2 = Product.create({})\n    const p = Product.create({ details: { name: \"bla\" } })\n})\n\ntest(\"#916 - 0\", () => {\n    const Todo = types.model(\"Todo\", {\n        title: types.string,\n        newTodo: types.optional(\n            types.late((): IAnyModelType => Todo),\n            {}\n        ) // N.B. this definition is never instantiateable!\n    })\n})\n\ntest(\"#916 - 1\", () => {\n    const Todo = types.model(\"Todo\", {\n        title: types.string,\n        newTodo: types.maybe(types.late((): IAnyModelType => Todo))\n    })\n    const t = Todo.create({\n        title: \"Get Coffee\"\n    })\n})\n\ntest(\"#916 - 2\", () => {\n    const Todo = types.model(\"Todo\", {\n        title: types.string,\n        newTodo: types.maybe(types.late((): IAnyModelType => Todo))\n    })\n    expect(\n        Todo.is({\n            title: \"A\",\n            newTodo: { title: \" test\" }\n        })\n    ).toBe(true)\n    expect(\n        Todo.is({\n            title: \"A\",\n            newTodo: { title: 7 }\n        })\n    ).toBe(false)\n})\n\ntest(\"#916 - 3\", () => {\n    const Todo = types.model(\"Todo\", {\n        title: types.string,\n        newTodo: types.maybe(types.late((): IAnyModelType => Todo))\n    })\n    const t = Todo.create({\n        title: \"Get Coffee\",\n        newTodo: { title: \"test\" }\n    })\n\n    expect(t.newTodo!.title).toBe(\"test\")\n})\n"
  },
  {
    "path": "__tests__/core/lazy.test.ts",
    "content": "import { when } from \"mobx\"\nimport { getRoot, types } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ninterface IRootModel {\n    shouldLoad: boolean\n}\n\ntest(\"it should load the correct type\", async () => {\n    const LazyModel = types\n        .model(\"LazyModel\", {\n            width: types.number,\n            height: types.number\n        })\n        .views(self => ({\n            get area() {\n                return self.height * self.width\n            }\n        }))\n\n    const Root = types\n        .model(\"Root\", {\n            shouldLoad: types.optional(types.boolean, false),\n            lazyModel: types.lazy<typeof LazyModel, IRootModel>(\"lazy\", {\n                loadType: () => Promise.resolve(LazyModel),\n                shouldLoadPredicate: parent => parent.shouldLoad == true\n            })\n        })\n        .actions(self => ({\n            load: () => {\n                self.shouldLoad = true\n            }\n        }))\n\n    const store = Root.create({\n        lazyModel: {\n            width: 3,\n            height: 2\n        }\n    })\n\n    expect(store.lazyModel.width).toBe(3)\n    expect(store.lazyModel.height).toBe(2)\n    expect(store.lazyModel.area).toBeUndefined()\n    store.load()\n\n    await when(() => store.lazyModel && store.lazyModel.area !== undefined, { timeout: 2000 })\n    await expect(store.lazyModel.area).toBe(6)\n})\n\ntest(\"maintains the tree structure when loaded\", async () => {\n    const LazyModel = types\n        .model(\"LazyModel\", {\n            width: types.number,\n            height: types.number\n        })\n        .views(self => ({\n            get area() {\n                const root = getRoot<{ rootValue: number }>(self)\n                return self.height * self.width * root.rootValue\n            }\n        }))\n\n    const Root = types\n        .model(\"Root\", {\n            shouldLoad: types.optional(types.boolean, false),\n            lazyModel: types.lazy<typeof LazyModel, IRootModel>(\"lazy\", {\n                loadType: () => Promise.resolve(LazyModel),\n                shouldLoadPredicate: parent => parent.shouldLoad == true\n            })\n        })\n        .views(() => ({\n            get rootValue() {\n                return 5\n            }\n        }))\n        .actions(self => ({\n            load: () => {\n                self.shouldLoad = true\n            }\n        }))\n\n    const store = Root.create({\n        lazyModel: {\n            width: 3,\n            height: 2\n        }\n    })\n\n    expect(store.lazyModel.width).toBe(3)\n    expect(store.lazyModel.height).toBe(2)\n    expect(store.rootValue).toEqual(5)\n    expect(store.lazyModel.area).toBeUndefined()\n    store.load()\n    const promise = new Promise<number>((resolve, reject) => {\n        when(\n            () => store.lazyModel && store.lazyModel.area !== undefined,\n            () => resolve(store.lazyModel.area)\n        )\n\n        setTimeout(reject, 2000)\n    })\n\n    await expect(promise).resolves.toBe(30)\n})\n"
  },
  {
    "path": "__tests__/core/literal.test.ts",
    "content": "import { types } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should allow only primitives\", () => {\n        const error = expect(() => {\n            types.model({\n                complexArg: types.literal({ a: 1 } as any)\n            })\n        }).toThrow(\"expected primitive as argument\")\n    })\n    test(\"it should fail if not optional and no default provided\", () => {\n        const Factory = types.literal(\"hello\")\n        expect(() => {\n            ;(Factory.create as any)()\n        }).toThrow(/is not assignable to type/)\n    })\n    test(\"it should throw if a different type is given\", () => {\n        const Factory = types.model(\"TestFactory\", {\n            shouldBeOne: types.literal(1)\n        })\n        expect(() => {\n            Factory.create({ shouldBeOne: 2 as any })\n        }).toThrow(/is not assignable to type/)\n    })\n}\ntest(\"it should support null type\", () => {\n    const M = types.model({\n        nullish: types.null\n    })\n    expect(\n        M.is({\n            nullish: null\n        })\n    ).toBe(true)\n    expect(M.is({ nullish: undefined })).toBe(false)\n    expect(M.is({ nullish: 17 })).toBe(false)\n})\ntest(\"it should support undefined type\", () => {\n    const M = types.model({\n        undefinedish: types.undefined\n    })\n    expect(\n        M.is({\n            undefinedish: undefined\n        })\n    ).toBe(true)\n    expect(M.is({})).toBe(true) // MWE: disputable, should be false?\n    expect(M.is({ undefinedish: null })).toBe(false)\n    expect(M.is({ undefinedish: 17 })).toBe(false)\n})\n"
  },
  {
    "path": "__tests__/core/map.test.ts",
    "content": "import { configure } from \"mobx\"\nimport {\n    onSnapshot,\n    onPatch,\n    applyPatch,\n    applySnapshot,\n    getSnapshot,\n    types,\n    unprotect,\n    isStateTreeNode,\n    SnapshotOut,\n    IJsonPatch,\n    IAnyModelType,\n    detach\n} from \"../../src\"\nimport { describe, expect, it, test } from \"bun:test\"\n\nconst createTestFactories = () => {\n    const ItemFactory = types.model({\n        to: \"world\"\n    })\n    const Factory = types.map(ItemFactory)\n    const PrimitiveMapFactory = types.model({\n        boolean: types.map(types.boolean),\n        string: types.map(types.string),\n        number: types.map(types.number)\n    })\n    return { Factory, ItemFactory, PrimitiveMapFactory }\n}\n// === FACTORY TESTS ===\ntest(\"it should create a factory\", () => {\n    const { Factory } = createTestFactories()\n    const snapshot = getSnapshot(Factory.create())\n    expect(snapshot).toEqual({})\n})\ntest(\"it should succeed if not optional and no default provided\", () => {\n    const Factory = types.map(types.string)\n    expect(Factory.create().toJSON()).toEqual({})\n})\ntest(\"it should restore the state from the snapshot\", () => {\n    const { Factory } = createTestFactories()\n    const instance = Factory.create({ hello: { to: \"world\" } })\n    expect(getSnapshot(instance)).toEqual({ hello: { to: \"world\" } })\n    expect((\"\" + instance).replace(/@\\d+/, \"@xx\")).toBe(\"[object ObservableMap]\") // default toString\n})\n// === SNAPSHOT TESTS ===\ntest(\"it should emit snapshots\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let snapshots: SnapshotOut<typeof doc>[] = []\n    onSnapshot(doc, snapshot => snapshots.push(snapshot))\n    doc.set(\"hello\", ItemFactory.create())\n    expect(snapshots).toEqual([{ hello: { to: \"world\" } }])\n})\ntest(\"it should apply snapshots\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applySnapshot(doc, { hello: { to: \"universe\" } })\n    expect(getSnapshot(doc)).toEqual({ hello: { to: \"universe\" } })\n})\ntest(\"it should return a snapshot\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.set(\"hello\", ItemFactory.create())\n    expect(getSnapshot(doc)).toEqual({ hello: { to: \"world\" } })\n})\ntest(\"it should be the same each time\", () => {\n    const { PrimitiveMapFactory } = createTestFactories()\n    const data = {\n        string: { a: \"a\", b: \"\" },\n        boolean: { a: true, b: false },\n        number: { a: 0, b: 42, c: NaN }\n    }\n    const doc = PrimitiveMapFactory.create(data)\n    expect(getSnapshot(doc)).toEqual(data)\n    applySnapshot(doc, data)\n    expect(getSnapshot(doc)).toEqual(data)\n    applySnapshot(doc, data)\n    expect(getSnapshot(doc)).toEqual(data)\n})\n// === PATCHES TESTS ===\ntest(\"it should emit add patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc.set(\"hello\", ItemFactory.create({ to: \"universe\" }))\n    expect(patches).toEqual([{ op: \"add\", path: \"/hello\", value: { to: \"universe\" } }])\n})\ntest(\"it should apply an add patch\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, { op: \"add\", path: \"/hello\", value: { to: \"universe\" } })\n    expect(getSnapshot(doc)).toEqual({ hello: { to: \"universe\" } })\n})\ntest(\"it should emit update patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.set(\"hello\", ItemFactory.create())\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc.set(\"hello\", ItemFactory.create({ to: \"universe\" }))\n    expect(patches).toEqual([{ op: \"replace\", path: \"/hello\", value: { to: \"universe\" } }])\n})\ntest(\"it should apply an update patch\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    applyPatch(doc, { op: \"replace\", path: \"/hello\", value: { to: \"universe\" } })\n    expect(getSnapshot(doc)).toEqual({ hello: { to: \"universe\" } })\n})\ntest(\"it should emit remove patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.set(\"hello\", ItemFactory.create())\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc.delete(\"hello\")\n    expect(patches).toEqual([{ op: \"remove\", path: \"/hello\" }])\n})\ntest(\"it should apply a remove patch\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    doc.set(\"hello\", ItemFactory.create())\n    applyPatch(doc, { op: \"remove\", path: \"/hello\" })\n    expect(getSnapshot(doc)).toEqual({})\n})\ntest(\"it should apply patches\", () => {\n    const { Factory, ItemFactory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, [\n        { op: \"add\", path: \"/hello\", value: { to: \"mars\" } },\n        { op: \"replace\", path: \"/hello\", value: { to: \"universe\" } }\n    ])\n    expect(getSnapshot(doc)).toEqual({ hello: { to: \"universe\" } })\n})\n// === TYPE CHECKS ===\ntest(\"it should check the type correctly\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    expect(Factory.is(doc)).toEqual(true)\n    expect(Factory.is([])).toEqual(false)\n    expect(Factory.is({})).toEqual(true)\n    expect(Factory.is({ hello: { to: \"mars\" } })).toEqual(true)\n    expect(Factory.is({ hello: { wrongKey: true } })).toEqual(true)\n    expect(Factory.is({ hello: { to: true } })).toEqual(false)\n})\ntest(\"it should support identifiers\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Store = types.model({\n        todos: types.optional(\n            types.map(\n                types.model({\n                    id: types.identifier\n                })\n            ),\n            {}\n        )\n    })\n    const store = Store.create()\n    unprotect(store)\n    store.todos.set(\"17\", { id: \"17\" })\n    const a = store.todos.get(\"17\")\n    applySnapshot(store.todos, { \"16\": { id: \"16\" }, \"17\": { id: \"17\" } })\n    expect(a === store.todos.get(\"17\")).toBe(true) // same instance still\n    expect(store.todos.get(\"17\")!.id).toBe(\"17\")\n    store.todos.put({ id: \"19\" })\n    expect(store.todos.get(\"19\")!.id).toBe(\"19\")\n    expect(\"\" + store.todos.get(\"19\")).toBe(\"AnonymousModel@/todos/19(id: 19)\")\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => applySnapshot(store.todos, { \"17\": { id: \"18\" } })).toThrow(\n            \"[mobx-state-tree] A map of objects containing an identifier should always store the object under their own identifier. Trying to store key '18', but expected: '17'\"\n        )\n    }\n})\ntest(\"#184 - types.map().get(key) should not throw if key doesnt exists\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create({\n        hello: {\n            to: \"world\"\n        }\n    })\n    expect(() => {\n        doc.get(\"notexistingkey\")\n    }).not.toThrow()\n})\ntest(\"#192 - put should not throw when identifier is a number\", () => {\n    const Todo = types.model(\"Todo\", { todo_id: types.identifierNumber, title: types.string })\n    const TodoStore = types\n        .model(\"TodoStore\", {\n            todos: types.optional(types.map(Todo), {})\n        })\n        .actions(self => {\n            function addTodo(todo: typeof Todo.Type | typeof Todo.CreationType) {\n                self.todos.put(todo)\n            }\n            return {\n                addTodo\n            }\n        })\n    const todoStore = TodoStore.create({})\n    expect(() => {\n        todoStore.addTodo({\n            todo_id: 1,\n            title: \"Test\"\n        })\n    }).not.toThrow()\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => {\n            todoStore.addTodo({ todo_id: \"1\", title: \"Test\" } as any)\n        }).toThrow(\n            'at path \"/todo_id\" value `\"1\"` is not assignable to type: `identifierNumber` (Value is not a valid identifierNumber, expected a number)'\n        )\n    }\n})\ntest(\"#192 - map should not mess up keys when putting twice\", () => {\n    const Todo = types.model(\"Todo\", { todo_id: types.identifierNumber, title: types.string })\n    const TodoStore = types\n        .model(\"TodoStore\", {\n            todos: types.optional(types.map(Todo), {})\n        })\n        .actions(self => {\n            function addTodo(todo: typeof Todo.Type | typeof Todo.CreationType) {\n                self.todos.put(todo)\n            }\n            return {\n                addTodo\n            }\n        })\n    const todoStore = TodoStore.create({})\n    todoStore.addTodo({\n        todo_id: 1,\n        title: \"Test\"\n    })\n    expect(getSnapshot(todoStore.todos)).toEqual({ \"1\": { todo_id: 1, title: \"Test\" } })\n    todoStore.addTodo({\n        todo_id: 1,\n        title: \"Test Edited\"\n    })\n    expect(getSnapshot(todoStore.todos)).toEqual({ \"1\": { todo_id: 1, title: \"Test Edited\" } })\n})\ntest(\"#694 - map.put should return new node\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Todo = types.model(\"Todo\", {\n        todo_id: types.identifier,\n        title: types.string\n    })\n    const TodoStore = types\n        .model(\"TodoStore\", {\n            todos: types.map(Todo)\n        })\n        .actions(self => {\n            function addAndReturnTodo(todo: typeof Todo.Type | typeof Todo.CreationType) {\n                return self.todos.put(todo)\n            }\n            return {\n                addAndReturnTodo\n            }\n        })\n    const todoStore = TodoStore.create({ todos: {} })\n\n    const addedTodo = todoStore.addAndReturnTodo(\n        Todo.create({\n            todo_id: \"1\",\n            title: \"Test 1\"\n        })\n    )\n\n    expect(isStateTreeNode(addedTodo)).toEqual(true)\n    expect(getSnapshot(addedTodo)).toEqual({ todo_id: \"1\", title: \"Test 1\" })\n\n    const editedTodo = todoStore.addAndReturnTodo({\n        todo_id: \"1\",\n        title: \"Test 1 Edited\"\n    })\n    expect(isStateTreeNode(editedTodo)).toEqual(true)\n    expect(getSnapshot(editedTodo)).toEqual({ todo_id: \"1\", title: \"Test 1 Edited\" })\n    expect(editedTodo).toEqual(addedTodo)\n\n    const addedTodo2 = todoStore.addAndReturnTodo({\n        todo_id: \"2\",\n        title: \"Test 2\"\n    })\n    expect(isStateTreeNode(addedTodo2)).toEqual(true)\n    expect(getSnapshot(addedTodo2)).toEqual({ todo_id: \"2\", title: \"Test 2\" })\n})\ntest(\"it should not throw when removing a non existing item from a map\", () => {\n    expect(() => {\n        const AppModel = types\n            .model({\n                myMap: types.map(types.number)\n            })\n            .actions(self => {\n                function something() {\n                    return self.myMap.delete(\"1020\")\n                }\n                return {\n                    something\n                }\n            })\n        const store = AppModel.create()\n        expect(store.something()).toBe(false)\n    }).not.toThrow()\n})\ntest(\"it should get map keys from reversePatch when deleted an item from a nested map\", () => {\n    const AppModel = types\n        .model({\n            value: types.map(types.map(types.map(types.number)))\n        })\n        .actions(self => ({\n            remove(k: string) {\n                self.value.delete(k)\n            }\n        }))\n    const store = AppModel.create({ value: { a: { b: { c: 10 } } } })\n    onPatch(store, (patch, reversePatch) => {\n        expect(patch).toEqual({ op: \"remove\", path: \"/value/a\" })\n        expect(reversePatch).toEqual({ op: \"add\", path: \"/value/a\", value: { b: { c: 10 } } })\n    })\n    store.remove(\"a\")\n})\n\ntest(\"map expects regular identifiers\", () => {\n    const A = types.model(\"A\", { a: types.identifier })\n    const B = types.model(\"B\", { b: types.identifier })\n\n    // NOTE: we can determine identifier attribute upfront, so no need to wait for error while craetion\n    expect(() => types.map(types.union(A, B))).toThrow(\n        `[mobx-state-tree] The objects in a map should all have the same identifier attribute, expected 'a', but child of type 'B' declared attribute 'b' as identifier`\n    )\n})\n\ntest(\"issue #876 - map.put works fine for models with preProcessSnapshot\", () => {\n    const Note = types.model(\"Item\", {\n        text: types.string\n    })\n    const Item = types\n        .model(\"Item\", {\n            id: types.identifier,\n            title: types.string,\n            notes: types.array(Note)\n        })\n        .preProcessSnapshot(snapshot => {\n            const result = Object.assign({}, snapshot)\n            if (typeof result.title !== \"string\") result.title = \"\"\n            return result\n        })\n\n    const Store = types\n        .model(\"Store\", {\n            items: types.optional(types.map(Item), {})\n        })\n        .actions(self => ({\n            afterCreate() {\n                self.items.put({\n                    id: \"1\",\n                    title: \"\",\n                    notes: [{ text: \"first note\" }, { text: \"second note\" }]\n                })\n            }\n        }))\n\n    let store!: typeof Store.Type\n    expect(() => {\n        store = Store.create({})\n    }).not.toThrow()\n    expect(getSnapshot(store)).toEqual({\n        items: {\n            \"1\": {\n                id: \"1\",\n                notes: [{ text: \"first note\" }, { text: \"second note\" }],\n                title: \"\"\n            }\n        }\n    })\n})\n\ntest(\"map can resolve late identifiers\", () => {\n    const Late = types.model({\n        id: types.identifier,\n        children: types.map(types.late((): IAnyModelType => Late))\n    })\n    const snapshot = {\n        id: \"1\",\n        children: {\n            \"2\": {\n                id: \"2\",\n                children: {}\n            }\n        }\n    }\n    expect(() => Late.create(snapshot)).not.toThrow()\n})\n\ntest(\"get should return value when key is a number\", () => {\n    const Todo = types.model(\"Todo\", {\n        todo_id: types.identifierNumber,\n        title: types.string\n    })\n    const TodoStore = types\n        .model(\"TodoStore\", {\n            todos: types.optional(types.map(Todo), {})\n        })\n        .actions(self => {\n            function addTodo(aTodo: typeof Todo.Type | typeof Todo.CreationType) {\n                self.todos.put(aTodo)\n            }\n            return {\n                addTodo\n            }\n        })\n    const todoStore = TodoStore.create({})\n\n    const todo = {\n        todo_id: 1,\n        title: \"Test\"\n    }\n\n    todoStore.addTodo(todo)\n    expect(todoStore.todos.get(1 as any as string)!.title).toEqual(\"Test\")\n})\n\ntest(\"numeric keys should work\", () => {\n    const M = types.model({\n        id: types.identifier,\n        title: \"test\"\n    })\n    const S = types.model({\n        mies: types.map(M),\n        ref: types.maybe(types.reference(M))\n    })\n\n    const s = S.create({\n        mies: {}\n    })\n    unprotect(s)\n\n    s.mies.set(7, { id: \"7\" })\n    const i7 = s.mies.get(7)!\n    expect(i7.title).toBe(\"test\")\n    expect(s.mies.has(\"7\")).toBeTruthy()\n    expect(s.mies.has(7 as any as string)).toBeTruthy()\n    expect(s.mies.get(\"7\")).toBeTruthy()\n    expect(s.mies.get(7 as any as string)).toBeTruthy()\n\n    s.mies.set(\"8\", { id: \"8\" })\n    expect(s.mies.has(\"8\")).toBeTruthy()\n    expect(s.mies.has(8 as any as string)).toBeTruthy()\n    expect(s.mies.get(\"8\")).toBeTruthy()\n    expect(s.mies.get(8 as any as string)).toBeTruthy()\n\n    expect(Array.from(s.mies.keys())).toEqual([\"7\", \"8\"])\n\n    s.mies.put({ id: \"7\", title: \"coffee\" })\n    expect(s.mies.size).toBe(2)\n    expect(s.mies.has(\"7\")).toBeTruthy()\n    expect(s.mies.has(7 as any as string)).toBeTruthy()\n    expect(s.mies.get(\"7\")).toBeTruthy()\n    expect(s.mies.get(7 as any as string)).toBeTruthy()\n    expect(i7.title).toBe(\"coffee\")\n\n    expect(s.mies.delete(8 as any as string)).toBeTruthy()\n    expect(s.mies.size).toBe(1)\n})\n\ndescribe(\"#826, adding stuff twice\", () => {\n    const Store = types\n        .model({\n            map: types.optional(types.map(types.boolean), {})\n        })\n        .actions(self => ({\n            toogleMap: (id: string) => {\n                self.map.set(id, !self.map.get(id))\n            }\n        }))\n\n    // This one pass fine 👍\n    test(\"Toogling once shouldn't throw\", () => {\n        const store = Store.create({})\n        expect(() => {\n            store.toogleMap(\"1\")\n        }).not.toThrow()\n    })\n\n    // This one throws with 'Not a child 1' error 👎\n    test(\"Toogling twice shouldn't throw\", () => {\n        const store = Store.create({})\n        expect(() => {\n            store.toogleMap(\"1\")\n            store.toogleMap(\"1\")\n        }).not.toThrow()\n    })\n})\n\ntest(\"#751 restore from snapshot should work\", () => {\n    const Submodel = types.model(\"Submodel\", {\n        id: types.identifierNumber\n    })\n\n    const Model = types.model(\"Model\", {\n        map: types.map(Submodel)\n    })\n\n    const server = Model.create({ map: {} })\n\n    // We add an item with a number id\n    unprotect(server)\n    server.map.set(1 as any as string, { id: 1 })\n\n    // We can access it using a number\n    expect(server.map.get(1 as any as string)!.id).toBe(1)\n\n    // But if we get a snapshot...\n    const snapshot = getSnapshot(server)\n    // And apply it back...\n    const browser = Model.create(snapshot)\n\n    // We can access it using a string\n    expect(server.map.get(\"1\")!.id).toBe(1)\n\n    // And as number\n    expect(server.map.get(1 as any as string)!.id).toBe(1)\n\n    expect(server.map.size).toBe(1)\n})\n\ntest(\"#1173 - detaching a map should not eliminate its children\", () => {\n    const M = types.model({})\n    const AM = types.map(M)\n    const Store = types.model({ items: AM })\n    const s = Store.create({ items: { x: {}, y: {}, z: {} } })\n    const n0 = s.items.get(\"x\")\n\n    unprotect(s)\n\n    const detachedItems = detach(s.items)\n    expect(s.items).not.toBe(detachedItems)\n    expect(s.items.size).toBe(0)\n    expect(detachedItems.size).toBe(3)\n    expect(detachedItems.get(\"x\")).toBe(n0!)\n})\n\ntest(\"#1131 - put with optional identifier\", () => {\n    const Test = types.model({\n        id: types.optional(types.identifier, () => Math.random().toString(36).substr(2)),\n        value: \"hi\"\n    })\n\n    const myMap = types.map(Test).create()\n    unprotect(myMap)\n    const val = myMap.put({})\n    expect(val.id).toBeTruthy()\n    expect(val.value).toBe(\"hi\")\n})\n\n/**\n * This test exercises the TypeScript types fo `MSTMap`, to ensure that our typings stay consistent. In PR #2072,\n * we changed from accepting `[string, any][] | IKeyValueMap<any> | Map<string, any> | undefined,` in `initialdata`\n * to accepting `IObservableMapInitialValues<string, any> | undefined,`.\n *\n * This test demonstrates backwards compatibility for the change, and will let us know if anything changes and breaks\n * if we ever update those types as well, or if MobX changes the exported `IObservableMapInitialValues` type.\n *\n * It looks like `[string, any][]` and `Map<string, any>` are actually not supported, so we just test the `IKeyValueMap<any>` and `undefined` cases\n * for now. See https://github.com/mobxjs/mobx-state-tree/pull/2072#issuecomment-1747482100\n */\ndescribe(\"#2072 - IObservableMapInitialValues types should work correctly\", () => {\n    it(\"should accept IKeyValueMap<any>\", () => {\n        const initialData = {\n            \"1\": \"Tyler\",\n            \"2\": \"Jamon\"\n        }\n\n        const mapInstance = types.map(types.string).create(initialData)\n        expect(mapInstance.size).toBe(2)\n    })\n    it(\"should accept undefined\", () => {\n        const mapInstance = types.map(types.string).create(undefined)\n        expect(mapInstance.size).toBe(0)\n    })\n})\n"
  },
  {
    "path": "__tests__/core/model.test.ts",
    "content": "import { applySnapshot, getSnapshot, types } from \"../../src\"\nimport { Hook } from \"../../src/internal\"\nimport { describe, expect, it, jest, test } from \"bun:test\"\n\ndescribe(\"Model instantiation\", () => {\n    describe(\"Model name\", () => {\n        test(\"Providing a string as the first argument should set it as the model's name.\", () => {\n            const Model = types.model(\"Name\", {})\n\n            expect(Model.name).toBe(\"Name\")\n        })\n        test(\"Providing an empty string as the first argument should set it as the model's name.\", () => {\n            const Model = types.model(\"\", {})\n\n            expect(Model.name).toBe(\"\")\n        })\n        describe(\"Providing a non-string argument as the first argument should set the model's name as 'AnonymousModel'.\", () => {\n            const testCases = [\n                {},\n                null,\n                undefined,\n                1,\n                true,\n                [],\n                function () {},\n                new Date(),\n                /a/,\n                new Map(),\n                new Set(),\n                Symbol(),\n                new Error(),\n                NaN,\n                Infinity\n            ]\n\n            testCases.forEach(testCase => {\n                test(`Providing ${JSON.stringify(\n                    testCase\n                )} as the first argument should set the model's name as 'AnonymousModel'.`, () => {\n                    const Model = types.model(testCase as any)\n\n                    expect(Model.name).toBe(\"AnonymousModel\")\n                })\n            })\n        })\n    })\n    describe(\"Model properties\", () => {\n        test(\"Providing a string as the first argument and an object as the second argument should use the object's properties in the model.\", () => {\n            const Model = types.model(\"name\", {\n                prop1: \"prop1\",\n                prop2: 2\n            })\n\n            expect(Model.properties).toHaveProperty(\"prop1\")\n            expect(Model.properties).toHaveProperty(\"prop2\")\n        })\n        test(\"Providing an object as the first argument should parse and use its properties.\", () => {\n            const Model = types.model({\n                prop1: \"prop1\",\n                prop2: 2\n            })\n\n            expect(Model.properties).toHaveProperty(\"prop1\")\n            expect(Model.properties).toHaveProperty(\"prop2\")\n        })\n        test(\"Providing a string as the first argument and a falsy value as the second argument should result in an empty set of properties.\", () => {\n            const Model = types.model(\"name\", null as any)\n\n            expect(Model.properties).toEqual({})\n        })\n        test(\"Model should not mutate properties object\", () => {\n            const properties = {\n                prop1: \"prop1\",\n                prop2: 2\n            }\n            const Model = types.model(\"name\", properties)\n\n            expect(properties).toEqual({\n                prop1: \"prop1\",\n                prop2: 2\n            })\n        })\n    })\n    describe(\"Model identifier\", () => {\n        test(\"If no identifier attribute is provided, the identifierAttribute should be undefined.\", () => {\n            const Model = types.model(\"name\", {})\n\n            expect(Model.identifierAttribute).toBeUndefined()\n        })\n        test(\"If an identifier attribute is provided, the identifierAttribute should be set for the object.\", () => {\n            const Model = types.model(\"name\", {\n                id: types.identifier\n            })\n\n            expect(Model.identifierAttribute).toBe(\"id\")\n        })\n        test(\"If an identifier attribute has already been provided, an error should be thrown when attempting to provide a second one.\", () => {\n            expect(() => {\n                types.model(\"name\", {\n                    id: types.identifier,\n                    id2: types.identifier\n                })\n            }).toThrow(\n                \"[mobx-state-tree] Cannot define property 'id2' as object identifier, property 'id' is already defined as identifier property\"\n            )\n        })\n    })\n    describe(\"Edge case behavior\", () => {\n        describe(\"when we provide no arguments to the function\", () => {\n            test(\"the model will be named AnonymousModel\", () => {\n                const Model = types.model()\n\n                expect(Model.name).toBe(\"AnonymousModel\")\n            })\n            test(\"the model will have no properties\", () => {\n                const Model = types.model()\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({})\n            })\n        })\n        test(\"the model will have no properties\", () => {\n            const Model = types.model()\n\n            const modelSnapshot = getSnapshot(Model.create())\n            expect(modelSnapshot).toEqual({})\n        })\n        if (process.env.NODE_ENV !== \"production\") {\n            test(\"it should not throw an error\", () => {\n                expect(() => {\n                    types.model()\n                }).not.toThrow()\n            })\n        }\n    })\n    describe(\"when we provide an invalid name value, but a valid property object\", () => {\n        if (process.env.NODE_ENV === \"production\") {\n            test(\"the model will be named AnonymousModel\", () => {\n                const Model = types.model(null as any, {\n                    prop1: \"prop1\",\n                    prop2: 2\n                })\n\n                expect(Model.name).toBe(\"AnonymousModel\")\n            })\n            test(\"the model will have no properties\", () => {\n                const Model = types.model(null as any, {\n                    prop1: \"prop1\",\n                    prop2: 2\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                // @ts-expect-error - we explicitly allowed an invalid input, so we expect an empty object, but TS doesn't.\n                expect(modelSnapshot).toEqual({})\n            })\n        } else {\n            test(\"it should complain about invalid name\", () => {\n                expect(() => {\n                    types.model(null as any, {\n                        prop1: \"prop1\",\n                        prop2: 2\n                    })\n                }).toThrow(\n                    \"[mobx-state-tree] Model creation failed. First argument must be a string when two arguments are provided\"\n                )\n            })\n        }\n    })\n    describe(\"when we provide three arguments to the function\", () => {\n        test(\"the model gets the correct name\", () => {\n            // @ts-ignore\n            const Model = types.model(\"name\", {}, {})\n\n            expect(Model.name).toBe(\"name\")\n        })\n        test(\"the model gets the correct properties\", () => {\n            const Model = types.model(\n                \"name\",\n                {\n                    prop1: \"prop1\",\n                    prop2: 2\n                },\n                // @ts-ignore\n                {}\n            )\n\n            const modelSnapshot = getSnapshot(Model.create())\n            expect(modelSnapshot).toEqual({\n                prop1: \"prop1\",\n                prop2: 2\n            })\n        })\n    })\n    test(\"it should call preProcessSnapshot with the correct argument\", () => {\n        const onSnapshot = jest.fn((snapshot: any) => {\n            return {\n                val: snapshot.val + 1\n            }\n        })\n\n        const Model = types\n            .model({\n                val: types.number\n            })\n            .preProcessSnapshot(onSnapshot)\n\n        const model = Model.create({ val: 0 })\n        applySnapshot(model, { val: 1 })\n        expect(onSnapshot).toHaveBeenLastCalledWith({ val: 1 })\n    })\n\n    describe(\"Should show a friendly message when a model has a property overridden by\", () => {\n        test(\"a view\", () => {\n            const UserModel = types\n                .model(\"UserModel\", {\n                    id: types.identifier,\n                    name: types.string\n                })\n                .views(user => ({\n                    get name() {\n                        return user.name\n                    }\n                }))\n\n            expect(() =>\n                UserModel.create({\n                    id: \"chakri\",\n                    name: \"Subramanya Chakravarthy\"\n                })\n            ).toThrow(\"[mobx-state-tree] 'name' is a property and cannot be declared as a view\")\n        })\n\n        test(\"an action\", () => {\n            const StringSet = types\n                .model(\"StringSet\", {\n                    setName: types.string,\n                    items: types.array(types.string)\n                })\n                .actions(self => ({\n                    setName(name: string) {\n                        self.setName = name\n                    }\n                }))\n\n            expect(() =>\n                StringSet.create({\n                    setName: \"Fruits\",\n                    items: [\"banana\", \"apple\"]\n                })\n            ).toThrow(\n                \"[mobx-state-tree] 'setName' is a property and cannot be declared as an action\"\n            )\n        })\n\n        test(\"a volatile\", () => {\n            const UserModel = types\n                .model(\"UserModel\", {\n                    id: types.identifier,\n                    name: types.string\n                })\n                .volatile(_self => ({\n                    name: \"Subramanya Chakravarthy\"\n                }))\n\n            expect(() =>\n                UserModel.create({\n                    id: \"chakri\",\n                    name: \"Subramanya Chakravarthy\"\n                })\n            ).toThrow(\n                \"[mobx-state-tree] 'name' is a property and cannot be declared as volatile state\"\n            )\n        })\n    })\n\n    describe(\"with all of the property types\", () => {\n        const IdentifiedWithString = types.model({ id: types.identifier })\n        const IdentifiedWithNumber = types.model({ id: types.identifierNumber })\n\n        const Custom = types.custom<string, string>({\n            name: \"angle bracketed\",\n            fromSnapshot(snapshot, _env) {\n                return `<${snapshot}>`\n            },\n            toSnapshot(value) {\n                return value.slice(1, -1)\n            },\n            isTargetType(value): boolean {\n                return value.startsWith(\"<\") && value.endsWith(\">\")\n            },\n            getValidationMessage(snapshot): string {\n                if (typeof snapshot == \"string\") return \"\"\n                throw new Error(`${snapshot} missing surrounding angle brackets`)\n            }\n        })\n\n        const Everything = types.model({\n            boolean: types.boolean,\n            custom: Custom,\n            Date: types.Date,\n            enumeration: types.enumeration([\"A\", \"B\"]),\n            float: types.float,\n            finite: types.finite,\n            frozen: types.frozen<{ s: string }>(),\n            integer: types.integer,\n            late: types.late(() => types.string),\n            lazy: types.lazy(\"lazy\", {\n                loadType: () => Promise.resolve(types.string),\n                shouldLoadPredicate: () => true\n            }),\n            literal: types.literal(\"literal\"),\n            maybe: types.maybe(types.string),\n            maybeNull: types.maybeNull(types.number),\n            null: types.null,\n            number: types.number,\n            optional: types.optional(types.string, \"default\"),\n            reference: types.reference(IdentifiedWithString),\n            refinement: types.refinement(types.string, s => s.length > 2),\n            string: types.string,\n            safeReference: types.safeReference(IdentifiedWithNumber),\n            undefined: types.undefined,\n            union: types.union(types.string, types.number)\n        } satisfies Record<\n            Exclude<\n                keyof typeof types,\n                | \"compose\"\n                | \"model\"\n                | \"identifier\"\n                | \"identifierNumber\"\n                | \"bigint\"\n                | \"map\"\n                | \"array\"\n                | \"snapshotProcessor\"\n            >,\n            any\n        >)\n\n        const Root = types.model({\n            everything: types.snapshotProcessor(Everything, {\n                preProcessor(snapshot) {\n                    if (snapshot.refinement.length < 2) {\n                        return { ...snapshot, refinement: \"<broken>\" }\n                    }\n                    return snapshot\n                }\n            }),\n            mapOfStrings: types.map(IdentifiedWithString),\n            arrayOfNumbers: types.array(IdentifiedWithNumber)\n        })\n\n        it(\"does not throw with input snapshots\", () => {\n            const value = Root.create({\n                everything: {\n                    boolean: true,\n                    custom: \"custom\",\n                    Date: 0,\n                    enumeration: \"A\",\n                    float: 1.23,\n                    finite: 1,\n                    frozen: { s: \"test\" },\n                    integer: 1,\n                    late: \"test\",\n                    lazy: \"test\",\n                    literal: \"literal\",\n                    maybe: \"test\",\n                    maybeNull: 1,\n                    null: null,\n                    number: 1,\n                    optional: \"test\",\n                    reference: \"id-a\",\n                    refinement: \"test\",\n                    string: \"test\",\n                    safeReference: 1,\n                    undefined: undefined,\n                    union: \"test\"\n                },\n                mapOfStrings: {\n                    \"id-a\": { id: \"id-a\" }\n                },\n                arrayOfNumbers: [{ id: 1 }]\n            })\n\n            expect(getSnapshot(value)).toEqual({\n                everything: {\n                    boolean: true,\n                    custom: \"custom\",\n                    Date: 0,\n                    enumeration: \"A\",\n                    float: 1.23,\n                    finite: 1,\n                    frozen: { s: \"test\" },\n                    integer: 1,\n                    late: \"test\",\n                    lazy: \"test\",\n                    literal: \"literal\",\n                    maybe: \"test\",\n                    maybeNull: 1,\n                    null: null,\n                    number: 1,\n                    optional: \"test\",\n                    reference: \"id-a\",\n                    refinement: \"test\",\n                    string: \"test\",\n                    safeReference: 1,\n                    undefined: undefined,\n                    union: \"test\"\n                },\n                mapOfStrings: {\n                    \"id-a\": { id: \"id-a\" }\n                },\n                arrayOfNumbers: [{ id: 1 }]\n            })\n        })\n\n        it(\"does not throw with input instances\", () => {\n            const instanceA = IdentifiedWithString.create({ id: \"id-a\" })\n            const instance1 = IdentifiedWithNumber.create({ id: 1 })\n            const value = Root.create({\n                everything: {\n                    boolean: types.boolean.create(true),\n                    custom: Custom.create(\"custom\"),\n                    Date: types.Date.create(0),\n                    enumeration: types.enumeration([\"A\", \"B\"]).create(\"A\"),\n                    float: types.float.create(1.23),\n                    finite: types.finite.create(1),\n                    frozen: types.frozen<{ s: string }>().create({ s: \"test\" }),\n                    integer: types.integer.create(1),\n                    late: types.string.create(\"test\"),\n                    lazy: types.string.create(\"test\"),\n                    literal: types.literal(\"literal\").create(\"literal\"),\n                    maybe: types.maybe(types.string).create(\"test\"),\n                    maybeNull: types.maybeNull(types.number).create(1),\n                    null: types.null.create(null),\n                    number: types.number.create(1),\n                    optional: types.optional(types.string, \"default\").create(\"test\"),\n                    reference: instanceA,\n                    refinement: types.refinement(types.string, s => s.length > 2).create(\"test\"),\n                    string: types.string.create(\"test\"),\n                    safeReference: instance1,\n                    undefined: types.undefined.create(undefined),\n                    union: types.union(types.string, types.number).create(\"test\")\n                },\n                mapOfStrings: { \"id-a\": instanceA },\n                arrayOfNumbers: [instance1]\n            })\n\n            expect(getSnapshot(value)).toEqual({\n                everything: {\n                    boolean: true,\n                    custom: \"custom\",\n                    Date: 0,\n                    enumeration: \"A\",\n                    float: 1.23,\n                    finite: 1,\n                    frozen: { s: \"test\" },\n                    integer: 1,\n                    late: \"test\",\n                    lazy: \"test\",\n                    literal: \"literal\",\n                    maybe: \"test\",\n                    maybeNull: 1,\n                    null: null,\n                    number: 1,\n                    optional: \"test\",\n                    reference: \"id-a\",\n                    refinement: \"test\",\n                    string: \"test\",\n                    safeReference: 1,\n                    undefined: undefined,\n                    union: \"test\"\n                },\n                mapOfStrings: {\n                    \"id-a\": { id: \"id-a\" }\n                },\n                arrayOfNumbers: [{ id: 1 }]\n            })\n        })\n    })\n})\ndescribe(\"Model properties objects\", () => {\n    describe(\"when a user names a property the same as an MST lifecycle hook\", () => {\n        test(\"it throws an error\", () => {\n            const hookValues = Object.values(Hook)\n\n            hookValues.forEach(hook => {\n                expect(() => {\n                    types.model({\n                        [hook]: types.string\n                    })\n                }).toThrow()\n            })\n        })\n    })\n    describe(\"when a user attempts to define a property with the get keyword\", () => {\n        test(\"it throws an error\", () => {\n            expect(() => {\n                types.model({\n                    get foo() {\n                        return \"bar\"\n                    }\n                })\n            }).toThrow(\n                \"[mobx-state-tree] Getters are not supported as properties. Please use views instead\"\n            )\n        })\n    })\n    describe(\"when a user attempts to define a property with null as the value\", () => {\n        test(\"it throws an error\", () => {\n            expect(() => {\n                types.model({\n                    foo: null as any\n                })\n            }).toThrow(\n                \"[mobx-state-tree] The default value of an attribute cannot be null or undefined as the type cannot be inferred. Did you mean `types.maybe(someType)`?\"\n            )\n        })\n    })\n    describe(\"when a user attempts to define a property with undefined as the value\", () => {\n        test(\"it throws an error\", () => {\n            expect(() => {\n                types.model({\n                    foo: undefined as any\n                })\n            }).toThrow(\n                \"[mobx-state-tree] The default value of an attribute cannot be null or undefined as the type cannot be inferred. Did you mean `types.maybe(someType)`?\"\n            )\n        })\n    })\n    describe(\"when a user defines a property using a primitive value (not null or undefined)\", () => {\n        describe(\"and the primitive value is a string\", () => {\n            test(\"it converts a string to an optional string\", () => {\n                const Model = types.model({\n                    foo: \"bar\"\n                })\n\n                const modelDescription = Model.describe()\n                expect(modelDescription).toBe(\"{ foo: string? }\")\n            })\n            test(\"it uses the primitive value as the default value\", () => {\n                const Model = types.model({\n                    foo: \"bar\"\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({\n                    foo: \"bar\"\n                })\n            })\n        })\n        describe(\"and the primitive value is a number\", () => {\n            test(\"it converts a number to an optional number\", () => {\n                const Model = types.model({\n                    foo: 1\n                })\n\n                const modelDescription = Model.describe()\n                expect(modelDescription).toBe(\"{ foo: number? }\")\n            })\n            test(\"it uses the primitive value as the default value\", () => {\n                const Model = types.model({\n                    foo: 1\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({\n                    foo: 1\n                })\n            })\n        })\n        describe(\"and the primitive value is a boolean\", () => {\n            test(\"it converts a boolean to an optional boolean\", () => {\n                const Model = types.model({\n                    foo: true\n                })\n\n                const modelDescription = Model.describe()\n                expect(modelDescription).toBe(\"{ foo: boolean? }\")\n            })\n            test(\"it uses the primitive value as the default value\", () => {\n                const Model = types.model({\n                    foo: true\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({\n                    foo: true\n                })\n            })\n        })\n        describe(\"and the primitive value is a date\", () => {\n            test(\"it converts a date to an optional date\", () => {\n                const Model = types.model({\n                    foo: new Date()\n                })\n\n                const modelDescription = Model.describe()\n                expect(modelDescription).toBe(\"{ foo: Date? }\")\n            })\n            test(\"it sets a default value with the date in unix milliseconds timestamp\", () => {\n                const date = new Date(\"2023-07-24T04:26:04.701Z\")\n                const Model = types.model({\n                    foo: date\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({\n                    foo: 1690172764701\n                })\n            })\n        })\n    })\n    describe(\"when a user defines a property using a complex type\", () => {\n        describe('and that type is \"types.map\"', () => {\n            test(\"it sets the default value to an empty map\", () => {\n                const Model = types.model({\n                    foo: types.map(types.string)\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({\n                    foo: {}\n                })\n            })\n        })\n        describe('and that type is \"types.array\"', () => {\n            test(\"it sets the default value to an empty array\", () => {\n                const Model = types.model({\n                    foo: types.array(types.string)\n                })\n\n                const modelSnapshot = getSnapshot(Model.create())\n                expect(modelSnapshot).toEqual({\n                    foo: []\n                })\n            })\n        })\n        describe(\"and that type is another model\", () => {\n            test(\"it sets the default value to the default of that model\", () => {\n                const Todo = types.model({\n                    task: types.optional(types.string, \"test\")\n                })\n\n                const TodoStore = types.model(\"TodoStore\", {\n                    todo1: types.optional(Todo, () => Todo.create())\n                })\n\n                const modelSnapshot = getSnapshot(TodoStore.create())\n                expect(modelSnapshot).toEqual({\n                    todo1: {\n                        task: \"test\"\n                    }\n                })\n            })\n        })\n    })\n    describe(\"when a user defines a property using a function\", () => {\n        if (process.env.NODE_ENV !== \"production\") {\n            test(\"it throws an error when not in production\", () => {\n                expect(() => {\n                    // @ts-ignore\n                    types.model({\n                        foo: () => \"bar\"\n                    })\n                }).toThrow(\n                    \"[mobx-state-tree] Invalid type definition for property 'foo', it looks like you passed a function. Did you forget to invoke it, or did you intend to declare a view / action?\"\n                )\n            })\n        }\n    })\n    describe(\"when a user defines a property using a plain JavaScript object\", () => {\n        if (process.env.NODE_ENV !== \"production\") {\n            test(\"it throws an error when not in production\", () => {\n                expect(() => {\n                    // @ts-ignore\n                    types.model({\n                        foo: {}\n                    })\n                }).toThrow()\n            })\n        }\n    })\n    describe(\"when a user uses `.props` to create a child model\", () => {\n        it(\"does not modify the parent properties\", () => {\n            const Parent = types.model({\n                first: types.string\n            })\n\n            const Child = Parent.props({\n                second: types.string\n            })\n\n            expect(Parent.properties).not.toHaveProperty(\"second\")\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/name.test.ts",
    "content": "import { types } from \"../../src\"\nimport { getDebugName } from \"mobx\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"it should have a debug name\", () => {\n    const Model = types.model(\"Name\")\n\n    const model = Model.create()\n    const array = types.array(Model).create()\n    const map = types.map(Model).create()\n\n    expect(getDebugName(model)).toBe(\"Name\")\n    expect(getDebugName(array)).toBe(\"Name[]\")\n    expect(getDebugName(map)).toBe(\"Map<string, Name>\")\n})\n"
  },
  {
    "path": "__tests__/core/node.test.ts",
    "content": "import {\n    getPath,\n    getSnapshot,\n    getParent,\n    hasParent,\n    getRoot,\n    getIdentifier,\n    getPathParts,\n    isAlive,\n    clone,\n    getType,\n    getChildType,\n    recordActions,\n    recordPatches,\n    types,\n    destroy,\n    unprotect,\n    hasParentOfType,\n    getParentOfType,\n    detach,\n    getNodeId\n} from \"../../src\"\n\nimport { autorun, configure } from \"mobx\"\nimport { expect, test } from \"bun:test\"\n\n// getParent\ntest(\"it should resolve to the parent instance\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(getParent<any>(row)).toEqual(doc.rows)\n})\n// hasParent\ntest(\"it should check for parent instance\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(hasParent(row)).toEqual(true)\n})\ntest(\"it should check for parent instance (unbound)\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const row = Row.create()\n    expect(hasParent(row)).toEqual(false)\n})\n// getParentOfType\ntest(\"it should resolve to the given parent instance\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Cell = types.model({})\n    const Row = types.model({\n        cells: types.optional(types.array(Cell), [])\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create({\n        rows: [\n            {\n                cells: [{}]\n            }\n        ]\n    })\n    expect(getParentOfType(doc.rows[0].cells[0], Document)).toEqual(doc)\n})\ntest(\"it should throw if there is not parent of type\", () => {\n    const Cell = types.model({})\n    const Row = types.model({\n        cells: types.optional(types.array(Cell), [])\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const row = Row.create({\n        cells: [{}]\n    })\n    expect(() => getParentOfType(row.cells[0], Document)).toThrow(\n        \"[mobx-state-tree] Failed to find the parent of AnonymousModel@/cells/0 of a given type\"\n    )\n})\n// hasParentOfType\ntest(\"it should check for parent instance of given type\", () => {\n    const Cell = types.model({})\n    const Row = types.model({\n        cells: types.optional(types.array(Cell), [])\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create({\n        rows: [\n            {\n                cells: [{}]\n            }\n        ]\n    })\n    expect(hasParentOfType(doc.rows[0].cells[0], Document)).toEqual(true)\n})\ntest(\"it should check for parent instance of given type (unbound)\", () => {\n    const Cell = types.model({})\n    const Row = types.model({\n        cells: types.optional(types.array(Cell), [])\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const row = Row.create({\n        cells: [{}]\n    })\n    expect(hasParentOfType(row.cells[0], Document)).toEqual(false)\n})\n// getRoot\ntest(\"it should resolve to the root of an object\", () => {\n    const Row = types.model(\"Row\", {\n        article_id: 0\n    })\n    const Document = types.model(\"Document\", {\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(getRoot<any>(row)).toBe(doc)\n})\n// getIdentifier\ntest(\"it should resolve to the identifier of the object\", () => {\n    const Document = types.model(\"Document\", {\n        id: types.identifier\n    })\n    const doc = Document.create({\n        id: \"document_1\"\n    })\n    // get identifier of object\n    expect(getIdentifier(doc)).toBe(\"document_1\")\n})\n// getPath\ntest(\"it should resolve the path of an object\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(getPath(row)).toEqual(\"/rows/0\")\n})\n// getPathParts\ntest(\"it should resolve the path of an object\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(getPathParts(row)).toEqual([\"rows\", \"0\"])\n})\ntest(\"it should resolve parents\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(hasParent(row)).toBe(true) // array\n    expect(hasParent(row, 2)).toBe(true) // row\n    expect(hasParent(row, 3)).toBe(false)\n    expect(getParent(row) === doc.rows).toBe(true) // array\n    expect(getParent(row, 2) === doc).toBe(true) // row\n    expect(() => getParent(row, 3)).toThrow(\n        \"[mobx-state-tree] Failed to find the parent of AnonymousModel@/rows/0 at depth 3\"\n    )\n})\n// clone\ntest(\"it should clone a node\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    const cloned = clone(doc)\n    expect(doc).toEqual(cloned)\n    expect(getSnapshot(doc)).toEqual(getSnapshot(cloned))\n})\ntest(\"it should be possible to clone a dead object\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Task = types.model(\"Task\", {\n        x: types.string\n    })\n    const a = Task.create({ x: \"a\" })\n    const store = types\n        .model({\n            todos: types.optional(types.array(Task), [])\n        })\n        .create({\n            todos: [a]\n        })\n    unprotect(store)\n    expect(store.todos.slice()).toEqual([a])\n    expect(isAlive(a)).toBe(true)\n    store.todos.splice(0, 1)\n    expect(isAlive(a)).toBe(false)\n    const a2 = clone(a)\n    store.todos.splice(0, 0, a2)\n    expect(store.todos[0].x).toBe(\"a\")\n})\n// getModelFactory\ntest(\"it should return the model factory\", () => {\n    const Document = types.model({\n        customer_id: 0\n    })\n    const doc = Document.create()\n    expect(getType(doc)).toEqual(Document)\n})\n// getChildModelFactory\ntest(\"it should return the child model factory\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const ArrayOfRow = types.optional(types.array(Row), [])\n    const Document = types.model({\n        rows: ArrayOfRow\n    })\n    const doc = Document.create()\n    expect(getChildType(doc, \"rows\")).toEqual(ArrayOfRow)\n})\ntest(\"a node can exists only once in a tree\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        rows: types.optional(types.array(Row), []),\n        foos: types.optional(types.array(Row), [])\n    })\n    const doc = Document.create()\n    unprotect(doc)\n    const row = Row.create()\n    doc.rows.push(row)\n    expect(() => {\n        doc.foos.push(row)\n    }).toThrow(\n        \"[mobx-state-tree] Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '/foos/0', but it lives already at '/rows/0'\"\n    )\n})\ntest(\"make sure array filter works properly\", () => {\n    const Row = types.model({\n        done: false\n    })\n    const Document = types\n        .model({\n            rows: types.optional(types.array(Row), [])\n        })\n        .actions(self => {\n            function clearDone() {\n                self.rows.filter(row => row.done === true).forEach(destroy)\n            }\n            return {\n                clearDone\n            }\n        })\n    const doc = Document.create()\n    unprotect(doc)\n    const a = Row.create({ done: true })\n    const b = Row.create({ done: false })\n    doc.rows.push(a)\n    doc.rows.push(b)\n    doc.clearDone()\n    expect(getSnapshot(doc)).toEqual({ rows: [{ done: false }] })\n})\n// === RECORD PATCHES ===\ntest(\"it can record and replay patches\", () => {\n    const Row = types.model({\n        article_id: 0\n    })\n    const Document = types.model({\n        customer_id: 0,\n        rows: types.optional(types.array(Row), [])\n    })\n    const source = Document.create()\n    unprotect(source)\n    const target = Document.create()\n    const recorder = recordPatches(source)\n    source.customer_id = 1\n    source.rows.push(Row.create({ article_id: 1 }))\n    recorder.replay(target)\n    expect(getSnapshot(source)).toEqual(getSnapshot(target))\n})\n// === RECORD ACTIONS ===\ntest(\"it can record and replay actions\", () => {\n    const Row = types\n        .model({\n            article_id: 0\n        })\n        .actions(self => {\n            function setArticle(article_id: number) {\n                self.article_id = article_id\n            }\n            return {\n                setArticle\n            }\n        })\n    const Document = types\n        .model({\n            customer_id: 0,\n            rows: types.optional(types.array(Row), [])\n        })\n        .actions(self => {\n            function setCustomer(customer_id: number) {\n                self.customer_id = customer_id\n            }\n            function addRow() {\n                self.rows.push(Row.create())\n            }\n            return {\n                setCustomer,\n                addRow\n            }\n        })\n    const source = Document.create()\n    const target = Document.create()\n    const recorder = recordActions(source)\n    source.setCustomer(1)\n    source.addRow()\n    source.rows[0].setArticle(1)\n    recorder.replay(target)\n    expect(getSnapshot(source)).toEqual(getSnapshot(target))\n})\n\ntest(\"Liveliness issue #683\", () => {\n    const User = types.model({ id: types.identifierNumber, name: types.string })\n\n    const Users = types\n        .model({\n            list: types.map(User)\n        })\n        .actions(self => ({\n            put(aUser: typeof User.CreationType | typeof User.Type) {\n                // if (self.has(user.id)) detach(self.get(user.id));\n                self.list.put(aUser)\n            },\n            get(id: string) {\n                return self.list.get(id)\n            },\n            has(id: string) {\n                return self.list.has(id)\n            }\n        }))\n\n    const users = Users.create({\n        list: {\n            1: { name: \"Name\", id: 1 }\n        }\n    })\n    const user = users.get(\"1\")\n    expect(user!.name).toBe(\"Name\")\n\n    users.put({ id: 1, name: \"NameX\" })\n    expect(user!.name).toBe(\"NameX\")\n    expect(users.get(\"1\")!.name).toBe(\"NameX\")\n})\n\ntest(\"triggers on changing paths - 1\", () => {\n    const Todo = types.model({\n        title: types.string\n    })\n    const App = types\n        .model({\n            todos: types.array(Todo)\n        })\n        .actions(self => ({\n            do(fn: () => void) {\n                fn()\n            }\n        }))\n\n    const t1 = Todo.create({ title: \"t1 \" })\n    const t2 = Todo.create({ title: \"t2 \" })\n\n    const app = App.create({\n        todos: [t1]\n    })\n\n    const events: string[] = []\n    const d1 = autorun(() => {\n        events.push(\"t1@\" + getPath(t1))\n    })\n    const d2 = autorun(() => {\n        events.push(\"t2@\" + getPath(t2))\n    })\n\n    expect(events.splice(0)).toEqual([\"t1@/todos/0\", \"t2@\"])\n    app.do(() => {\n        app.todos.unshift(t2)\n    })\n    expect(events.splice(0)).toEqual([\"t2@/todos/0\", \"t1@/todos/1\"])\n    app.do(() => {\n        detach(t2)\n    })\n    expect(events.splice(0)).toEqual([\"t1@/todos/0\", \"t2@\"])\n\n    app.do(() => {\n        app.todos.splice(0)\n    })\n    expect(events.splice(0)).toEqual([\"t1@\"])\n})\n\ntest(\"getNodeId works\", () => {\n    const M = types.model({})\n    const m1 = M.create()\n    const m2 = M.create()\n    const m1Id = getNodeId(m1)\n    const m2Id = getNodeId(m2)\n    expect(m1Id).toBeGreaterThan(0)\n    expect(m2Id).toBe(m1Id + 1)\n})\n"
  },
  {
    "path": "__tests__/core/number.test.ts",
    "content": "import { t } from \"../../src\"\nimport { Hook, NodeLifeCycle } from \"../../src/internal\"\nimport { describe, it, expect, test } from \"bun:test\"\n\ndescribe(\"types.number\", () => {\n    describe(\"methods\", () => {\n        describe(\"create\", () => {\n            describe(\"with no arguments\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    it(\"should throw an error in development\", () => {\n                        expect(() => {\n                            t.number.create()\n                        }).toThrow()\n                    })\n                }\n            })\n            describe(\"with a number argument\", () => {\n                it(\"should return a number\", () => {\n                    const n = t.number.create(1)\n                    expect(typeof n).toBe(\"number\")\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                // Keep in mind, Infinity and NaN are treated as numbers in JavaScript, so we won't test for them here.\n                const testCases = [\n                    null,\n                    undefined,\n                    \"string\",\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                if (process.env.NODE_ENV !== \"production\") {\n                    testCases.forEach(testCase => {\n                        it(`should throw an error when passed ${JSON.stringify(testCase)}`, () => {\n                            expect(() => {\n                                t.number.create(testCase as any)\n                            }).toThrow()\n                        })\n                    })\n                }\n            })\n        })\n        describe(\"describe\", () => {\n            it(\"should return the value 'number'\", () => {\n                const description = t.number.describe()\n                expect(description).toBe(\"number\")\n            })\n        })\n        describe(\"getSnapshot\", () => {\n            it(\"should return the value passed in\", () => {\n                const n = t.number.instantiate(null, \"\", {}, 1)\n                const snapshot = t.number.getSnapshot(n)\n                expect(snapshot).toBe(1)\n            })\n        })\n        describe(\"getSubtype\", () => {\n            it(\"should return null\", () => {\n                const subtype = t.number.getSubTypes()\n                expect(subtype).toBe(null)\n            })\n        })\n        describe(\"instantiate\", () => {\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"with invalid arguments\", () => {\n                    it(\"should not throw an error\", () => {\n                        expect(() => {\n                            // @ts-ignore\n                            t.number.instantiate()\n                        }).not.toThrow()\n                    })\n                })\n            }\n            describe(\"with a number argument\", () => {\n                it(\"should return an object\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    expect(typeof n).toBe(\"object\")\n                })\n            })\n        })\n        describe(\"is\", () => {\n            describe(\"with a number argument\", () => {\n                it(\"should return true\", () => {\n                    const result = t.number.is(1)\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                // Keep in mind, Infinity and NaN are treated as numbers in JavaScript, so we won't test for them here.\n                const testCases = [\n                    null,\n                    undefined,\n                    \"string\",\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = t.number.is(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        describe(\"isAssignableFrom\", () => {\n            describe(\"with a number argument\", () => {\n                it(\"should return true\", () => {\n                    const result = t.number.isAssignableFrom(t.number)\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    t.Date,\n                    t.boolean,\n                    t.finite,\n                    t.float,\n                    t.identifier,\n                    t.identifierNumber,\n                    t.integer,\n                    t.null,\n                    t.string,\n                    t.undefined\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = t.number.isAssignableFrom(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        // TODO: we need to test this, but to be honest I'm not sure what the expected behavior is on single number nodes.\n        describe.skip(\"reconcile\", () => {})\n        describe(\"validate\", () => {\n            describe(\"with a number argument\", () => {\n                it(\"should return with no validation errors\", () => {\n                    const result = t.number.validate(1, [])\n                    expect(result).toEqual([])\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                // Keep in mind, Infinity and NaN are treated as numbers in JavaScript, so we won't test for them here.\n                const testCases = [\n                    null,\n                    undefined,\n                    \"string\",\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error()\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return with a validation error when passed ${JSON.stringify(\n                        testCase\n                    )}`, () => {\n                        const result = t.number.validate(testCase as any, [])\n                        expect(result).toEqual([\n                            {\n                                context: [],\n                                message: \"Value is not a number\",\n                                value: testCase\n                            }\n                        ])\n                    })\n                })\n            })\n        })\n    })\n    describe(\"properties\", () => {\n        describe(\"flags\", () => {\n            test(\"return the correct value\", () => {\n                const flags = t.number.flags\n                expect(flags).toBe(2)\n            })\n        })\n        describe(\"identifierAttribute\", () => {\n            // We don't have a way to set the identifierAttribute on a primitive type, so this should return undefined.\n            test(\"returns undefined\", () => {\n                const identifierAttribute = t.number.identifierAttribute\n                expect(identifierAttribute).toBeUndefined()\n            })\n        })\n        describe(\"isType\", () => {\n            test(\"returns true\", () => {\n                const isType = t.number.isType\n                expect(isType).toBe(true)\n            })\n        })\n        describe(\"name\", () => {\n            test('returns \"number\"', () => {\n                const name = t.number.name\n                expect(name).toBe(\"number\")\n            })\n        })\n    })\n    describe(\"instance\", () => {\n        describe(\"methods\", () => {\n            describe(\"aboutToDie\", () => {\n                it(\"calls the beforeDetach hook\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    let called = false\n                    n.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n                    n.aboutToDie()\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"die\", () => {\n                it(\"kills the node\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    n.die()\n                    expect(n.isAlive).toBe(false)\n                })\n                it(\"should mark the node as dead\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    n.die()\n                    expect(n.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"finalizeCreation\", () => {\n                it(\"should mark the node as finalized\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    n.finalizeCreation()\n                    expect(n.state).toBe(NodeLifeCycle.FINALIZED)\n                })\n            })\n            describe(\"finalizeDeath\", () => {\n                it(\"should mark the node as dead\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    n.finalizeDeath()\n                    expect(n.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"getReconciliationType\", () => {\n                it(\"should return the correct type\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    const type = n.getReconciliationType()\n                    expect(type).toBe(t.number)\n                })\n            })\n            describe(\"getSnapshot\", () => {\n                it(\"should return the value passed in\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    const snapshot = n.getSnapshot()\n                    expect(snapshot).toBe(1)\n                })\n            })\n            describe(\"registerHook\", () => {\n                it(\"should register a hook and call it\", () => {\n                    const n = t.number.instantiate(null, \"\", {}, 1)\n                    let called = false\n                    n.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n\n                    n.die()\n\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"setParent\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    describe(\"with null\", () => {\n                        it(\"should throw an error\", () => {\n                            const n = t.number.instantiate(null, \"\", {}, 1)\n                            expect(() => {\n                                n.setParent(null, \"foo\")\n                            }).toThrow()\n                        })\n                    })\n                    describe(\"with a parent object\", () => {\n                        it(\"should throw an error\", () => {\n                            const Parent = t.model({\n                                child: t.number\n                            })\n\n                            const parent = Parent.create({ child: 1 })\n\n                            const n = t.number.instantiate(null, \"\", {}, 1)\n\n                            expect(() => {\n                                // @ts-ignore\n                                n.setParent(parent, \"bar\")\n                            }).toThrow(\n                                \"[mobx-state-tree] assertion failed: scalar nodes cannot change their parent\"\n                            )\n                        })\n                    })\n                }\n            })\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/object-node.test.ts",
    "content": "import { t } from \"../../src/index\"\nimport { Hook, ObjectNode, onPatch, unprotect } from \"../../src/internal\"\nimport { describe, expect, jest, it, spyOn } from \"bun:test\"\n\nconst TestModel = t.model(\"TestModel\", {\n    title: t.string\n})\nconst TestArray = t.array(TestModel)\nconst TestMap = t.map(TestModel)\nconst Parent = t.model(\"Parent\", {\n    child: t.maybe(TestModel)\n})\nconst TestModelWithIdentifier = t.model(\"TestModelWithIdentifier\", {\n    id: t.identifier,\n    title: t.string\n})\n\n/**\n * These tests were added to help understand the ObjectNode class and how it interacts internally in MST.\n * As such, they test internals of the library that aren't intended to be used by consumers. That means:\n *\n * 1. Please do not use these examples in application-level code with MST, these are not necessarily best practices, and are definitely not intended as a public API\n * 2. These tests may not test every single case, but they cover scenarios I was interested in understanding.\n * 3. Since the tests are tightly coupled to implementation, this test suite may end up being noisy.\n *\n * If you are making a change to MST and get failures here, please consider that as a yellow flag, not a red flag.\n * Feel free to make changes you need to, or even skip tests if they're a nuisance.\n */\ndescribe(\"ObjectNode\", () => {\n    describe(\"constructor\", () => {\n        // Since ObjectNode is not exported as part of the MST API, we don't have tests for invalid parameters, but we expect an error in this scenario.\n        it(\"throws if type is not a complex type\", () => {\n            expect(() => new ObjectNode(t.string as any, null, \"\", {}, \"foo\")).toThrow(\n                \"complexType.initializeChildNodes is not a function\"\n            )\n        })\n        it(\"works with a complex type\", () => {\n            const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n            expect(node).toBeDefined()\n        })\n    })\n    describe(\"methods\", () => {\n        describe(\"aboutToDie\", () => {\n            describe(\"if the observable node is unitialized\", () => {\n                it(\"does not call the onAboutToDie hook\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.beforeDestroy, hook)\n                    node.aboutToDie()\n                    expect(hook).not.toHaveBeenCalled()\n                })\n            })\n            describe(\"if the observable node is initialized\", () => {\n                it(\"calls the onAboutToDie hook\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.beforeDestroy, hook)\n                    node.createObservableInstance() // createObservableInstance calls finalizeCreation internally, and marks the observable node as being created.\n                    node.aboutToDie()\n                    expect(hook).toHaveBeenCalled()\n                })\n            })\n        })\n        describe(\"addDisposer\", () => {\n            it(\"adds a disposer to the node\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const disposer = jest.fn()\n                node.addDisposer(disposer)\n                node.createObservableInstance()\n                expect(node.hasDisposer(disposer)).toBe(true)\n            })\n        })\n        describe(\"addMiddleWare\", () => {\n            it(\"adds a middleware to the node\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const middleware = jest.fn((call, next) => {\n                    next(call)\n                })\n                node.addMiddleWare(middleware)\n                node.createObservableInstance()\n                node.applySnapshot({ title: \"hello\" } as any)\n                expect(middleware).toHaveBeenCalled()\n            })\n        })\n        describe(\"applyPatchLocally\", () => {\n            describe(\"when the node is protected\", () => {\n                it(\"throws an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(() =>\n                        node.applyPatchLocally(\"\", {\n                            op: \"replace\",\n                            path: \"\",\n                            value: { title: \"hello\" }\n                        })\n                    ).toThrow(\n                        \"[mobx-state-tree] Cannot modify 'TestModel@<root>', the object is protected and can only be modified by using an action.\"\n                    )\n                })\n            })\n            describe(\"when the node is not alive\", () => {\n                it(\"warns by default\", () => {\n                    const warnSpy = spyOn(console, \"warn\")\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                    node.die()\n                    node.applyPatchLocally(\"\", {\n                        op: \"replace\",\n                        path: \"\",\n                        value: { title: \"hello\" }\n                    })\n\n                    expect(warnSpy).toHaveBeenCalled()\n                })\n            })\n            describe(\"when the node is alive and not protected\", () => {\n                describe(\"for models\", () => {\n                    it(\"does not allow remove\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        expect(() => {\n                            node.applyPatchLocally(\"\", {\n                                op: \"remove\",\n                                path: \"\"\n                            })\n                        }).toThrow(\"[mobx-state-tree] object does not support operation remove\")\n                    })\n                    it(\"allows add\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"title\", {\n                            op: \"add\",\n                            path: \"\",\n                            value: \"world\"\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.title).toBe(\"world\")\n                    })\n                    it(\"allows replace\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"title\", {\n                            op: \"replace\",\n                            path: \"\",\n                            value: \"world\"\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.title).toBe(\"world\")\n                    })\n                })\n                describe(\"for arrays\", () => {\n                    it(\"works for replace\", () => {\n                        const node = new ObjectNode(TestArray as any, null, \"\", {}, [\n                            { title: \"hello\" }\n                        ])\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"0\", {\n                            op: \"replace\",\n                            path: \"\",\n                            value: { title: \"world\" }\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue[0].title).toBe(\"world\")\n                    })\n                    it(\"works for add\", () => {\n                        const node = new ObjectNode(TestArray as any, null, \"\", {}, [\n                            { title: \"hello\" }\n                        ])\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"1\", {\n                            op: \"add\",\n                            path: \"\",\n                            value: { title: \"world\" }\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.length).toBe(2)\n                        // @ts-ignore\n                        expect(node.storedValue[1].title).toBe(\"world\")\n                    })\n                    it(\"works for remove\", () => {\n                        const node = new ObjectNode(TestArray as any, null, \"\", {}, [\n                            { title: \"hello\" }\n                        ])\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"0\", {\n                            op: \"remove\",\n                            path: \"\"\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.length).toBe(0)\n                    })\n                })\n                describe(\"for maps\", () => {\n                    it(\"works for add\", () => {\n                        const node = new ObjectNode(\n                            TestMap as any,\n                            null,\n                            \"\",\n                            {},\n                            { hello: { title: \"hello\" } }\n                        )\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"world\", {\n                            op: \"add\",\n                            path: \"\",\n                            value: { title: \"world\" }\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.get(\"world\").title).toBe(\"world\")\n                    })\n                    it(\"works for replace\", () => {\n                        const node = new ObjectNode(\n                            TestMap as any,\n                            null,\n                            \"\",\n                            {},\n                            { hello: { title: \"hello\" } }\n                        )\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"hello\", {\n                            op: \"replace\",\n                            path: \"\",\n                            value: { title: \"world\" }\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.get(\"hello\").title).toBe(\"world\")\n                    })\n                    it(\"works for remove\", () => {\n                        const node = new ObjectNode(\n                            TestMap as any,\n                            null,\n                            \"\",\n                            {},\n                            { hello: { title: \"hello\" } }\n                        )\n                        unprotect(node.root.value) // In order to call applyPatchLocally, the node must be unprotected\n                        node.applyPatchLocally(\"hello\", {\n                            op: \"remove\",\n                            path: \"\"\n                        })\n\n                        // @ts-ignore\n                        expect(node.storedValue.size).toBe(0)\n                    })\n                })\n            })\n        })\n        describe(\"applyPatches\", () => {\n            describe(\"when the path is not specified\", () => {\n                it(\"applies the value as a snapshot\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    // Notice we're saying to \"remove\" the value, but using just a snapshot. This will just apply a snapshot based on how applyPatches runs.\n                    // @ts-ignore\n                    node.applyPatches([{ op: \"remove\", value: { title: \"world\" } }])\n\n                    // @ts-ignore\n                    expect(node.storedValue.title).toBe(\"world\")\n                })\n            })\n            describe(\"with correct paths\", () => {\n                it(\"applies the patch\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.applyPatches([{ op: \"replace\", path: \"/title\", value: \"world\" }])\n\n                    // @ts-ignore\n                    expect(node.storedValue.title).toBe(\"world\")\n                })\n            })\n        })\n        describe(\"applySnapshot\", () => {\n            it(\"works\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                node.applySnapshot({ title: \"world\" })\n                // @ts-ignore\n                expect(node.storedValue.title).toBe(\"world\")\n            })\n        })\n        describe(\"assertAlive\", () => {\n            describe(\"when the node is alive\", () => {\n                it(\"does not warn\", () => {\n                    const warnSpy = spyOn(console, \"warn\")\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.assertAlive({})\n                    expect(warnSpy).not.toHaveBeenCalled()\n                })\n            })\n            describe(\"when the node is not alive\", () => {\n                it(\"warns about liveliness\", () => {\n                    const warnSpy = spyOn(console, \"warn\")\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    node.assertAlive({})\n                    const receivedErrorMessage = warnSpy.mock.calls[0][0].toString()\n                    expect(receivedErrorMessage).toBe(\n                        \"Error: [mobx-state-tree] You are trying to read or write to an object that is no longer part of a state tree. (Object type: 'TestModel', Path upon death: '', Subpath: '', Action: ''). Either detach nodes first, or don't use objects after removing / replacing them in the tree.\"\n                    )\n                })\n            })\n        })\n        describe(\"assertWritable\", () => {\n            describe(\"when the node is not alive\", () => {\n                it(\"throws an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    expect(() => node.assertWritable({})).toThrow(\n                        \"[mobx-state-tree] Cannot modify 'TestModel@<root> [dead]', the object is protected and can only be modified by using an action.\"\n                    )\n                })\n            })\n            describe(\"when the node is alive and protected\", () => {\n                it(\"throws an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    // Nodes are protected by default\n                    expect(() => node.assertWritable({})).toThrow(\n                        \"[mobx-state-tree] Cannot modify 'TestModel@<root>', the object is protected and can only be modified by using an action.\"\n                    )\n                })\n            })\n            describe(\"when the node is alive and not protected\", () => {\n                it(\"does not throw an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    unprotect(node.root.value)\n                    expect(() => node.assertWritable({})).not.toThrow()\n                })\n            })\n        })\n        describe(\"clearParent\", () => {\n            it(\"removes the parent from a node\", () => {\n                const parent = Parent.create({ child: { title: \"hello\" } })\n                const child = parent.child\n                expect(child).toBeDefined()\n                // We can't directly modify the tree without unprotecting it first\n                unprotect(parent)\n                // The object node is made available through the $treenode property\n                child!.$treenode.clearParent()\n                expect(parent.child).toBeUndefined()\n            })\n        })\n        describe(\"createObservableInstance\", () => {\n            describe(\"when the node is still initializing\", () => {\n                it(\"works\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(node.storedValue).toBeUndefined()\n                    expect(node.state).toBe(0)\n                    node.createObservableInstance()\n                    expect(node.storedValue).toBeDefined()\n                    expect(node.state).toBe(2)\n                })\n            })\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"when the node has been initialized\", () => {\n                    it(\"does not work\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.createObservableInstance()\n                        expect(() => node.createObservableInstance()).toThrow(\n                            \"[mobx-state-tree] assertion failed: the creation of the observable instance must be done on the initializing phase\"\n                        )\n                    })\n                })\n            }\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"if the node is dead\", () => {\n                    it(\"does not work\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.die()\n                        expect(() => node.createObservableInstance()).toThrow(\n                            \"[mobx-state-tree] assertion failed: the creation of the observable instance must be done on the initializing phase\"\n                        )\n                    })\n                })\n            }\n        })\n        describe(\"createObservableInstanceIfNeeded\", () => {\n            describe(\"when the node is still initializing\", () => {\n                it(\"works\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(node.storedValue).toBeUndefined()\n                    expect(node.state).toBe(0)\n                    node.createObservableInstanceIfNeeded()\n                    expect(node.storedValue).toBeDefined()\n                    expect(node.state).toBe(2)\n                })\n            })\n            describe(\"when the node has been initialized\", () => {\n                it(\"does not throw, but an observable instance should be available\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.createObservableInstanceIfNeeded()\n                    expect(() => node.createObservableInstanceIfNeeded()).not.toThrow()\n                    expect(node.storedValue).toBeDefined()\n                    expect(node.state).toBe(2)\n                })\n            })\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"if the node is dead\", () => {\n                    it(\"does not work\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.die()\n                        expect(() => node.createObservableInstance()).toThrow(\n                            \"[mobx-state-tree] assertion failed: the creation of the observable instance must be done on the initializing phase\"\n                        )\n                    })\n                })\n            }\n        })\n        describe(\"detach\", () => {\n            describe(\"when the node is not alive\", () => {\n                it(\"does throws an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    expect(() => node.detach()).toThrow(\"Error while detaching, node is not alive.\")\n                })\n            })\n            describe(\"when the node is alive and does not have a parent\", () => {\n                it(\"does not throw an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(() => node.detach()).not.toThrow()\n                })\n            })\n            describe(\"when the node is alive and has a parent\", () => {\n                it(\"detaches the node from the parent\", () => {\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n                    const child = parent.child\n                    expect(child).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    child!.$treenode.detach()\n                    expect(parent.child).toBeUndefined()\n                })\n            })\n        })\n        describe(\"die\", () => {\n            describe(\"if the node is already dead\", () => {\n                it(\"does nothing\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    expect(() => node.die()).not.toThrow()\n                })\n            })\n            describe(\"if the node is detaching\", () => {\n                it(\"does nothing\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.detach()\n                    expect(() => node.die()).not.toThrow()\n                })\n            })\n            describe(\"if the node is unititalized\", () => {\n                it(\"does not call the onAboutToDie hooks\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.beforeDestroy, hook)\n                    node.die()\n                    expect(hook).not.toHaveBeenCalled()\n                })\n            })\n            describe(\"if the die method gets past lifecycle checks\", () => {\n                it('calls the \"aboutToDie\" hook', () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.beforeDestroy, hook)\n                    node.createObservableInstance()\n                    node.die()\n                    expect(hook).toHaveBeenCalled()\n                })\n                it(\"finalizes the death of its children\", () => {\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n                    const child = parent.child\n                    expect(child).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.die()\n                    expect(child!.$treenode.state).toBe(4)\n                })\n                it(\"notifies the identifier cache that it has died\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const identifierCache = node.root.identifierCache\n                    const identifierCacheNotifySpy = identifierCache\n                        ? spyOn(identifierCache, \"notifyDied\")\n                        : jest.fn()\n                    node.createObservableInstance()\n                    node.die()\n                    expect(identifierCacheNotifySpy).toHaveBeenCalledWith(node)\n                })\n                it(\"stores the snapshot upon death\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.createObservableInstance()\n                    node.die()\n                    expect(node.snapshot).toEqual({ title: \"hello\" })\n                })\n                it(\"sets the subpath upon death\", () => {\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n                    const child = parent.child\n                    expect(child).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.die()\n                    expect(child!.$treenode.subpathUponDeath).toBe(\"child\")\n                })\n                it(\"sets the path upon death\", () => {\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n                    const child = parent.child\n                    expect(child).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.die()\n                    expect(child!.$treenode.pathUponDeath).toBe(\"/child\")\n                })\n                it(\"sets its parent to null\", () => {\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n                    const child = parent.child\n                    expect(child).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.die()\n                    expect(child!.$treenode.parent).toBeNull()\n                })\n                it(\"sets the state to dead\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.createObservableInstance()\n                    node.die()\n                    expect(node.state).toBe(4)\n                })\n            })\n        })\n        describe(\"emitPatch\", () => {\n            it(\"emits the patch and a reverse patch\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                node.createObservableInstance()\n                const patchMock = jest.fn()\n                onPatch(node.storedValue, patchMock)\n                node.emitPatch(\n                    {\n                        op: \"replace\",\n                        path: \"title\",\n                        value: \"world\",\n                        oldValue: \"hello\"\n                    },\n                    node\n                )\n\n                expect(patchMock).toHaveBeenCalledWith(\n                    {\n                        op: \"replace\",\n                        path: \"/title\",\n                        value: \"world\"\n                    },\n                    {\n                        op: \"replace\",\n                        path: \"/title\",\n                        value: \"hello\"\n                    }\n                )\n            })\n            it(\"emits the patch and a reverse patch through its parent\", () => {\n                const parent = Parent.create({ child: { title: \"hello\" } })\n                const patchMock = jest.fn()\n                onPatch(parent, patchMock)\n                parent.child!.$treenode.emitPatch(\n                    {\n                        op: \"replace\",\n                        path: \"title\",\n                        value: \"world\",\n                        oldValue: \"hello\"\n                    },\n                    parent.child!.$treenode\n                )\n\n                expect(patchMock).toHaveBeenCalledWith(\n                    {\n                        op: \"replace\",\n                        path: \"/child/title\",\n                        value: \"world\"\n                    },\n                    {\n                        op: \"replace\",\n                        path: \"/child/title\",\n                        value: \"hello\"\n                    }\n                )\n            })\n        })\n        describe(\"finalizeCreation\", () => {\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"if the node is not alive\", () => {\n                    it(\"fails\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.die()\n                        expect(() => node.finalizeCreation()).toThrow(\n                            \"assertion failed: cannot finalize the creation of a node that is already dead\"\n                        )\n                    })\n                })\n            }\n            describe(\"when a node has no parent\", () => {\n                it(\"calls the afterCreationFinalization hook\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.afterCreationFinalization, hook)\n                    node.state = 1 // Force the state to CREATED so we don't bail out in the isAlive check as per the prior test\n                    node.finalizeCreation()\n                    expect(hook).toHaveBeenCalled()\n                })\n                it('sets the state to \"FINALIZED\"', () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.state = 1 // Force the state to CREATED so we don't bail out in the isAlive check as per the prior test\n                    node.finalizeCreation()\n                    // @ts-expect-error - We're testing the internal state here\n                    expect(node.state).toBe(2)\n                })\n            })\n            describe(\"when the node has a parent\", () => {\n                describe(\"but the parent is not yet finalized\", () => {\n                    it(\"does not call the afterAttach hook\", () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const child = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        const hook = jest.fn()\n                        child.registerHook(Hook.afterCreationFinalization, hook)\n                        child.setParent(parent, \"child\")\n                        child.state = 1 // Force the state to CREATED in the child so we don't bail out in the isAlive check as per the prior test\n                        child.finalizeCreation()\n                        expect(hook).not.toHaveBeenCalled()\n                    })\n                    it('does not set the state to \"FINALIZED\"', () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const child = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        child.setParent(parent, \"child\")\n                        child.state = 1 // Force the state to CREATED in the child so we don't bail out in the isAlive check as per the prior test\n                        child.finalizeCreation()\n                        expect(child.state).toBe(1)\n                    })\n                })\n                describe(\"and the parent is finalized\", () => {\n                    it(\"calls the afterAttach hook\", () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const child = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        const hook = jest.fn()\n                        child.registerHook(Hook.afterCreationFinalization, hook)\n                        child.setParent(parent, \"child\")\n                        child.state = 1 // Force the state to CREATED in the child so we don't bail out in the isAlive check as per the prior test\n                        parent.state = 2 // Force the state to FINALIZED so we don't bail out during the baseFinalizeCreation on child\n                        child.finalizeCreation()\n                        expect(hook).toHaveBeenCalled()\n                    })\n                    it('sets the state to \"FINALIZED\"', () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const child = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        child.setParent(parent, \"child\")\n                        child.state = 1 // Force the state to CREATED in the child so we don't bail out in the isAlive check as per the prior test\n                        parent.state = 2 // Force the state to FINALIZED so we don't bail out during the baseFinalizeCreation on child\n                        child.finalizeCreation()\n                        // @ts-expect-error - We're testing the internal state here\n                        expect(child.state).toBe(2)\n                    })\n                })\n            })\n            describe(\"when the node has children\", () => {\n                it(\"fires the finalizeCreation hook on the parent\", () => {\n                    const env = {}\n                    const child = new ObjectNode(TestModel as any, null, \"\", env, {\n                        title: \"hello\"\n                    })\n                    const parent = new ObjectNode(Parent as any, null, \"\", env, {\n                        child: child.storedValue\n                    })\n                    child.setParent(parent, \"child\")\n                    const hook = jest.fn()\n                    parent.registerHook(Hook.afterCreationFinalization, hook)\n                    child.state = 1\n                    expect(hook).not.toHaveBeenCalled()\n                    parent.state = 1 // Force the state to CREATED so we don't bail out in the isAlive check as per the prior test\n                    parent.finalizeCreation()\n                    expect(hook).toHaveBeenCalled()\n                })\n                it(\"fires the afterAttach hook on the child\", () => {\n                    const env = {}\n                    const child = new ObjectNode(TestModel as any, null, \"\", env, {\n                        title: \"hello\"\n                    })\n                    const parent = new ObjectNode(Parent as any, null, \"\", env, {\n                        child: child.storedValue\n                    })\n                    child.setParent(parent, \"child\")\n                    const c = parent.getChildNode(\"child\")\n                    const hook = jest.fn()\n                    c.registerHook(Hook.afterAttach, hook)\n                    c.state = 1\n                    expect(hook).not.toHaveBeenCalled()\n                    parent.state = 1 // Force the state to CREATED so we don't bail out in the isAlive check as per the prior test\n                    parent.finalizeCreation()\n                    expect(hook).toHaveBeenCalled()\n                })\n                it(\"sets both child and parent states to finalized\", () => {\n                    const env = {}\n                    const child = new ObjectNode(TestModel as any, null, \"\", env, {\n                        title: \"hello\"\n                    })\n                    const parent = new ObjectNode(Parent as any, null, \"\", env, {\n                        child: child.storedValue\n                    })\n                    child.setParent(parent, \"child\")\n                    const c = parent.getChildNode(\"child\")\n                    c.state = 1\n                    parent.state = 1 // Force the state to CREATED so we don't bail out in the isAlive check as per the prior test\n                    parent.finalizeCreation()\n                    // @ts-expect-error - We're testing the internal state here\n                    expect(c.state).toBe(2)\n                    // @ts-expect-error - We're testing the internal state here\n                    expect(parent.state).toBe(2)\n                })\n            })\n        })\n        describe(\"finalizeDeath\", () => {\n            it(\"does everything die() does without calling aboutToDie()\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const hook = jest.fn()\n                node.registerHook(Hook.beforeDestroy, hook)\n                node.createObservableInstance()\n                node.finalizeDeath()\n                expect(hook).not.toHaveBeenCalled()\n                expect(node.state).toBe(4)\n            })\n        })\n        describe(\"getChildNode\", () => {\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"when the node is not alive\", () => {\n                    it(\"fails\", () => {\n                        const warnSpy = spyOn(console, \"warn\")\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.die()\n                        node.getChildNode(\"title\")\n                        const receivedErrorMessage = warnSpy.mock.calls[0][0].toString()\n                        expect(receivedErrorMessage).toBe(\n                            \"Error: [mobx-state-tree] You are trying to read or write to an object that is no longer part of a state tree. (Object type: 'TestModel', Path upon death: '', Subpath: 'title', Action: ''). Either detach nodes first, or don't use objects after removing / replacing them in the tree.\"\n                        )\n                    })\n                })\n            }\n            describe(\"when the node is alive\", () => {\n                it(\"returns the child node\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.createObservableInstance()\n                    const childNode = node.getChildNode(\"title\")\n                    expect(childNode).toBeDefined()\n                    expect(childNode.storedValue).toBe(\"hello\")\n                })\n            })\n        })\n        describe(\"getChildType\", () => {\n            it(\"returns the child type\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const childType = node.getChildType(\"title\")\n                expect(childType).toBe(t.string)\n            })\n        })\n        describe(\"getChildren\", () => {\n            describe(\"when the node is not alive\", () => {\n                it(\"fails\", () => {\n                    const warnSpy = spyOn(console, \"warn\")\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    node.getChildren()\n                    const receivedErrorMessage = warnSpy.mock.calls[0][0].toString()\n                    expect(receivedErrorMessage).toBe(\n                        \"Error: [mobx-state-tree] You are trying to read or write to an object that is no longer part of a state tree. (Object type: 'TestModel', Path upon death: '', Subpath: '', Action: ''). Either detach nodes first, or don't use objects after removing / replacing them in the tree.\"\n                    )\n                })\n            })\n            describe(\"when the node is alive and has children\", () => {\n                it(\"returns an array of children\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.createObservableInstance()\n                    const children = node.getChildren()\n                    expect(children).toBeDefined()\n                    expect(children.length).toBe(1)\n                    expect(children[0].storedValue).toBe(\"hello\")\n                })\n            })\n        })\n        describe(\"getReconciliationType\", () => {\n            it(\"returns the complext type used to instantiate the node\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.getReconciliationType()).toBe(TestModel)\n            })\n        })\n        describe(\"getSnapshot\", () => {\n            it(\"returns the snapshot\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.getSnapshot()).toEqual({ title: \"hello\" })\n            })\n        })\n        describe(\"hasDisposer\", () => {\n            describe(\"when there are no disposers\", () => {\n                it(\"returns false\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(node.hasDisposer(() => {})).toBe(false)\n                })\n            })\n            describe(\"when there are disposers\", () => {\n                it(\"returns true\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const disposer = () => {}\n                    node.addDisposer(disposer)\n                    expect(node.hasDisposer(disposer)).toBe(true)\n                })\n            })\n        })\n        describe(\"onPatch\", () => {\n            it(\"registers the patch listener\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const listener = jest.fn()\n                node.onPatch(listener)\n                node.createObservableInstance()\n                node.applyPatches([{ op: \"replace\", path: \"/title\", value: \"world\" }])\n                expect(listener).toHaveBeenCalledWith(\n                    {\n                        op: \"replace\",\n                        path: \"/title\",\n                        value: \"world\"\n                    },\n                    {\n                        op: \"replace\",\n                        path: \"/title\",\n                        value: \"hello\"\n                    }\n                )\n            })\n        })\n        describe(\"onSnapshot\", () => {\n            it(\"registers the snapshot listener\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const listener = jest.fn()\n                node.onSnapshot(listener)\n                node.createObservableInstance()\n                node.applySnapshot({ title: \"world\" })\n                expect(listener).toHaveBeenCalledWith({ title: \"world\" })\n            })\n        })\n        describe(\"registerHook\", () => {\n            describe(\"afterCreate\", () => {\n                it(\"works\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.afterCreate, hook)\n                    // We call afterCreate during observable instance creation\n                    node.createObservableInstance()\n                    expect(hook).toHaveBeenCalled()\n                })\n            })\n            describe(\"afterAttach\", () => {\n                describe(\"for a root node\", () => {\n                    it(\"does not get called\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        const hook = jest.fn()\n                        node.registerHook(Hook.afterAttach, hook)\n                        // We call afterAttach during observable instance creation\n                        node.createObservableInstance()\n                        expect(hook).not.toHaveBeenCalled()\n                    })\n                })\n                describe(\"for a non-root node\", () => {\n                    it(\"gets called\", () => {\n                        const env = {}\n                        const child = new ObjectNode(TestModel as any, null, \"\", env, {\n                            title: \"hello\"\n                        })\n                        const parent = new ObjectNode(Parent as any, null, \"\", env, {\n                            child: child.storedValue\n                        })\n                        child.setParent(parent, \"child\")\n                        const c = parent.getChildNode(\"child\")\n                        const hook = jest.fn()\n                        c.registerHook(Hook.afterAttach, hook)\n                        parent.createObservableInstance()\n                        expect(hook).toHaveBeenCalled()\n                    })\n                })\n            })\n            describe(\"afterCreationFinalization\", () => {\n                it(\"works\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.afterCreationFinalization, hook)\n                    // We call afterCreationFinalization during observable instance creation\n                    node.createObservableInstance()\n                    expect(hook).toHaveBeenCalled()\n                })\n            })\n            describe(\"beforeDetach\", () => {\n                describe(\"for a root node\", () => {\n                    it(\"does not get called\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        const hook = jest.fn()\n                        node.registerHook(Hook.beforeDetach, hook)\n                        node.createObservableInstance()\n                        node.detach()\n                        expect(hook).not.toHaveBeenCalled()\n                    })\n                })\n                describe(\"for a non-root node\", () => {\n                    it(\"gets called\", () => {\n                        const env = {}\n                        const child = new ObjectNode(TestModel as any, null, \"\", env, {\n                            title: \"hello\"\n                        })\n                        const parent = new ObjectNode(Parent as any, null, \"\", env, {\n                            child: child.storedValue\n                        })\n                        child.setParent(parent, \"child\")\n                        const hook = jest.fn()\n                        child.registerHook(Hook.beforeDetach, hook)\n                        parent.createObservableInstance()\n                        unprotect(parent.storedValue) // In order to detach a child node directly, we need to unprotect the root here\n                        child.detach()\n                        expect(hook).toHaveBeenCalled()\n                    })\n                })\n            })\n            describe(\"beforeDestroy\", () => {\n                it(\"works\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.beforeDestroy, hook)\n                    // We need to create an observable instance before we can destroy it and get the hook to fire\n                    node.createObservableInstance()\n                    node.die()\n                    expect(hook).toHaveBeenCalled()\n                })\n            })\n        })\n        describe(\"removeChild\", () => {\n            describe(\"for models\", () => {\n                it(\"removes the child by path\", () => {\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n                    const child = parent.child\n                    expect(child).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.removeChild(\"child\")\n                    expect(parent.child).toBeUndefined()\n                })\n            })\n            describe(\"for arrays\", () => {\n                it(\"removes the child by index\", () => {\n                    const parent = TestArray.create([{ title: \"hello\" }])\n                    expect(parent[0]).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.removeChild(0)\n                    expect(parent[0]).toBeUndefined()\n                })\n            })\n            describe(\"for maps\", () => {\n                it(\"removes the child by key\", () => {\n                    const parent = TestMap.create({ hello: { title: \"hello\" } })\n                    expect(parent.get(\"hello\")).toBeDefined()\n                    // We can't directly modify the tree without unprotecting it first\n                    unprotect(parent)\n                    // The object node is made available through the $treenode property\n                    parent.$treenode.removeChild(\"hello\")\n                    expect(parent.get(\"hello\")).toBeUndefined()\n                })\n            })\n        })\n        describe(\"removeDisposer\", () => {\n            describe(\"when a disposer does not exist\", () => {\n                it(\"throws an error\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(() => node.removeDisposer(() => {})).toThrow(\n                        \"[mobx-state-tree] cannot remove a disposer which was never registered for execution\"\n                    )\n                })\n            })\n            describe(\"when a disposer exists\", () => {\n                it(\"removes the disposer\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const disposer = () => {}\n                    node.addDisposer(disposer)\n                    node.removeDisposer(disposer)\n                    expect(node.hasDisposer(disposer)).toBe(false)\n                })\n            })\n        })\n        describe(\"setParent\", () => {\n            describe(\"if the parent and subpath are unchanged\", () => {\n                it(\"does nothing\", () => {\n                    const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                    const node = new ObjectNode(\n                        TestModel as any,\n                        parent,\n                        \"child\",\n                        {},\n                        { title: \"hello\" }\n                    )\n                    node.setParent(parent, \"child\")\n                    expect(node.parent).toBe(parent)\n                    expect(node.subpath).toBe(\"child\")\n                })\n            })\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"if there is no subpath\", () => {\n                    it(\"throws an error\", () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"child\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        expect(() => node.setParent(parent, \"\")).toThrow(\n                            \"[mobx-state-tree] assertion failed: subpath expected\"\n                        )\n                    })\n                })\n                describe(\"if there is no new parent\", () => {\n                    it(\"throws an error\", () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"child\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        expect(() => node.setParent(null as any, \"child\")).toThrow(\n                            \"[mobx-state-tree] assertion failed: new parent expected\"\n                        )\n                    })\n                })\n                describe(\"if the node already has a parent\", () => {\n                    it(\"throws an error\", () => {\n                        const parent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            parent,\n                            \"child\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        const newParent = new ObjectNode(Parent as any, null, \"\", {}, {})\n                        expect(() => node.setParent(newParent, \"child\")).toThrow(\n                            \"[mobx-state-tree] A node cannot exists twice in the state tree. Failed to add TestModel@/child to path '/child'.\"\n                        )\n                    })\n                })\n                describe(\"if the parent is made to be itself\", () => {\n                    it(\"throws an error\", () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        expect(() => node.setParent(node, \"child\")).toThrow(\n                            \"[mobx-state-tree] A state tree is not allowed to contain itself. Cannot assign TestModel@<root> to path '/child'\"\n                        )\n                    })\n                })\n                describe(\"if the parent exists in another state tree in a different environment\", () => {\n                    it(\"throws an error\", () => {\n                        const env1 = {}\n                        const env2 = {}\n\n                        const node = new ObjectNode(TestModel as any, null, \"\", env1, {\n                            title: \"hello\"\n                        })\n                        const parent = new ObjectNode(Parent as any, null, \"\", env2, {})\n\n                        expect(() => node.setParent(parent, \"child\")).toThrow(\n                            \"[mobx-state-tree] A state tree cannot be made part of another state tree as long as their environments are different.\"\n                        )\n                    })\n                })\n            }\n            describe(\"if the parent is different\", () => {\n                it(\"gives the node a new parent\", () => {\n                    const env = {}\n                    const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                    const node = new ObjectNode(TestModel as any, null, \"\", env, { title: \"hello\" })\n                    node.setParent(parent, \"child\")\n                    expect(node.parent).toBe(parent)\n                })\n                it(\"fires the afterAttach hook\", () => {\n                    const env = {}\n                    const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                    const node = new ObjectNode(TestModel as any, null, \"\", env, { title: \"hello\" })\n                    const hook = jest.fn()\n                    node.registerHook(Hook.afterAttach, hook)\n                    node.setParent(parent, \"child\")\n                    expect(hook).toHaveBeenCalled()\n                })\n            })\n            describe(\"if the parent is the same but the subpath has changed\", () => {\n                it(\"gives the node a new subpath\", () => {\n                    const env = {}\n                    const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                    const node = new ObjectNode(TestModel as any, parent, \"\", env, {\n                        title: \"hello\"\n                    })\n                    node.setParent(parent, \"child\")\n                    expect(node.subpath).toBe(\"child\")\n                })\n            })\n        })\n        describe(\"toString\", () => {\n            it(\"returns a string representation of the node\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.toString()).toBe(\"TestModel@<root>\")\n            })\n        })\n        describe(\"unbox\", () => {\n            // This was probably intended to be used with `null` or `undefined`, but the implementation just checks for falsy values.\n            describe(\"when given some falsy value\", () => {\n                it(\"returns the value\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(node.unbox(undefined)).toBeUndefined()\n                    // @ts-expect-error - we're testing the behavior of unbox with non-undefined values\n                    expect(node.unbox(null as any)).toBe(null)\n                    // @ts-expect-error - we're testing the behavior of unbox with non-undefined values\n                    expect(node.unbox(false as any)).toBe(false)\n                    // @ts-expect-error - we're testing the behavior of unbox with non-undefined values\n                    expect(node.unbox(0 as any)).toBe(0)\n                })\n            })\n            describe(\"when given a child node\", () => {\n                it(\"gives back the value\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    const childNode = node.getChildNode(\"title\")\n                    // @ts-expect-error - we're testing the behavior of unbox with non-undefined values\n                    expect(node.unbox(childNode)).toBe(\"hello\")\n                })\n            })\n        })\n    })\n    describe(\"properties\", () => {\n        describe(\"_isRunningAciton\", () => {\n            // The only time we ever set this to _true is during the operation sin createObservableInstance, so we don't have a great way to test it. For now, just test default value.\n            it(\"is false by default\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node._isRunningAction).toBe(false)\n            })\n        })\n        describe(\"environmment\", () => {\n            it(\"returns the environment\", () => {\n                const env = {}\n                const node = new ObjectNode(TestModel as any, null, \"\", env, { title: \"hello\" })\n                expect(node.environment).toBe(env)\n            })\n            it(\"matches by reference\", () => {\n                const env = {}\n                // It's using the reference to the object, not just the value of an \"empty\" object\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.environment).not.toBe(env)\n            })\n        })\n        describe(\"hasSnapshotPostProcessor\", () => {\n            it(\"returns false by default\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.hasSnapshotPostProcessor).toBe(false)\n            })\n            it(\"returns false if there is a pre processor and no post processor\", () => {\n                const NewType = t.model(\"NewType\", {\n                    title: t.string\n                })\n\n                const NewType2 = t.snapshotProcessor(NewType, {\n                    preProcessor(sn: any) {\n                        return { title: sn.title.toUpperCase() }\n                    }\n                })\n\n                const instance = NewType2.create({ title: \"hello\" })\n                const node = instance.$treenode\n\n                expect(node.hasSnapshotPostProcessor).toBe(false)\n            })\n            it(\"returns true if there is a post processor\", () => {\n                const NewType = t.model(\"NewType\", {\n                    title: t.string\n                })\n\n                const NewType2 = t.snapshotProcessor(NewType, {\n                    postProcessor(sn: any, node: any) {\n                        return { title: sn.title.toUpperCase() }\n                    }\n                })\n\n                const instance = NewType2.create({ title: \"hello\" })\n                const node = instance.$treenode\n\n                expect(node.hasSnapshotPostProcessor).toBe(true)\n            })\n            it(\"returns true if there is a pre processor and a post processor\", () => {\n                const NewType = t.model(\"NewType\", {\n                    title: t.string\n                })\n\n                const NewType2 = t.snapshotProcessor(NewType, {\n                    preProcessor(sn: any) {\n                        return { title: sn.title.toUpperCase() }\n                    },\n                    postProcessor(sn: any, node: any) {\n                        return { title: sn.title.toUpperCase() }\n                    }\n                })\n\n                const instance = NewType2.create({ title: \"hello\" })\n                const node = instance.$treenode\n\n                expect(node.hasSnapshotPostProcessor).toBe(true)\n            })\n        })\n        describe(\"identifier\", () => {\n            describe(\"when the type has no identifier\", () => {\n                it(\"returns null\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    expect(node.identifier).toBe(null)\n                })\n            })\n            describe(\"when the type has an identifier\", () => {\n                it(\"returns the identifier\", () => {\n                    const node = new ObjectNode(\n                        TestModelWithIdentifier as any,\n                        null,\n                        \"\",\n                        {},\n                        { id: \"1234\", title: \"hello\" }\n                    )\n                    expect(node.identifier).toBe(\"1234\")\n                })\n            })\n        })\n        describe(\"when the type does not have an identifier\", () => {\n            it(\"returns undefined\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.identifierAttribute).toBeUndefined()\n            })\n        })\n        describe(\"when the type has an identifier\", () => {\n            it(\"returns the identifier\", () => {\n                const node = new ObjectNode(\n                    TestModelWithIdentifier as any,\n                    null,\n                    \"\",\n                    {},\n                    { id: \"1234\", title: \"hello\" }\n                )\n                expect(node.identifierAttribute).toBe(\"id\")\n            })\n        })\n        describe(\"identifierCache\", () => {\n            describe(\"if the node is the root\", () => {\n                it(\"exists\", () => {\n                    const Parent = t.model(\"Parent\", {\n                        child: TestModel\n                    })\n\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n\n                    expect(parent.$treenode.identifierCache).toBeDefined()\n                })\n                it(\"keeps track of ids\", () => {\n                    const Parent = t.model(\"Parent\", {\n                        child: TestModelWithIdentifier\n                    })\n\n                    const parent = Parent.create({ child: { id: \"1234\", title: \"hello\" } })\n                    const identifierCache = parent.$treenode.identifierCache\n                    expect(identifierCache.has(TestModelWithIdentifier, \"1234\")).toBe(true)\n                    expect(identifierCache.has(TestModelWithIdentifier, \"aaa\")).toBe(false)\n                })\n            })\n            describe(\"if the node is not the root\", () => {\n                it(\"is undefined\", () => {\n                    const Parent = t.model(\"Parent\", {\n                        child: TestModel\n                    })\n\n                    const parent = Parent.create({ child: { title: \"hello\" } })\n\n                    expect(parent.child.$treenode.identifierCache).toBeUndefined()\n                })\n            })\n        })\n        describe(\"isAlive\", () => {\n            describe(\"when the node is dead\", () => {\n                it(\"returns false\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    expect(node.isAlive).toBe(false)\n                })\n            })\n            describe(\"when the node is initializing, created, finalized, or detaching\", () => {\n                const testCases = [0, 1, 2, 3]\n                testCases.forEach(state => {\n                    it(`returns true when the state is ${state}`, () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.state = state\n                        expect(node.isAlive).toBe(true)\n                    })\n                })\n            })\n        })\n        describe(\"isDetaching\", () => {\n            describe(\"when the node is detaching\", () => {\n                it(\"returns true\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.state = 3\n                    expect(node.isDetaching).toBe(true)\n                })\n            })\n            describe(\"when the node is in any other state\", () => {\n                const testCases = [0, 1, 2, 4]\n                testCases.forEach(state => {\n                    it(`returns false when the state is ${state}`, () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.state = state\n                        expect(node.isDetaching).toBe(false)\n                    })\n                })\n            })\n        })\n        describe(\"isProtected\", () => {\n            it(\"returns true by default\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.isProtected).toBe(true)\n            })\n            it(\"returns false if the node is unprotected\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                node.createObservableInstance()\n                unprotect(node.storedValue)\n                expect(node.isProtected).toBe(false)\n            })\n        })\n        describe(\"isRoot\", () => {\n            it(\"returns true by default\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.isRoot).toBe(true)\n            })\n            it(\"returns false when it is made a child of another node\", () => {\n                const env = {}\n                const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                const node = new ObjectNode(TestModel as any, null, \"\", env, { title: \"hello\" })\n                node.setParent(parent, \"child\")\n                expect(node.isRoot).toBe(false)\n            })\n        })\n        describe(\"middlewares\", () => {\n            it(\"returns no middlewares by default\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.middlewares).toBeUndefined()\n            })\n            it(\"returns the middlewares\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const middleware = jest.fn()\n                node.addMiddleWare(middleware)\n                expect(node.middlewares).toBeDefined()\n            })\n        })\n        describe(\"nodeId\", () => {\n            it(\"increments every time a node is created\", () => {\n                const node1 = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                const node2 = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node1.nodeId).not.toBe(node2.nodeId)\n            })\n        })\n        /**\n         * observableIsAlive will follow the isAlive patterns,\n         * it just has a side effect of reporting observation when called\n         */\n        describe(\"observableIsAlive\", () => {\n            describe(\"when the node is dead\", () => {\n                it(\"returns false\", () => {\n                    const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                    node.die()\n                    expect(node.observableIsAlive).toBe(false)\n                })\n            })\n            describe(\"when the node is initializing, created, finalized, or detaching\", () => {\n                const testCases = [0, 1, 2, 3]\n                testCases.forEach(state => {\n                    it(`returns true when the state is ${state}`, () => {\n                        const node = new ObjectNode(\n                            TestModel as any,\n                            null,\n                            \"\",\n                            {},\n                            { title: \"hello\" }\n                        )\n                        node.state = state\n                        expect(node.observableIsAlive).toBe(true)\n                    })\n                })\n            })\n        })\n        describe(\"parent\", () => {\n            it(\"returns the parent node\", () => {\n                const env = {}\n                const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                const node = new ObjectNode(TestModel as any, parent, \"\", env, { title: \"hello\" })\n                expect(node.parent).toBe(parent)\n            })\n            it(\"returns null when there is no parent\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.parent).toBe(null)\n            })\n        })\n        describe(\"path\", () => {\n            it(\"returns the provided path\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.path).toBe(\"\")\n            })\n        })\n        describe(\"root\", () => {\n            it(\"returns the node when it is the root\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.root).toBe(node)\n            })\n            it(\"returns the root node when it is not the root\", () => {\n                const env = {}\n                const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                const node = new ObjectNode(TestModel as any, parent, \"\", env, { title: \"hello\" })\n                expect(node.root).toBe(parent)\n            })\n        })\n        describe(\"snapshot\", () => {\n            it(\"returns the snapshot\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.snapshot).toEqual({ title: \"hello\" })\n            })\n        })\n        describe(\"state\", () => {\n            // We implicitly test this in many ways through the rest of the spec, so I've just left a simple one here.\n            it(\"works\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.state).toBe(0)\n            })\n        })\n        describe(\"storedValue\", () => {\n            it(\"is undefined before the observable instance is created\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.storedValue).toBeUndefined()\n            })\n            it('is the \"value\" of the observable instance after it is created', () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                node.createObservableInstance()\n                expect(node.storedValue).toBeDefined()\n                // @ts-ignore\n                expect(node.storedValue.title).toBe(\"hello\")\n            })\n        })\n        describe(\"subpath\", () => {\n            it('returns \"\" when the node is the root', () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.subpath).toBe(\"\")\n            })\n            it(\"returns the subpath when the node is not the root\", () => {\n                const env = {}\n                const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                const node = new ObjectNode(TestModel as any, parent, \"child\", env, {\n                    title: \"hello\"\n                })\n                expect(node.subpath).toBe(\"child\")\n            })\n        })\n        describe(\"subpathUponDeath\", () => {\n            it(\"remembers the subpath upon death\", () => {\n                const env = {}\n                const parent = new ObjectNode(Parent as any, null, \"\", env, {})\n                const node = new ObjectNode(TestModel as any, parent, \"child\", env, {\n                    title: \"hello\"\n                })\n                node.die()\n                expect(node.subpathUponDeath).toBe(\"child\")\n                parent.die()\n                expect(parent.subpathUponDeath).toBe(\"\")\n            })\n        })\n        describe(\"type\", () => {\n            it(\"returns the given type of the objectnode\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                expect(node.type).toBe(TestModel as any)\n            })\n        })\n        describe(\"value\", () => {\n            it(\"returns the value as returned by the given type\", () => {\n                const node = new ObjectNode(TestModel as any, null, \"\", {}, { title: \"hello\" })\n                // @ts-ignore\n                expect(node.value.title).toBe(\"hello\")\n            })\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/object.test.ts",
    "content": "import {\n    destroy,\n    detach,\n    onSnapshot,\n    onPatch,\n    onAction,\n    applyPatch,\n    applyAction,\n    applySnapshot,\n    getSnapshot,\n    unprotect,\n    types,\n    setLivelinessChecking,\n    getParent,\n    SnapshotOut,\n    IJsonPatch,\n    ISerializedActionCall,\n    isAlive,\n    cast,\n    resolveIdentifier\n} from \"../../src\"\n\nimport { autorun, reaction, observable, configure, getDebugName } from \"mobx\"\nimport { MstError } from \"../../src/internal\"\nimport { expect, test } from \"bun:test\"\n\nconst createTestFactories = () => {\n    const Factory = types\n        .model({\n            to: \"world\"\n        })\n        .actions(self => {\n            function setTo(to: string) {\n                self.to = to\n            }\n            return {\n                setTo\n            }\n        })\n    const ComputedFactory = types\n        .model({\n            width: 100,\n            height: 200\n        })\n        .views(self => ({\n            get area() {\n                return self.width * self.height\n            }\n        }))\n    const ComputedFactory2 = types\n        .model({\n            props: types.map(types.number)\n        })\n        .views(self => ({\n            get area() {\n                return self.props.get(\"width\")! * self.props.get(\"height\")!\n            }\n        }))\n        .actions(self => {\n            function setWidth(value: number) {\n                self.props.set(\"width\", value)\n            }\n            function setHeight(value: number) {\n                self.props.set(\"height\", value)\n            }\n            return {\n                setWidth,\n                setHeight\n            }\n        })\n    const BoxFactory = types.model({\n        width: 0,\n        height: 0\n    })\n    const ColorFactory = types.model({\n        color: \"#FFFFFF\"\n    })\n    return { Factory, ComputedFactory, ComputedFactory2, BoxFactory, ColorFactory }\n}\n\nconst createFactoryWithChildren = () => {\n    const File = types\n        .model(\"File\", {\n            name: types.string\n        })\n        .actions(self => ({\n            rename(value: string) {\n                self.name = value\n            }\n        }))\n\n    const Folder = types\n        .model(\"Folder\", {\n            name: types.string,\n            files: types.array(File)\n        })\n        .actions(self => ({\n            rename(value: string) {\n                self.name = value\n            }\n        }))\n    return Folder\n}\n// === FACTORY TESTS ===\ntest(\"it should create a factory\", () => {\n    const { Factory } = createTestFactories()\n    const instance = Factory.create()\n    const snapshot = getSnapshot(instance)\n    expect(snapshot).toEqual({ to: \"world\" })\n    expect(getSnapshot(Factory.create())).toEqual({ to: \"world\" }) // toJSON is there as shortcut for getSnapshot(), primarily for debugging convenience\n    expect(Factory.create().toString()).toEqual(\"AnonymousModel@<root>\")\n})\ntest(\"it should restore the state from the snapshot\", () => {\n    const { Factory } = createTestFactories()\n    expect(getSnapshot(Factory.create({ to: \"universe\" }))).toEqual({ to: \"universe\" })\n})\n// === SNAPSHOT TESTS ===\ntest(\"it should emit snapshots\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let snapshots: SnapshotOut<typeof doc>[] = []\n    onSnapshot(doc, snapshot => snapshots.push(snapshot))\n    doc.to = \"universe\"\n    expect(snapshots).toEqual([{ to: \"universe\" }])\n})\n\ntest(\"it should emit snapshots for children\", () => {\n    const Factory = createFactoryWithChildren()\n    const folder = Factory.create({\n        name: \"Photos to sort\",\n        files: [\n            {\n                name: \"Photo1\"\n            },\n            {\n                name: \"Photo2\"\n            }\n        ]\n    })\n    let snapshotsP: SnapshotOut<typeof folder>[] = []\n    let snapshotsC: SnapshotOut<(typeof folder.files)[0]>[] = []\n    onSnapshot(folder, snapshot => snapshotsP.push(snapshot))\n    folder.rename(\"Vacation photos\")\n    expect(snapshotsP[0]).toEqual({\n        name: \"Vacation photos\",\n        files: [{ name: \"Photo1\" }, { name: \"Photo2\" }]\n    })\n\n    onSnapshot(folder.files[0], snapshot => snapshotsC.push(snapshot))\n    folder.files[0].rename(\"01-arrival\")\n    expect(snapshotsP[1]).toEqual({\n        name: \"Vacation photos\",\n        files: [{ name: \"01-arrival\" }, { name: \"Photo2\" }]\n    })\n    expect(snapshotsC[0]).toEqual({ name: \"01-arrival\" })\n\n    folder.files[1].rename(\"02-hotel\")\n    expect(snapshotsP[2]).toEqual({\n        name: \"Vacation photos\",\n        files: [{ name: \"01-arrival\" }, { name: \"02-hotel\" }]\n    })\n    expect(snapshotsP.length).toBe(3)\n    expect(snapshotsC.length).toBe(1)\n})\n\ntest(\"it should apply snapshots\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    applySnapshot(doc, { to: \"universe\" })\n    expect(getSnapshot(doc)).toEqual({ to: \"universe\" })\n})\ntest(\"it should apply and accept null value for types.maybe(complexType)\", () => {\n    const Item = types.model(\"Item\", {\n        value: types.string\n    })\n    const Model = types.model(\"Model\", {\n        item: types.maybe(Item)\n    })\n    const myModel = Model.create()\n    applySnapshot(myModel, { item: { value: \"something\" } })\n    applySnapshot(myModel, { item: undefined })\n    expect(getSnapshot(myModel)).toEqual({ item: undefined })\n})\ntest(\"it should apply and accept null value for types.maybeNull(complexType)\", () => {\n    const Item = types.model(\"Item\", {\n        value: types.string\n    })\n    const Model = types.model(\"Model\", {\n        item: types.maybeNull(Item)\n    })\n    const myModel = Model.create()\n    applySnapshot(myModel, { item: { value: \"something\" } })\n    applySnapshot(myModel, { item: null })\n    expect(getSnapshot(myModel)).toEqual({ item: null })\n})\ntest(\"it should return a snapshot\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    expect(getSnapshot(doc)).toEqual({ to: \"world\" })\n})\n// === PATCHES TESTS ===\ntest(\"it should emit patches\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    doc.to = \"universe\"\n    expect(patches).toEqual([{ op: \"replace\", path: \"/to\", value: \"universe\" }])\n})\ntest(\"it should apply a patch\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, { op: \"replace\", path: \"/to\", value: \"universe\" })\n    expect(getSnapshot(doc)).toEqual({ to: \"universe\" })\n})\ntest(\"it should apply patches\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    applyPatch(doc, [\n        { op: \"replace\", path: \"/to\", value: \"mars\" },\n        { op: \"replace\", path: \"/to\", value: \"universe\" }\n    ])\n    expect(getSnapshot(doc)).toEqual({ to: \"universe\" })\n})\ntest(\"it should stop listening to patches patches\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    unprotect(doc)\n    let patches: IJsonPatch[] = []\n    let disposer = onPatch(doc, patch => patches.push(patch))\n    doc.to = \"universe\"\n    disposer()\n    doc.to = \"mweststrate\"\n    expect(patches).toEqual([{ op: \"replace\", path: \"/to\", value: \"universe\" }])\n})\n// === ACTIONS TESTS ===\ntest(\"it should call actions correctly\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    doc.setTo(\"universe\")\n    expect(getSnapshot(doc)).toEqual({ to: \"universe\" })\n})\ntest(\"it should emit action calls\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    let actions: ISerializedActionCall[] = []\n    onAction(doc, action => actions.push(action))\n    doc.setTo(\"universe\")\n    expect(actions).toEqual([{ name: \"setTo\", path: \"\", args: [\"universe\"] }])\n})\ntest(\"it should apply action call\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    applyAction(doc, { name: \"setTo\", path: \"\", args: [\"universe\"] })\n    expect(getSnapshot(doc)).toEqual({ to: \"universe\" })\n})\ntest(\"it should apply actions calls\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    applyAction(doc, [\n        { name: \"setTo\", path: \"\", args: [\"mars\"] },\n        { name: \"setTo\", path: \"\", args: [\"universe\"] }\n    ])\n    expect(getSnapshot(doc)).toEqual({ to: \"universe\" })\n})\n// === COMPUTED VALUES ===\ntest(\"it should have computed properties\", () => {\n    const { ComputedFactory } = createTestFactories()\n    const doc = ComputedFactory.create()\n    unprotect(doc)\n    doc.width = 3\n    doc.height = 2\n    expect(doc.area).toEqual(6)\n})\n\ntest(\"it should throw if a replaced object is read or written to\", () => {\n    const Todo = types\n        .model(\"Todo\", {\n            title: \"test\",\n            arr: types.array(types.string),\n            map: types.map(types.string),\n            sub: types.optional(\n                types\n                    .model(\"Sub\", {\n                        title: \"test2\"\n                    })\n                    .actions(self => ({\n                        fn2() {}\n                    })),\n                {}\n            )\n        })\n        .actions(self => ({\n            fn() {\n                self.sub.fn2()\n            }\n        }))\n    const Store = types.model(\"Store\", {\n        todo: Todo\n    })\n    const data = {\n        title: \"alive\",\n        arr: [\"arr0\"],\n        map: { mapkey0: \"mapval0\" },\n        sub: { title: \"title\" }\n    }\n    const s = Store.create({ todo: { ...data, title: \"dead\" } })\n    unprotect(s)\n\n    const deadArr = s.todo.arr\n    s.todo.arr = cast(data.arr)\n\n    const deadMap = s.todo.map\n    s.todo.map = cast(data.map)\n\n    const deadSub = s.todo.sub\n    s.todo.sub = cast(data.sub)\n\n    const deadTodo = s.todo\n    s.todo = Todo.create(data)\n\n    expect(s.todo.title).toBe(\"alive\")\n\n    setLivelinessChecking(\"error\")\n\n    function getError(obj: any, path: string, subpath: string, action: string) {\n        return `You are trying to read or write to an object that is no longer part of a state tree. (Object type: '${getDebugName(\n            obj\n        )}', Path upon death: '${path}', Subpath: '${subpath}', Action: '${action}'). Either detach nodes first, or don't use objects after removing / replacing them in the tree.`\n    }\n\n    // dead todo\n    expect(() => {\n        deadTodo.fn()\n    }).toThrow(getError(deadTodo, \"/todo\", \"\", \"/todo.fn()\"))\n    expect(() => {\n        // tslint:disable-next-line:no-unused-expression\n        deadTodo.title\n    }).toThrow(getError(deadTodo, \"/todo\", \"title\", \"\"))\n    expect(() => {\n        deadTodo.title = \"5\"\n    }).toThrow(getError(deadTodo, \"/todo\", \"title\", \"\"))\n\n    expect(() => {\n        // tslint:disable-next-line:no-unused-expression\n        deadTodo.arr[0]\n    }).toThrow(getError(deadTodo, \"/todo\", \"arr\", \"\"))\n    expect(() => {\n        deadTodo.arr.push(\"arr1\")\n    }).toThrow(getError(deadTodo, \"/todo\", \"arr\", \"\"))\n\n    expect(() => {\n        deadTodo.map.get(\"mapkey0\")\n    }).toThrow(getError(deadTodo, \"/todo\", \"map\", \"\"))\n    expect(() => {\n        deadTodo.map.set(\"mapkey1\", \"val\")\n    }).toThrow(getError(deadTodo, \"/todo\", \"map\", \"\"))\n\n    expect(() => {\n        deadTodo.sub.fn2()\n    }).toThrow(getError(deadTodo, \"/todo\", \"sub\", \"\"))\n    expect(() => {\n        // tslint:disable-next-line:no-unused-expression\n        deadTodo.sub.title\n    }).toThrow(getError(deadTodo, \"/todo\", \"sub\", \"\"))\n    expect(() => {\n        deadTodo.sub.title = \"hi\"\n    }).toThrow(getError(deadTodo, \"/todo\", \"sub\", \"\"))\n\n    // dead array\n    expect(() => {\n        // tslint:disable-next-line:no-unused-expression\n        deadArr[0]\n    }).toThrow(getError(deadArr, \"/todo/arr\", \"0\", \"\"))\n    expect(() => {\n        deadArr[0] = \"hi\"\n    }).toThrow(getError(deadArr, \"/todo/arr\", \"0\", \"\"))\n    expect(() => {\n        deadArr.push(\"hi\")\n    }).toThrow(getError(deadArr, \"/todo/arr\", \"1\", \"\"))\n\n    // dead map\n    expect(() => {\n        deadMap.get(\"mapkey0\")\n    }).toThrow(getError(deadMap, \"/todo/map\", \"mapkey0\", \"\"))\n    expect(() => {\n        deadMap.set(\"mapkey0\", \"val\")\n    }).toThrow(getError(deadMap, \"/todo/map\", \"mapkey0\", \"\"))\n\n    // dead subobj\n    expect(() => {\n        deadSub.fn2()\n    }).toThrow(getError(deadSub, \"/todo/sub\", \"\", \"/todo/sub.fn2()\"))\n    expect(() => {\n        // tslint:disable-next-line:no-unused-expression\n        deadSub.title\n    }).toThrow(getError(deadSub, \"/todo/sub\", \"title\", \"\"))\n    expect(() => {\n        deadSub.title = \"ho\"\n    }).toThrow(getError(deadSub, \"/todo/sub\", \"title\", \"\"))\n})\n\ntest(\"it should warn if a replaced object is read or written to\", () => {\n    const Todo = types\n        .model(\"Todo\", {\n            title: \"test\"\n        })\n        .actions(self => {\n            function fn() {}\n            return {\n                fn\n            }\n        })\n    const Store = types.model(\"Store\", {\n        todo: Todo\n    })\n    const s = Store.create({\n        todo: { title: \"3\" }\n    })\n    unprotect(s)\n    const todo = s.todo\n    s.todo = Todo.create({ title: \"4\" })\n    expect(s.todo.title).toBe(\"4\")\n\n    // try reading old todo\n    setLivelinessChecking(\"error\")\n    const error =\n        \"You are trying to read or write to an object that is no longer part of a state tree\"\n    expect(() => todo.fn()).toThrow(error)\n    expect(() => todo.title).toThrow(error)\n    unprotect(todo)\n    expect(() => {\n        todo.title = \"5\"\n    }).toThrow(error)\n})\n\n// === COMPOSE FACTORY ===\ntest(\"it should compose factories\", () => {\n    const { BoxFactory, ColorFactory } = createTestFactories()\n    const ComposedFactory = types.compose(BoxFactory, ColorFactory)\n    expect(getSnapshot(ComposedFactory.create())).toEqual({ width: 0, height: 0, color: \"#FFFFFF\" })\n})\ntest(\"it should compose factories with computed properties\", () => {\n    const { ComputedFactory2, ColorFactory } = createTestFactories()\n    const ComposedFactory = types.compose(ColorFactory, ComputedFactory2)\n    const store = ComposedFactory.create({ props: { width: 100, height: 200 } })\n    expect(getSnapshot(store)).toEqual({ props: { width: 100, height: 200 }, color: \"#FFFFFF\" })\n    expect(store.area).toBe(20000)\n    expect(typeof store.setWidth).toBe(\"function\")\n    expect(typeof store.setHeight).toBe(\"function\")\n})\ntest(\"it should compose multiple types with computed properties\", () => {\n    const { ComputedFactory2, ColorFactory } = createTestFactories()\n    const ComposedFactory = types.compose(ColorFactory, ComputedFactory2)\n    const store = ComposedFactory.create({ props: { width: 100, height: 200 } })\n    expect(getSnapshot(store)).toEqual({ props: { width: 100, height: 200 }, color: \"#FFFFFF\" })\n    expect(store.area).toBe(20000)\n    expect(typeof store.setWidth).toBe(\"function\")\n    expect(typeof store.setHeight).toBe(\"function\")\n})\ntest(\"methods get overridden by compose\", () => {\n    const A = types\n        .model({\n            count: types.optional(types.number, 0)\n        })\n        .actions(self => {\n            function increment() {\n                self.count += 1\n            }\n            return {\n                increment\n            }\n        })\n    const B = A.actions(self => ({\n        increment() {\n            self.count += 10\n        }\n    }))\n    const store = B.create()\n    expect(getSnapshot(store)).toEqual({ count: 0 })\n    expect(store.count).toBe(0)\n    store.increment()\n    expect(store.count).toBe(10)\n})\ntest(\"compose should add new props\", () => {\n    const A = types.model({\n        count: types.optional(types.number, 0)\n    })\n    const B = A.props({\n        called: types.optional(types.number, 0)\n    })\n    const store = B.create()\n    expect(getSnapshot(store)).toEqual({ count: 0, called: 0 })\n    expect(store.count).toBe(0)\n})\ntest(\"models should expose their actions to be used in a composable way\", () => {\n    const A = types\n        .model({\n            count: types.optional(types.number, 0)\n        })\n        .actions(self => {\n            function increment() {\n                self.count += 1\n            }\n            return {\n                increment\n            }\n        })\n    const B = A.props({\n        called: types.optional(types.number, 0)\n    }).actions(self => {\n        const baseIncrement = self.increment\n        return {\n            increment() {\n                baseIncrement()\n                self.called += 1\n            }\n        }\n    })\n    const store = B.create()\n    expect(getSnapshot(store)).toEqual({ count: 0, called: 0 })\n    expect(store.count).toBe(0)\n    store.increment()\n    expect(store.count).toBe(1)\n    expect(store.called).toBe(1)\n})\ntest(\"compose should be overwrite\", () => {\n    const A = types\n        .model({\n            name: \"\",\n            alias: \"\"\n        })\n        .views(self => ({\n            get displayName() {\n                return self.alias || self.name\n            }\n        }))\n    const B = A.props({\n        type: \"\"\n    }).views(self => ({\n        get displayName() {\n            return self.alias || self.name + self.type\n        }\n    }))\n    const storeA = A.create({ name: \"nameA\", alias: \"aliasA\" })\n    const storeB = B.create({ name: \"nameB\", alias: \"aliasB\", type: \"typeB\" })\n    const storeC = B.create({ name: \"nameC\", type: \"typeC\" })\n    expect(storeA.displayName).toBe(\"aliasA\")\n    expect(storeB.displayName).toBe(\"aliasB\")\n    expect(storeC.displayName).toBe(\"nameCtypeC\")\n})\n// === TYPE CHECKS ===\ntest(\"it should check the type correctly\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    expect(Factory.is(doc)).toEqual(true)\n    expect(Factory.is([])).toEqual(false)\n    expect(Factory.is({})).toEqual(true)\n    expect(Factory.is({ to: \"mars\" })).toEqual(true)\n    expect(Factory.is({ wrongKey: true })).toEqual(true)\n    expect(Factory.is({ to: 3 })).toEqual(false)\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"complex map / array values are optional by default\", () => {\n        expect(\n            types\n                .model({\n                    todo: types.model({})\n                })\n                .is({})\n        ).toBe(false)\n        expect(() =>\n            types\n                .model({\n                    todo: types.model({})\n                })\n                .create({} as any)\n        ).toThrow()\n        expect(\n            types\n                .model({\n                    todo: types.array(types.string)\n                })\n                .is({})\n        ).toBe(true) // TBD: or true?\n        expect(\n            getSnapshot(\n                types\n                    .model({\n                        todo: types.array(types.string)\n                    })\n                    .create({})\n            )\n        ).toEqual({ todo: [] })\n        expect(\n            types\n                .model({\n                    todo: types.map(types.string)\n                })\n                .is({})\n        ).toBe(true)\n        expect(\n            getSnapshot(\n                types\n                    .model({\n                        todo: types.map(types.string)\n                    })\n                    .create({})\n            )\n        ).toEqual({ todo: {} })\n    })\n}\n// === VIEW FUNCTIONS ===\ntest(\"view functions should be tracked\", () => {\n    const model = types\n        .model({\n            x: 3\n        })\n        .views(self => ({\n            doubler() {\n                return self.x * 2\n            }\n        }))\n        .create()\n    unprotect(model)\n    const values: number[] = []\n    const d = autorun(() => {\n        values.push(model.doubler())\n    })\n    model.x = 7\n    expect(values).toEqual([6, 14])\n})\ntest(\"view functions should not be allowed to change state\", () => {\n    const model = types\n        .model({\n            x: 3\n        })\n        .views(self => ({\n            doubler() {\n                self.x *= 2\n            }\n        }))\n        .actions(self => {\n            function anotherDoubler() {\n                self.x *= 2\n            }\n            return {\n                anotherDoubler\n            }\n        })\n        .create()\n    expect(() => model.doubler()).toThrow()\n    model.anotherDoubler()\n    expect(model.x).toBe(6)\n})\ntest(\"it should consider primitives as proposed defaults\", () => {\n    const now = new Date()\n    const Todo = types.model({\n        id: 0,\n        name: \"Hello world\",\n        done: false,\n        createdAt: now\n    })\n    const doc = Todo.create()\n    expect(getSnapshot(doc)).toEqual({\n        id: 0,\n        name: \"Hello world\",\n        done: false,\n        createdAt: now.getTime()\n    })\n})\ntest(\"it should throw if a non-primitive value is provided and no default can be created\", () => {\n    expect(() => {\n        types.model({\n            complex: {\n                a: 1,\n                b: 2\n            } as any\n        })\n    }).toThrow()\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should not be possible to remove a node from a parent if it is required, see \", () => {\n        const A = types.model(\"A\", { x: 3 })\n        const B = types.model(\"B\", { a: A })\n        const b = B.create({ a: { x: 7 } })\n        unprotect(b)\n        expect(() => {\n            detach(b.a)\n        }).toThrow(/Error while converting `undefined` to `A`/)\n        expect(() => {\n            destroy(b.a)\n        }).toThrow(/Error while converting `undefined` to `A`/)\n    })\n    test(\"it should be possible to remove a node from a parent if it is defined as type maybe \", () => {\n        const A = types.model(\"A\", { x: 3 })\n        const B = types.model(\"B\", { a: types.maybe(A) })\n        const b = B.create({ a: { x: 7 } })\n        unprotect(b)\n        expect(() => {\n            const a = b.a!\n            detach(a)\n            destroy(a)\n        }).not.toThrow()\n        expect(b.a).toBeUndefined()\n        expect(getSnapshot(b).a).toBeUndefined()\n    })\n    test(\"it should be possible to remove a node from a parent if it is defined as type maybeNull \", () => {\n        const A = types.model(\"A\", { x: 3 })\n        const B = types.model(\"B\", { a: types.maybeNull(A) })\n        const b = B.create({ a: { x: 7 } })\n        unprotect(b)\n        expect(() => {\n            const a = b.a!\n            detach(a)\n            destroy(a)\n        }).not.toThrow()\n        expect(b.a).toBe(null)\n        expect(getSnapshot(b).a).toBe(null)\n    })\n}\ntest(\"it should be possible to share states between views and actions using enhance\", () => {\n    const A = types.model({}).extend(self => {\n        const localState = observable.box(3)\n        return {\n            views: {\n                get x() {\n                    return localState.get()\n                }\n            },\n            actions: {\n                setX(value: number) {\n                    localState.set(value)\n                }\n            }\n        }\n    })\n    let x = 0\n    let a = A.create()\n    const d = reaction(\n        () => a.x,\n        v => {\n            x = v\n        }\n    )\n    a.setX(7)\n    expect(a.x).toBe(7)\n    expect(x).toBe(7)\n    d()\n})\ntest(\"It should throw if any other key is returned from extend\", () => {\n    const A = types.model({}).extend(() => ({ stuff() {} }) as any)\n    expect(() => A.create()).toThrow(/stuff/)\n})\n\ntest(\"782, TS + compose\", () => {\n    const User = types.model(\"User\", {\n        id: types.identifier,\n        name: types.maybe(types.string),\n        avatar: types.maybe(types.string)\n    })\n\n    const user = User.create({ id: \"someId\" })\n})\n\ntest(\"961 - model creating should not change snapshot\", () => {\n    const M = types.model({ foo: 1 })\n    const o = {}\n\n    const m = M.create(o)\n    expect(o).toEqual({})\n    expect(getSnapshot(m)).toEqual({ foo: 1 })\n})\n\nif (process.env.NODE_ENV === \"development\")\n    test(\"beautiful errors\", () => {\n        expect(() => {\n            types.model(\"User\", { x: (types.identifier as any)() })\n        }).toThrow(\"types.identifier is not a function\")\n        expect(() => {\n            types.model(\"User\", { x: { bla: true } as any })\n        }).toThrow(\n            \"Invalid type definition for property 'x', it looks like you passed an object. Try passing another model type or a types.frozen\"\n        )\n        expect(() => {\n            types.model(\"User\", { x: function () {} as any })\n        }).toThrow(\n            \"Invalid type definition for property 'x', it looks like you passed a function. Did you forget to invoke it, or did you intend to declare a view / action?\"\n        )\n    })\n\ntest(\"#967 - changing values in afterCreate/afterAttach when node is instantiated from view\", () => {\n    const Answer = types\n        .model(\"Answer\", {\n            title: types.string,\n            selected: false\n        })\n        .actions(self => ({\n            toggle() {\n                self.selected = !self.selected\n            }\n        }))\n    const Question = types\n        .model(\"Question\", { title: types.string, answers: types.array(Answer) })\n        .views(self => ({\n            get brokenView() {\n                // this should not be allowed\n                // MWE: disabled, MobX 6 no longer forbids this\n                // expect(() => {\n                //     self.answers[0].toggle()\n                // }).toThrow()\n                return 0\n            }\n        }))\n        .actions(self => ({\n            afterCreate() {\n                // we should allow changes even when inside a computed property when done inside afterCreate/afterAttach\n                self.answers[0].toggle()\n                // but not further computed changes\n                expect(self.brokenView).toBe(0)\n            },\n            afterAttach() {\n                // we should allow changes even when inside a computed property when done inside afterCreate/afterAttach\n                self.answers[0].toggle()\n                expect(self.brokenView).toBe(0)\n            }\n        }))\n\n    const Product = types\n        .model(\"Product\", {\n            questions: types.array(Question)\n        })\n        .views(self => ({\n            get selectedAnswers() {\n                const result = []\n                for (const question of self.questions) {\n                    result.push(question.answers.find(a => a.selected))\n                }\n                return result\n            }\n        }))\n\n    const product = Product.create({\n        questions: [\n            { title: \"Q 0\", answers: [{ title: \"A 0.0\" }, { title: \"A 0.1\" }] },\n            { title: \"Q 1\", answers: [{ title: \"A 1.0\" }, { title: \"A 1.1\" }] }\n        ]\n    })\n\n    // tslint:disable-next-line:no-unused-expression\n    product.selectedAnswers\n})\n\ntest(\"#993-1 - after attach should have a parent when accesing a reference directly\", () => {\n    const L4 = types\n        .model(\"Todo\", {\n            id: types.identifier,\n            finished: false\n        })\n        .actions(self => ({\n            afterAttach() {\n                expect(getParent(self)).toBeTruthy()\n            }\n        }))\n\n    const L3 = types.model({ l4: L4 }).actions(self => ({\n        afterAttach() {\n            expect(getParent(self)).toBeTruthy()\n        }\n    }))\n\n    const L2 = types\n        .model({\n            l3: L3\n        })\n        .actions(self => ({\n            afterAttach() {\n                expect(getParent(self)).toBeTruthy()\n            }\n        }))\n\n    const L1 = types\n        .model({\n            l2: L2,\n            selected: types.reference(L4)\n        })\n        .actions(self => ({\n            afterAttach() {\n                throw new MstError(\"should never be called\")\n            }\n        }))\n\n    const createL1 = () =>\n        L1.create({\n            l2: {\n                l3: {\n                    l4: {\n                        id: \"11124091-11c1-4dda-b2ed-7dd6323491a5\"\n                    }\n                }\n            },\n            selected: \"11124091-11c1-4dda-b2ed-7dd6323491a5\"\n        })\n\n    // test 1, real child first\n    {\n        const l1 = createL1()\n\n        const a = l1.l2.l3.l4\n        const b = l1.selected\n    }\n\n    // test 2, reference first\n    {\n        const l1 = createL1()\n\n        const a = l1.selected\n        const b = l1.l2.l3.l4\n    }\n})\n\ntest(\"#993-2 - references should have a parent even when the parent has not been accessed before\", () => {\n    const events: string[] = []\n\n    const L4 = types\n        .model(\"Todo\", {\n            id: types.identifier,\n            finished: false\n        })\n        .actions(self => ({\n            toggle() {\n                self.finished = !self.finished\n            },\n            afterCreate() {\n                events.push(\"l4-ac\")\n            },\n            afterAttach() {\n                events.push(\"l4-at\")\n            }\n        }))\n\n    const L3 = types.model({ l4: L4 }).actions(self => ({\n        afterCreate() {\n            events.push(\"l3-ac\")\n        },\n        afterAttach() {\n            events.push(\"l3-at\")\n        }\n    }))\n\n    const L2 = types\n        .model({\n            l3: L3\n        })\n        .actions(self => ({\n            afterCreate() {\n                events.push(\"l2-ac\")\n            },\n            afterAttach() {\n                events.push(\"l2-at\")\n            }\n        }))\n\n    const L1 = types\n        .model({\n            l2: L2,\n            selected: types.reference(L4)\n        })\n        .actions(self => ({\n            afterCreate() {\n                events.push(\"l1-ac\")\n            },\n            afterAttach() {\n                events.push(\"l1-at\")\n            }\n        }))\n\n    const createL1 = () =>\n        L1.create({\n            l2: {\n                l3: {\n                    l4: {\n                        id: \"11124091-11c1-4dda-b2ed-7dd6323491a5\"\n                    }\n                }\n            },\n            selected: \"11124091-11c1-4dda-b2ed-7dd6323491a5\"\n        })\n\n    const expectedEvents = [\n        \"l1-ac\",\n        \"l2-ac\",\n        \"l2-at\",\n        \"l3-ac\",\n        \"l3-at\",\n        \"l4-ac\",\n        \"l4-at\",\n        \"onSnapshot\",\n        \"-\",\n        \"onSnapshot\"\n    ]\n\n    // test 1, real child first\n    {\n        const l1 = createL1()\n        onSnapshot(l1, () => {\n            events.push(\"onSnapshot\")\n        })\n\n        l1.l2.l3.l4.toggle()\n        events.push(\"-\")\n        l1.selected.toggle()\n        expect(events).toEqual(expectedEvents)\n    }\n\n    const expectedEvents2 = [\n        \"l1-ac\",\n        \"l4-ac\",\n        \"l3-ac\",\n        \"l2-ac\",\n        \"l2-at\",\n        \"l3-at\",\n        \"l4-at\",\n        \"onSnapshot\",\n        \"-\",\n        \"onSnapshot\"\n    ]\n\n    // test 2, reference first\n    // the order of hooks is different but they are all called\n    events.length = 0\n    {\n        const l1 = createL1()\n        onSnapshot(l1, () => {\n            events.push(\"onSnapshot\")\n        })\n\n        l1.selected.toggle()\n        events.push(\"-\")\n        l1.l2.l3.l4.toggle()\n        expect(events).toEqual(expectedEvents2)\n    }\n\n    // test 3, reference get parent should be available from the beginning and all the way to the root\n    {\n        const rootL1 = createL1()\n        const l4 = rootL1.selected\n        const l3 = getParent(l4)\n        expect(l3).toBeTruthy()\n        const l2 = getParent(l3)\n        expect(l2).toBeTruthy()\n        const l1 = getParent(l2)\n        expect(l1).toBeTruthy()\n\n        expect(l1).toBe(rootL1)\n        expect(l2).toBe(rootL1.l2)\n        expect(l3).toBe(rootL1.l2.l3)\n        expect(l4).toBe(rootL1.l2.l3.l4)\n    }\n})\n\ntest(\"it should emit patches when applySnapshot is used\", () => {\n    const { Factory } = createTestFactories()\n    const doc = Factory.create()\n    let patches: IJsonPatch[] = []\n    onPatch(doc, patch => patches.push(patch))\n    applySnapshot(doc, { ...getSnapshot(doc), to: \"universe\" })\n    expect(patches).toEqual([{ op: \"replace\", path: \"/to\", value: \"universe\" }])\n})\n\ntest(\"isAlive must be reactive\", () => {\n    const Todo = types.model({ text: types.string })\n    const TodoStore = types.model({\n        todos: types.array(Todo),\n        todo: types.maybe(Todo)\n    })\n\n    const store = TodoStore.create({\n        todos: [{ text: \"1\" }, { text: \"2\" }],\n        todo: { text: \"3\" }\n    })\n    unprotect(store)\n\n    const t1 = store.todos[0]!\n    const t2 = store.todos[1]!\n    const t3 = store.todo!\n\n    let calls = 0\n    const r1 = reaction(\n        () => isAlive(t1),\n        v => {\n            expect(v).toBe(false)\n            calls++\n        }\n    )\n    const r2 = reaction(\n        () => isAlive(t2),\n        v => {\n            expect(v).toBe(false)\n            calls++\n        }\n    )\n    const r3 = reaction(\n        () => isAlive(t3),\n        v => {\n            expect(v).toBe(false)\n            calls++\n        }\n    )\n\n    try {\n        store.todos = cast([])\n        store.todo = undefined\n\n        expect(calls).toBe(3)\n    } finally {\n        r1()\n        r2()\n        r3()\n    }\n})\n\ntest(\"#1112 - identifier cache should be cleared for unaccessed wrapped objects\", () => {\n    const mock1 = [\n        { id: \"1\", name: \"Kate\" },\n        { id: \"2\", name: \"John\" }\n    ]\n    const mock2 = [\n        { id: \"3\", name: \"Andrew\" },\n        { id: \"2\", name: \"John\" }\n    ]\n\n    const mock1_2 = mock1.map((i, index) => ({ text: `Text${index}`, entity: i }))\n    const mock2_2 = mock2.map((i, index) => ({ text: `Text${index}`, entity: i }))\n\n    const Entity = types.model({\n        id: types.identifier,\n        name: types.string\n    })\n\n    const Wrapper = types.model({\n        text: types.string,\n        entity: Entity\n    })\n\n    const Store = types\n        .model({\n            list: types.optional(types.array(Wrapper), []),\n            selectedId: 2\n        })\n        .views(self => ({\n            get selectedEntity() {\n                return resolveIdentifier(Entity, self, self.selectedId)\n            }\n        }))\n\n    const store = Store.create()\n    unprotect(store)\n\n    store.list.replace(mock1_2)\n    store.list.replace(mock2_2)\n\n    expect(store.selectedEntity!.id).toBe(\"2\")\n})\n\ntest(\"#1173 - detaching a model should not screw it\", () => {\n    const AM = types.model({ x: 5 })\n    const Store = types.model({ item: types.maybe(AM) })\n    const s = Store.create({ item: { x: 6 } })\n    const n0 = s.item\n\n    unprotect(s)\n\n    const detachedItem = detach(s.item!)\n    expect(s.item).not.toBe(detachedItem)\n    expect(s.item).toBeUndefined()\n    expect(detachedItem.x).toBe(6)\n    expect(detachedItem).toBe(n0!)\n})\n\ntest(\"#1702 - should not throw with useProxies: 'ifavailable'\", () => {\n    configure({\n        useProxies: \"ifavailable\"\n    })\n\n    const M = types.model({ x: 5 }).views(self => ({\n        get y() {\n            return self.x\n        }\n    }))\n\n    expect(() => {\n        M.create({})\n    }).not.toThrow()\n})\n"
  },
  {
    "path": "__tests__/core/optimizations.test.ts",
    "content": "import { getSnapshot, applySnapshot, unprotect, types } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"it should avoid processing patch if is exactly the current one in applySnapshot\", () => {\n    const Model = types.model({\n        a: types.number,\n        b: types.string\n    })\n    const store = Model.create({ a: 1, b: \"hello\" })\n    const snapshot = getSnapshot(store)\n    applySnapshot(store, snapshot)\n    expect(getSnapshot(store)).toBe(snapshot) // no new snapshot emitted\n})\ntest(\"it should avoid processing patch if is exactly the current one in reconcile\", () => {\n    const Model = types.model({\n        a: types.number,\n        b: types.string\n    })\n    const RootModel = types.model({\n        a: Model\n    })\n    const store = RootModel.create({ a: { a: 1, b: \"hello\" } })\n    unprotect(store)\n    // NOTE: snapshots are not equal after property access anymore,\n    // so we test initial and actual ones separately\n    const snapshot = getSnapshot(store)\n    expect(getSnapshot(store)).toEqual(snapshot)\n\n    store.a = snapshot.a\n    // check whether reconciliation works on initial values\n    expect(getSnapshot(store)).toEqual(snapshot)\n\n    // access property to initialize observable instance\n    expect(getSnapshot(store.a)).toEqual(snapshot.a)\n\n    // check whether initializing instance does not cause snapshot invalidation\n    const actualSnapshot = getSnapshot(store)\n    expect(actualSnapshot.a).toBe(snapshot.a)\n})\n"
  },
  {
    "path": "__tests__/core/optional-extension.test.ts",
    "content": "import { getSnapshot, types, unprotect } from \"../../src\"\nimport { describe, expect, test } from \"bun:test\"\n\ndescribe(\"null as default\", () => {\n    describe(\"basic tests\", () => {\n        const M = types.model({\n            x: types.optional(types.number, 1, [null]),\n            y: types.optional(types.number, () => 2, [null])\n        })\n\n        test(\"with optional values, then assigned values\", () => {\n            const m = M.create({\n                x: null,\n                y: null\n            })\n            unprotect(m)\n\n            expect(m.x).toBe(1)\n            expect(m.y).toBe(2)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 1,\n                y: 2\n            })\n\n            m.x = 10\n            m.y = 20\n            expect(m.x).toBe(10)\n            expect(m.y).toBe(20)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 10,\n                y: 20\n            })\n        })\n\n        test(\"with given values, then assigned optional values\", () => {\n            const m = M.create({\n                x: 10,\n                y: 20\n            })\n            unprotect(m)\n\n            expect(m.x).toBe(10)\n            expect(m.y).toBe(20)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 10,\n                y: 20\n            })\n\n            m.x = null as any\n            m.y = null as any\n            expect(m.x).toBe(1)\n            expect(m.y).toBe(2)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 1,\n                y: 2\n            })\n        })\n    })\n\n    test(\"when the underlying type accepts undefined it should be ok\", () => {\n        const M = types.model({\n            a: types.optional(types.union(types.undefined, types.number), undefined, [null]),\n            b: types.optional(types.union(types.undefined, types.number), 5, [null])\n        })\n\n        {\n            const m = M.create({\n                a: null,\n                b: null\n            })\n            expect(m.a).toBeUndefined()\n            expect(m.b).toBe(5)\n            expect(getSnapshot(m)).toEqual({\n                a: undefined,\n                b: 5\n            })\n        }\n\n        {\n            const m = M.create({\n                a: 10,\n                b: 20\n            })\n            expect(m.a).toBe(10)\n            expect(m.b).toBe(20)\n            expect(getSnapshot(m)).toEqual({\n                a: 10,\n                b: 20\n            })\n        }\n\n        {\n            const m = M.create({\n                a: undefined,\n                b: undefined\n            })\n            expect(m.a).toBeUndefined()\n            expect(m.b).toBeUndefined()\n            expect(getSnapshot(m)).toEqual({\n                a: undefined,\n                b: undefined\n            })\n        }\n    })\n\n    test(\"when the underlying type does not accept undefined, then undefined should throw\", () => {\n        const M = types.model({\n            a: types.optional(types.number, 5, [null]),\n            b: types.optional(types.number, 6, [null])\n        })\n\n        {\n            const m = M.create({\n                a: null,\n                b: null\n            })\n            expect(m.a).toBe(5)\n            expect(m.b).toBe(6)\n        }\n\n        if (process.env.NODE_ENV !== \"production\") {\n            expect(() => {\n                M.create({\n                    a: null,\n                    b: undefined as any // undefined is not valid\n                })\n            }).toThrow(\"value `undefined` is not assignable to type: `number`\")\n\n            expect(() => {\n                M.create({\n                    a: null\n                    // b: null missing, but should be there\n                } as any)\n            }).toThrow(\"value `undefined` is not assignable to type: `number`\")\n        }\n    })\n})\n\ndescribe(\"'empty' or false as default\", () => {\n    describe(\"basic tests\", () => {\n        const M = types.model({\n            x: types.optional(types.number, 1, [\"empty\", false]),\n            y: types.optional(types.number, () => 2, [\"empty\", false])\n        })\n\n        test(\"with optional values, then assigned values\", () => {\n            const m = M.create({\n                x: \"empty\",\n                y: false\n            })\n            unprotect(m)\n\n            expect(m.x).toBe(1)\n            expect(m.y).toBe(2)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 1,\n                y: 2\n            })\n\n            m.x = 10\n            m.y = 20\n            expect(m.x).toBe(10)\n            expect(m.y).toBe(20)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 10,\n                y: 20\n            })\n        })\n\n        test(\"with given values, then assigned 'empty'\", () => {\n            const m = M.create({\n                x: 10,\n                y: 20\n            })\n            unprotect(m)\n\n            expect(m.x).toBe(10)\n            expect(m.y).toBe(20)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 10,\n                y: 20\n            })\n\n            m.x = \"empty\" as any\n            m.y = false as any\n            expect(m.x).toBe(1)\n            expect(m.y).toBe(2)\n\n            expect(getSnapshot(m)).toEqual({\n                x: 1,\n                y: 2\n            })\n        })\n    })\n\n    test(\"when the underlying type accepts undefined it should be ok\", () => {\n        const M = types.model({\n            a: types.optional(types.union(types.undefined, types.number), undefined, [\n                \"empty\",\n                false\n            ]),\n            b: types.optional(types.union(types.undefined, types.number), 5, [\"empty\", false])\n        })\n\n        {\n            const m = M.create({\n                a: \"empty\",\n                b: false\n            })\n            expect(m.a).toBeUndefined()\n            expect(m.b).toBe(5)\n            expect(getSnapshot(m)).toEqual({\n                a: undefined,\n                b: 5\n            })\n        }\n\n        {\n            const m = M.create({\n                a: 10,\n                b: 20\n            })\n            expect(m.a).toBe(10)\n            expect(m.b).toBe(20)\n            expect(getSnapshot(m)).toEqual({\n                a: 10,\n                b: 20\n            })\n        }\n\n        {\n            const m = M.create({\n                a: undefined,\n                b: undefined\n            })\n            expect(m.a).toBeUndefined()\n            expect(m.b).toBeUndefined()\n            expect(getSnapshot(m)).toEqual({\n                a: undefined,\n                b: undefined\n            })\n        }\n    })\n\n    test(\"when the underlying type does not accept undefined, then undefined should throw\", () => {\n        const M = types.model({\n            a: types.optional(types.number, 5, [\"empty\", false]),\n            b: types.optional(types.number, 6, [\"empty\", false])\n        })\n\n        {\n            const m = M.create({\n                a: \"empty\",\n                b: false\n            })\n            expect(m.a).toBe(5)\n            expect(m.b).toBe(6)\n        }\n\n        if (process.env.NODE_ENV !== \"production\") {\n            expect(() => {\n                M.create({\n                    a: undefined as any,\n                    b: undefined as any\n                })\n            }).toThrow(\"value `undefined` is not assignable to type: `number`\")\n        }\n    })\n})\n\ntest(\"cached snapshots should be ok when using default values\", () => {\n    const M = types.model({ x: 5, y: 6 })\n    const Store = types.model({\n        deep: types.model({\n            a: types.optional(types.undefined, undefined),\n            b: types.optional(types.undefined, undefined, [\"empty\"]),\n            c: types.optional(types.number, 5),\n            d: types.optional(types.number, 5, [\"empty\"]),\n\n            a2: types.optional(types.undefined, () => undefined),\n            b2: types.optional(types.undefined, () => undefined, [\"empty\"]),\n            c2: types.optional(types.number, () => 5),\n            d2: types.optional(types.number, () => 5, [\"empty\"]),\n\n            a3: types.optional(M, { y: 20 }),\n            b3: types.optional(M, { y: 20 }, [\"empty\"]),\n            c3: types.optional(M, () => M.create({ y: 20 })),\n            d3: types.optional(M, () => M.create({ y: 20 }), [\"empty\"]),\n            e3: types.optional(M, () => ({ y: 20 })),\n            f3: types.optional(M, () => ({ y: 20 }), [\"empty\"])\n        })\n    })\n\n    const s = Store.create({\n        deep: {\n            b: \"empty\",\n            d: \"empty\",\n            b2: \"empty\",\n            d2: \"empty\",\n            b3: \"empty\",\n            d3: \"empty\",\n            f3: \"empty\"\n        }\n    })\n    expect(getSnapshot(s)).toEqual({\n        deep: {\n            a: undefined,\n            b: undefined,\n            c: 5,\n            d: 5,\n            a2: undefined,\n            b2: undefined,\n            c2: 5,\n            d2: 5,\n            a3: { x: 5, y: 20 },\n            b3: { x: 5, y: 20 },\n            c3: { x: 5, y: 20 },\n            d3: { x: 5, y: 20 },\n            e3: { x: 5, y: 20 },\n            f3: { x: 5, y: 20 }\n        }\n    })\n})\n"
  },
  {
    "path": "__tests__/core/optional.test.ts",
    "content": "import { getSnapshot, types, unprotect, applySnapshot, cast } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"it should provide a default value, if no snapshot is provided\", () => {\n    const Row = types.model({\n        name: \"\",\n        quantity: 0\n    })\n    const Factory = types.model({\n        rows: types.optional(types.array(Row), [{ name: \"test\" }])\n    })\n    const doc = Factory.create()\n    expect(getSnapshot(doc)).toEqual({ rows: [{ name: \"test\", quantity: 0 }] })\n})\n\ntest(\"it should use the snapshot if provided\", () => {\n    const Row = types.model({\n        name: \"\",\n        quantity: 0\n    })\n    const Factory = types.model({\n        rows: types.optional(types.array(Row), [{ name: \"test\" }])\n    })\n    const doc = Factory.create({ rows: [{ name: \"snapshot\", quantity: 0 }] })\n    expect(getSnapshot(doc)).toEqual({ rows: [{ name: \"snapshot\", quantity: 0 }] })\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should throw if default value is invalid snapshot\", () => {\n        const Row = types.model({\n            name: types.string,\n            quantity: types.number\n        })\n        const error = expect(() => {\n            types.model({\n                rows: types.optional(types.array(Row), [{}] as any)\n            })\n        }).toThrow()\n    })\n\n    test(\"it should throw bouncing errors from its sub-type\", () => {\n        const Row = types.model({\n            name: types.string,\n            quantity: types.number\n        })\n        const RowList = types.optional(types.array(Row), [])\n        const error = expect(() => {\n            RowList.create([\n                { name: \"a\", quantity: 1 },\n                { name: \"b\", quantity: \"x\" }\n            ] as any)\n        }).toThrow()\n    })\n}\n\ntest(\"it should accept a function to provide dynamic values\", () => {\n    let defaultValue = 1\n    const Factory = types.model({\n        a: types.optional(types.number, () => defaultValue)\n    })\n    expect(getSnapshot(Factory.create())).toEqual({ a: 1 })\n    defaultValue = 2\n    expect(getSnapshot(Factory.create())).toEqual({ a: 2 })\n    defaultValue = \"hello world!\" as any\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => Factory.create()).toThrow(\n            `[mobx-state-tree] Error while converting \\`\"hello world!\"\\` to \\`number\\`:\\n\\n    value \\`\"hello world!\"\\` is not assignable to type: \\`number\\` (Value is not a number).`\n        )\n    }\n})\n\ntest(\"Values should reset to default if omitted in snapshot\", () => {\n    const Store = types.model({\n        todo: types.model({\n            id: types.identifier,\n            done: false,\n            title: \"test\",\n            thing: types.frozen({})\n        })\n    })\n    const store = Store.create({ todo: { id: \"2\" } })\n    unprotect(store)\n    store.todo.done = true\n    expect(store.todo.done).toBe(true)\n    store.todo = cast({ title: \"stuff\", id: \"2\" })\n    expect(store.todo.title).toBe(\"stuff\")\n    expect(store.todo.done).toBe(false)\n})\n\ntest(\"optional frozen should fallback to default value if snapshot is undefined\", () => {\n    const Store = types.model({ thing: types.frozen({}) })\n    const store = Store.create({\n        thing: null\n    })\n\n    expect(store.thing).toBeNull()\n    applySnapshot(store, {})\n    expect(store.thing).toBeDefined()\n    expect(store.thing).toEqual({})\n})\n\ntest(\"an instance is not a valid default value, snapshot or function that creates instance must be used\", () => {\n    const Row = types.model(\"Row\", {\n        name: \"\",\n        quantity: 0\n    })\n\n    // passing a node directly, without a generator function\n    expect(() => {\n        types.model({ rows: types.optional(types.array(Row), types.array(Row).create()) })\n    }).toThrow(\n        \"default value cannot be an instance, pass a snapshot or a function that creates an instance/snapshot instead\"\n    )\n\n    // an alike node but created from a different yet equivalent type\n    const e = expect(() => {\n        const Factory = types.model({\n            rows: types.optional(types.array(Row), () => types.array(Row).create())\n        })\n        // we need to create the node for it to throw, since generator functions are typechecked when nodes are created\n        // tslint:disable-next-line:no-unused-expression\n        Factory.create()\n    })\n    if (process.env.NODE_ENV === \"production\") {\n        e.not.toThrow()\n    } else {\n        e.toThrow(\"Error while converting <> to `Row[]`\")\n    }\n\n    {\n        // a node created on a generator function of the exact same type\n        const RowArray = types.array(Row)\n        const Factory = types.model(\"Factory\", {\n            rows: types.optional(RowArray, () => RowArray.create())\n        })\n        const doc = Factory.create()\n        expect(getSnapshot(doc)).toEqual({ rows: [] })\n    }\n})\n\ntest(\"undefined can work as a missing value\", () => {\n    const M = types.model({ x: types.union(types.undefined, types.number) })\n    const m1 = M.create({ x: 5 })\n    expect(m1.x).toBe(5)\n    const m2 = M.create({ x: undefined })\n    expect(m2.x).toBeUndefined()\n    const m3 = M.create({}) // is ok as well (even in TS)\n    expect(m3.x).toBeUndefined()\n})\n"
  },
  {
    "path": "__tests__/core/parent-properties.test.ts",
    "content": "import { types, getEnv, getParent, getPath, Instance } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nconst ChildModel = types\n    .model(\"Child\", {\n        parentPropertyIsNullAfterCreate: false,\n        parentEnvIsNullAfterCreate: false,\n        parentPropertyIsNullAfterAttach: false\n    })\n    .views(self => {\n        return {\n            get parent(): IParentModelInstance {\n                return getParent<typeof ParentModel>(self)\n            }\n        }\n    })\n    .actions(self => ({\n        afterCreate() {\n            self.parentPropertyIsNullAfterCreate = typeof self.parent.fetch === \"undefined\"\n            self.parentEnvIsNullAfterCreate = typeof getEnv(self.parent).fetch === \"undefined\"\n        },\n        afterAttach() {\n            self.parentPropertyIsNullAfterAttach = typeof self.parent.fetch === \"undefined\"\n        }\n    }))\n\nconst ParentModel = types\n    .model(\"Parent\", {\n        child: types.optional(ChildModel, {})\n    })\n    .views(self => ({\n        get fetch() {\n            return getEnv(self).fetch\n        }\n    }))\n\ninterface IParentModelInstance extends Instance<typeof ParentModel> {}\n\n// NOTE: parents are now always created before children;\n// moreover, we do not actually have actions hash during object-node creation\ntest(\"Parent property have value during child's afterCreate() event\", () => {\n    const mockFetcher = () => Promise.resolve(true)\n    const parent = ParentModel.create({}, { fetch: mockFetcher })\n    // Because the child is created before the parent creation is finished, this one will yield `true` (the .fetch view is still undefined)\n    expect(parent.child.parentPropertyIsNullAfterCreate).toBe(false)\n    // ... but, the env is available\n    expect(parent.child.parentEnvIsNullAfterCreate).toBe(false)\n})\ntest(\"Parent property has value during child's afterAttach() event\", () => {\n    const mockFetcher = () => Promise.resolve(true)\n    const parent = ParentModel.create({}, { fetch: mockFetcher })\n    expect(parent.child.parentPropertyIsNullAfterAttach).toBe(false)\n})\n\ntest(\"#917\", () => {\n    const SubTodo = types\n        .model(\"SubTodo\", {\n            id: types.optional(types.number, () => Math.random()),\n            title: types.string,\n            finished: false\n        })\n        .views(self => ({\n            get path() {\n                return getPath(self)\n            }\n        }))\n        .actions(self => ({\n            toggle() {\n                self.finished = !self.finished\n            }\n        }))\n\n    const Todo = types\n        .model(\"Todo\", {\n            id: types.optional(types.number, () => Math.random()),\n            title: types.string,\n            finished: false,\n            subTodos: types.array(SubTodo)\n        })\n        .views(self => ({\n            get path() {\n                return getPath(self)\n            }\n        }))\n        .actions(self => ({\n            toggle() {\n                self.finished = !self.finished\n            }\n        }))\n\n    const TodoStore = types\n        .model(\"TodoStore\", {\n            todos: types.array(Todo)\n        })\n        .views(self => ({\n            get unfinishedTodoCount() {\n                return self.todos.filter(todo => !todo.finished).length\n            }\n        }))\n        .actions(self => ({\n            addTodo(title: string) {\n                self.todos.push({\n                    title,\n                    subTodos: [\n                        {\n                            title\n                        }\n                    ]\n                })\n            }\n        }))\n\n    const store2 = TodoStore.create({\n        todos: [\n            Todo.create({\n                title: \"get Coffee\",\n                subTodos: [\n                    SubTodo.create({\n                        title: \"test\"\n                    })\n                ]\n            })\n        ]\n    })\n\n    expect(store2.todos[0].path).toBe(\"/todos/0\")\n    expect(store2.todos[0].subTodos[0].path).toBe(\"/todos/0/subTodos/0\")\n})\n"
  },
  {
    "path": "__tests__/core/pointer.test.ts",
    "content": "import { types, unprotect, IAnyModelType, castToReferenceSnapshot } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nfunction Pointer<IT extends IAnyModelType>(Model: IT) {\n    return types.model(\"PointerOf\" + Model.name, {\n        value: types.maybe(types.reference(Model))\n    })\n}\nconst Todo = types.model(\"Todo\", {\n    id: types.identifier,\n    name: types.string\n})\ntest(\"it should allow array of pointer objects\", () => {\n    const TodoPointer = Pointer(Todo)\n    const AppStore = types.model(\"AppStore\", {\n        todos: types.array(Todo),\n        selected: types.optional(types.array(TodoPointer), [])\n    })\n    const store = AppStore.create({\n        todos: [\n            { id: \"1\", name: \"Hello\" },\n            { id: \"2\", name: \"World\" }\n        ],\n        selected: []\n    })\n    unprotect(store)\n    const ref = TodoPointer.create({ value: castToReferenceSnapshot(store.todos[0]) }) // Fails because store.todos does not belongs to the same tree\n    store.selected.push(ref)\n    expect(store.selected[0].value).toBe(store.todos[0])\n})\ntest(\"it should allow array of pointer objects - 2\", () => {\n    const TodoPointer = Pointer(Todo)\n    const AppStore = types.model({\n        todos: types.array(Todo),\n        selected: types.optional(types.array(TodoPointer), [])\n    })\n    const store = AppStore.create({\n        todos: [\n            { id: \"1\", name: \"Hello\" },\n            { id: \"2\", name: \"World\" }\n        ],\n        selected: []\n    })\n    unprotect(store)\n    const ref = TodoPointer.create()\n    store.selected.push(ref)\n    ref.value = store.todos[0]\n    expect(store.selected[0].value).toBe(store.todos[0])\n})\ntest(\"it should allow array of pointer objects - 3\", () => {\n    const TodoPointer = Pointer(Todo)\n    const AppStore = types.model({\n        todos: types.array(Todo),\n        selected: types.optional(types.array(TodoPointer), [])\n    })\n    const store = AppStore.create({\n        todos: [\n            { id: \"1\", name: \"Hello\" },\n            { id: \"2\", name: \"World\" }\n        ],\n        selected: []\n    })\n    unprotect(store)\n    const ref = TodoPointer.create({ value: castToReferenceSnapshot(store.todos[0]) })\n    store.selected.push(ref)\n    expect(store.selected[0].value).toBe(store.todos[0])\n})\ntest(\"it should allow array of pointer objects - 4\", () => {\n    const TodoPointer = Pointer(Todo)\n    const AppStore = types.model({\n        todos: types.array(Todo),\n        selected: types.optional(types.array(TodoPointer), [])\n    })\n    const store = AppStore.create({\n        todos: [\n            { id: \"1\", name: \"Hello\" },\n            { id: \"2\", name: \"World\" }\n        ],\n        selected: []\n    })\n    unprotect(store)\n    const ref = TodoPointer.create() // Fails because ref is required\n    store.selected.push(ref)\n    ref.value = store.todos[0]\n    expect(ref.value).toBe(store.todos[0])\n})\n"
  },
  {
    "path": "__tests__/core/primitives.test.ts",
    "content": "import { isFinite, isFloat, isInteger } from \"../../src/utils\"\nimport { types, applySnapshot, getSnapshot } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"Date instance can be reused\", () => {\n    const Model = types.model({\n        a: types.model({\n            b: types.string\n        }),\n        c: types.Date // types.string -> types.Date\n    })\n    const Store = types\n        .model({\n            one: Model,\n            index: types.array(Model)\n        })\n        .actions(self => {\n            function set(one: typeof Model.Type) {\n                self.one = one\n            }\n            function push(model: typeof Model.Type) {\n                self.index.push(model)\n            }\n            return {\n                set,\n                push\n            }\n        })\n    const object = { a: { b: \"string\" }, c: new Date() } // string -> date (number)\n    const instance = Store.create({\n        one: object,\n        index: [object]\n    })\n    instance.set(object)\n    expect(() => instance.push(object)).not.toThrow()\n    expect(instance.one.c).toBe(object.c)\n    expect(instance.index[0].c).toBe(object.c)\n})\ntest(\"Date can be rehydrated using unix timestamp\", () => {\n    const time = new Date()\n    const newTime = 6813823163\n    const Factory = types.model({\n        date: types.optional(types.Date, () => time)\n    })\n    const store = Factory.create()\n    expect(store.date.getTime()).toBe(time.getTime())\n    applySnapshot(store, { date: newTime })\n    expect(store.date.getTime()).toBe(newTime)\n    expect(getSnapshot(store).date).toBe(newTime)\n})\n\ntest(\"check isInteger\", () => {\n    expect(isInteger(5)).toBe(true)\n    expect(isInteger(-5)).toBe(true)\n    expect(isInteger(5.2)).toBe(false)\n})\n\ntest(\"Default inference for integers is 'number'\", () => {\n    const A = types.model({\n        x: 3\n    })\n    expect(\n        A.is({\n            x: 2.5\n        })\n    ).toBe(true)\n})\n\ntest(\"check isFloat\", () => {\n    expect(isFloat(3.14)).toBe(true)\n    expect(isFloat(-2.5)).toBe(true)\n    expect(isFloat(Infinity)).toBe(true)\n    expect(isFloat(10)).toBe(false)\n    expect(isFloat(0)).toBe(false)\n    expect(isFloat(\"3.14\")).toBe(false)\n    expect(isFloat(null)).toBe(false)\n    expect(isFloat(undefined)).toBe(false)\n    expect(isFloat(NaN)).toBe(false)\n})\n\ntest(\"check isFinite\", () => {\n    expect(isFinite(3.14)).toBe(true)\n    expect(isFinite(-2.5)).toBe(true)\n    expect(isFinite(10)).toBe(true)\n    expect(isFinite(0)).toBe(true)\n    expect(isFinite(\"3.14\")).toBe(false)\n    expect(isFinite(null)).toBe(false)\n    expect(isFinite(undefined)).toBe(false)\n    expect(isFinite(NaN)).toBe(false)\n    expect(isFinite(Infinity)).toBe(false)\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"Passing non integer to types.integer\", () => {\n        const Size = types.model({\n            width: types.integer,\n            height: 20\n        })\n\n        expect(() => {\n            const size = Size.create({ width: 10 })\n        }).not.toThrow()\n\n        expect(() => {\n            const size = Size.create({ width: 10.5 })\n        }).toThrow()\n    })\n}\n\ntest(\"types.bigint accepts bigint values\", () => {\n    const Model = types.model({\n        id: types.bigint,\n        value: types.bigint\n    })\n    const instance = Model.create({ id: BigInt(1), value: BigInt(2) })\n    expect(instance.id).toBe(BigInt(1))\n    expect(instance.value).toBe(BigInt(2))\n    expect(types.bigint.describe()).toBe(\"bigint\")\n})\n\ntest(\"types.bigint snapshot round-trip preserves bigint\", () => {\n    const Model = types.model({ id: types.bigint })\n    const instance = Model.create({ id: BigInt(\"9007199254740993\") })\n    const snapshot = getSnapshot(instance)\n    expect(snapshot.id).toBe(\"9007199254740993\")\n    expect(typeof snapshot.id).toBe(\"string\")\n})\n\ntest(\"types.bigint snapshot is JSON-serializable and deserializes correctly\", () => {\n    const Model = types.model({ id: types.bigint, value: types.bigint })\n    const instance = Model.create({ id: BigInt(1), value: BigInt(\"9007199254740993\") })\n    const snapshot = getSnapshot(instance)\n    const json = JSON.stringify(snapshot)\n    expect(json).toBe('{\"id\":\"1\",\"value\":\"9007199254740993\"}')\n    const parsed = JSON.parse(json)\n    const restored = Model.create(parsed)\n    expect(restored.id).toBe(BigInt(1))\n    expect(restored.value).toBe(BigInt(\"9007199254740993\"))\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"Passing non bigint/string/number to types.bigint\", () => {\n        const Model = types.model({ id: types.bigint })\n        expect(() => Model.create({ id: BigInt(1) })).not.toThrow()\n        expect(() => Model.create({ id: \"1\" })).not.toThrow()\n        expect(() => Model.create({ id: 1 })).not.toThrow()\n        expect(() => Model.create({ id: null as any })).toThrow()\n    })\n}\n"
  },
  {
    "path": "__tests__/core/protect.test.ts",
    "content": "import { protect, unprotect, applySnapshot, types, isProtected, getParent, cast } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nconst Todo = types\n    .model(\"Todo\", {\n        title: \"\"\n    })\n    .actions(self => {\n        function setTitle(newTitle: string) {\n            self.title = newTitle\n        }\n        return {\n            setTitle\n        }\n    })\nconst Store = types.model(\"Store\", {\n    todos: types.array(Todo)\n})\nfunction createTestStore() {\n    return Store.create({\n        todos: [{ title: \"Get coffee\" }, { title: \"Get biscuit\" }]\n    })\n}\ntest(\"it should be possible to protect an object\", () => {\n    const store = createTestStore()\n    unprotect(store)\n    store.todos[1].title = \"A\"\n    protect(store)\n    expect(() => {\n        store.todos[0].title = \"B\"\n    }).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo@/todos/0', the object is protected and can only be modified by using an action.\"\n    )\n    expect(store.todos[1].title).toBe(\"A\")\n    expect(store.todos[0].title).toBe(\"Get coffee\")\n    store.todos[0].setTitle(\"B\")\n    expect(store.todos[0].title).toBe(\"B\")\n})\ntest(\"protect should protect against any update\", () => {\n    const store = createTestStore()\n    expect(\n        // apply Snapshot / patch are currently allowed, even outside protected mode\n        () => {\n            applySnapshot(store, { todos: [{ title: \"Get tea\" }] })\n        }\n    ).not.toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo@<root>', the object is protected and can only be modified by using an action.\"\n    )\n    expect(() => {\n        store.todos.push({ title: \"test\" })\n    }).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo[]@/todos', the object is protected and can only be modified by using an action.\"\n    )\n    expect(() => {\n        store.todos[0].title = \"test\"\n    }).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo@/todos/0', the object is protected and can only be modified by using an action.\"\n    )\n})\ntest(\"protect should also protect children\", () => {\n    const store = createTestStore()\n    expect(() => {\n        store.todos[0].title = \"B\"\n    }).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo@/todos/0', the object is protected and can only be modified by using an action.\"\n    )\n    store.todos[0].setTitle(\"B\")\n    expect(store.todos[0].title).toBe(\"B\")\n})\ntest(\"unprotected mode should be lost when attaching children\", () => {\n    const store = Store.create({ todos: [] })\n    const t1 = Todo.create({ title: \"hello\" })\n    unprotect(t1)\n    expect(isProtected(t1)).toBe(false)\n    expect(isProtected(store)).toBe(true)\n    t1.title = \"world\" // ok\n    unprotect(store)\n    store.todos.push(t1)\n    protect(store)\n    expect(isProtected(t1)).toBe(true)\n    expect(isProtected(store)).toBe(true)\n    expect(() => {\n        t1.title = \"B\"\n    }).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo@/todos/0', the object is protected and can only be modified by using an action.\"\n    )\n    store.todos[0].setTitle(\"C\")\n    expect(store.todos[0].title).toBe(\"C\")\n})\ntest(\"protected mode should be inherited when attaching children\", () => {\n    const store = Store.create({ todos: [] })\n    unprotect(store)\n    const t1 = Todo.create({ title: \"hello\" })\n    expect(isProtected(t1)).toBe(true)\n    expect(isProtected(store)).toBe(false)\n    expect(() => {\n        t1.title = \"B\"\n    }).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Todo@<root>', the object is protected and can only be modified by using an action.\"\n    )\n    store.todos.push(t1)\n    t1.title = \"world\" // ok, now unprotected\n    expect(isProtected(t1)).toBe(false)\n    expect(isProtected(store)).toBe(false)\n    expect(store.todos[0].title).toBe(\"world\")\n})\ntest(\"action cannot modify parent\", () => {\n    const Child = types\n        .model(\"Child\", {\n            x: 2\n        })\n        .actions(self => ({\n            setParentX() {\n                getParent<typeof self>(self).x += 1\n            }\n        }))\n    const Parent = types.model(\"Parent\", {\n        x: 3,\n        child: Child\n    })\n    const p = Parent.create({ child: {} })\n    expect(() => p.child.setParentX()).toThrow(\n        \"[mobx-state-tree] Cannot modify 'Parent@<root>', the object is protected and can only be modified by using an action.\"\n    )\n})\n"
  },
  {
    "path": "__tests__/core/recordPatches.test.ts",
    "content": "import {\n    getSnapshot,\n    unprotect,\n    recordPatches,\n    types,\n    IType,\n    IJsonPatch,\n    Instance,\n    cast,\n    IAnyModelType,\n    IMSTMap\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nfunction testPatches<C, S, T extends object>(\n    type: IType<C, S, T>,\n    snapshot: C,\n    fn: any,\n    expectedPatches: IJsonPatch[],\n    expectedInversePatches: IJsonPatch[]\n) {\n    const instance = type.create(snapshot)\n    const baseSnapshot = getSnapshot(instance)\n    const recorder = recordPatches(instance)\n    unprotect(instance)\n    fn(instance)\n    recorder.stop()\n    expect(recorder.patches).toEqual(expectedPatches)\n    expect(recorder.inversePatches).toEqual(expectedInversePatches)\n    const clone = type.create(snapshot)\n    recorder.replay(clone)\n    expect(getSnapshot(clone)).toEqual(getSnapshot(instance))\n    recorder.undo()\n    expect(getSnapshot(instance)).toEqual(baseSnapshot)\n}\nconst Node = types.model(\"Node\", {\n    id: types.identifierNumber,\n    text: \"Hi\",\n    children: types.optional(types.array(types.late((): IAnyModelType => Node)), [])\n})\n\ntest(\"it should apply simple patch\", () => {\n    testPatches(\n        Node,\n        { id: 1 },\n        (n: Instance<typeof Node>) => {\n            n.text = \"test\"\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/text\",\n                value: \"test\"\n            }\n        ],\n        [\n            {\n                op: \"replace\",\n                path: \"/text\",\n                value: \"Hi\"\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to arrays\", () => {\n    testPatches(\n        Node,\n        { id: 1, children: [{ id: 2 }] },\n        (n: Instance<typeof Node>) => {\n            const children = n.children as unknown as Instance<typeof Node>[]\n            children[0].text = \"test\" // update\n            children[0] = cast({ id: 2, text: \"world\" }) // this reconciles; just an update\n            children[0] = cast({ id: 4, text: \"coffee\" }) // new object\n            children[1] = cast({ id: 3, text: \"world\" }) // addition\n            children.splice(0, 1) // removal\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"world\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0\",\n                value: {\n                    id: 4,\n                    text: \"coffee\",\n                    children: []\n                }\n            },\n            {\n                op: \"add\",\n                path: \"/children/1\",\n                value: {\n                    id: 3,\n                    text: \"world\",\n                    children: []\n                }\n            },\n            {\n                op: \"remove\",\n                path: \"/children/0\"\n            }\n        ],\n        [\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"Hi\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/0\",\n                value: {\n                    children: [],\n                    id: 2,\n                    text: \"world\"\n                }\n            },\n            {\n                op: \"remove\",\n                path: \"/children/1\"\n            },\n            {\n                op: \"add\",\n                path: \"/children/0\",\n                value: {\n                    children: [],\n                    id: 4,\n                    text: \"coffee\"\n                }\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to maps\", () => {\n    const NodeMap = types.model(\"NodeMap\", {\n        id: types.identifierNumber,\n        text: \"Hi\",\n        children: types.optional(types.map(types.late((): IAnyModelType => NodeMap)), {})\n    })\n    testPatches(\n        NodeMap,\n        { id: 1, children: { 2: { id: 2 } } },\n        (n: Instance<typeof NodeMap>) => {\n            const children = n.children as IMSTMap<typeof NodeMap>\n            children.get(\"2\")!.text = \"test\" // update\n            children.put({ id: 2, text: \"world\" }) // this reconciles; just an update\n            children.set(\n                \"4\",\n                NodeMap.create({ id: 4, text: \"coffee\", children: { 23: { id: 23 } } })\n            ) // new object\n            children.put({ id: 3, text: \"world\", children: { 7: { id: 7 } } }) // addition\n            children.delete(\"2\") // removal\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/children/2/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/2/text\",\n                value: \"world\"\n            },\n            {\n                op: \"add\",\n                path: \"/children/4\",\n                value: {\n                    children: {\n                        23: {\n                            children: {},\n                            id: 23,\n                            text: \"Hi\"\n                        }\n                    },\n                    id: 4,\n                    text: \"coffee\"\n                }\n            },\n            {\n                op: \"add\",\n                path: \"/children/3\",\n                value: {\n                    children: {\n                        7: {\n                            children: {},\n                            id: 7,\n                            text: \"Hi\"\n                        }\n                    },\n                    id: 3,\n                    text: \"world\"\n                }\n            },\n            {\n                op: \"remove\",\n                path: \"/children/2\"\n            }\n        ],\n        [\n            {\n                op: \"replace\",\n                path: \"/children/2/text\",\n                value: \"Hi\"\n            },\n            {\n                op: \"replace\",\n                path: \"/children/2/text\",\n                value: \"test\"\n            },\n            {\n                op: \"remove\",\n                path: \"/children/4\"\n            },\n            {\n                op: \"remove\",\n                path: \"/children/3\"\n            },\n            {\n                op: \"add\",\n                path: \"/children/2\",\n                value: {\n                    children: {},\n                    id: 2,\n                    text: \"world\"\n                }\n            }\n        ]\n    )\n})\n\ntest(\"it should apply deep patches to objects\", () => {\n    const NodeObject = types.model(\"NodeObject\", {\n        id: types.identifierNumber,\n        text: \"Hi\",\n        child: types.maybe(types.late((): IAnyModelType => NodeObject))\n    })\n    testPatches(\n        NodeObject,\n        { id: 1, child: { id: 2 } },\n        (n: Instance<typeof NodeObject>) => {\n            n.child!.text = \"test\" // update\n            n.child = cast({ id: 2, text: \"world\" }) // this reconciles; just an update\n            n.child = NodeObject.create({ id: 2, text: \"coffee\", child: { id: 23 } })\n            n.child = cast({ id: 3, text: \"world\", child: { id: 7 } }) // addition\n            n.child = undefined // removal\n        },\n        [\n            {\n                op: \"replace\",\n                path: \"/child/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/child/text\",\n                value: \"world\"\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: {\n                        child: undefined,\n                        id: 23,\n                        text: \"Hi\"\n                    },\n                    id: 2,\n                    text: \"coffee\"\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: {\n                        child: undefined,\n                        id: 7,\n                        text: \"Hi\"\n                    },\n                    id: 3,\n                    text: \"world\"\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: undefined\n            }\n        ],\n        [\n            {\n                op: \"replace\",\n                path: \"/child/text\",\n                value: \"Hi\"\n            },\n            {\n                op: \"replace\",\n                path: \"/child/text\",\n                value: \"test\"\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: undefined,\n                    id: 2,\n                    text: \"world\"\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: {\n                        child: undefined,\n                        id: 23,\n                        text: \"Hi\"\n                    },\n                    id: 2,\n                    text: \"coffee\"\n                }\n            },\n            {\n                op: \"replace\",\n                path: \"/child\",\n                value: {\n                    child: {\n                        child: undefined,\n                        id: 7,\n                        text: \"Hi\"\n                    },\n                    id: 3,\n                    text: \"world\"\n                }\n            }\n        ]\n    )\n})\n"
  },
  {
    "path": "__tests__/core/reference-custom.test.ts",
    "content": "import { reaction, when, values } from \"mobx\"\nimport {\n    types,\n    recordPatches,\n    getSnapshot,\n    applySnapshot,\n    applyPatch,\n    unprotect,\n    getRoot,\n    onSnapshot,\n    flow,\n    Instance,\n    resolveIdentifier\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"it should support custom references - basics\", () => {\n    const User = types.model({\n        id: types.identifier,\n        name: types.string\n    })\n    const UserByNameReference = types.maybeNull(\n        types.reference(User, {\n            // given an identifier, find the user\n            get(identifier, parent): any {\n                return (\n                    (parent as Instance<typeof Store>)!.users.find(u => u.name === identifier) ||\n                    null\n                )\n            },\n            // given a user, produce the identifier that should be stored\n            set(value) {\n                return value.name\n            }\n        })\n    )\n    const Store = types.model({\n        users: types.array(User),\n        selection: UserByNameReference\n    })\n    const s = Store.create({\n        users: [\n            { id: \"1\", name: \"Michel\" },\n            { id: \"2\", name: \"Mattia\" }\n        ],\n        selection: \"Mattia\"\n    })\n    unprotect(s)\n    expect(s.selection!.name).toBe(\"Mattia\")\n    expect(s.selection === s.users[1]).toBe(true)\n    expect(getSnapshot(s).selection).toBe(\"Mattia\")\n    s.selection = s.users[0]\n    expect(s.selection!.name).toBe(\"Michel\")\n    expect(s.selection === s.users[0]).toBe(true)\n    expect(getSnapshot(s).selection).toBe(\"Michel\")\n    s.selection = null\n    expect(getSnapshot(s).selection).toBe(null)\n    applySnapshot(s, Object.assign({}, getSnapshot(s), { selection: \"Mattia\" }))\n    // @ts-expect-error - typescript doesn't know that applySnapshot will update the selection\n    expect(s.selection).toBe(s.users[1])\n    applySnapshot(s, Object.assign({}, getSnapshot(s), { selection: \"Unknown\" }))\n    expect(s.selection).toBe(null)\n})\n\ntest(\"it should support custom references - adv\", () => {\n    const User = types.model({\n        id: types.identifier,\n        name: types.string\n    })\n    const NameReference = types.reference(User, {\n        get(identifier, parent): any {\n            if (identifier === null) return null\n            const users = values(getRoot<Instance<typeof Store>>(parent!).users)\n            return users.filter(u => u.name === identifier)[0] || null\n        },\n        set(value) {\n            return value ? value.name : \"\"\n        }\n    })\n    const Store = types.model({\n        users: types.map(User),\n        selection: NameReference\n    })\n    const s = Store.create({\n        users: {\n            \"1\": { id: \"1\", name: \"Michel\" },\n            \"2\": { id: \"2\", name: \"Mattia\" }\n        },\n        selection: \"Mattia\"\n    })\n    unprotect(s)\n    expect(s.selection.name).toBe(\"Mattia\")\n    expect(s.selection === s.users.get(\"2\")).toBe(true)\n    expect(getSnapshot<typeof Store.SnapshotType>(s).selection).toBe(\"Mattia\")\n    const p = recordPatches(s)\n    const r: any[] = []\n    onSnapshot(s, r.push.bind(r))\n    const ids: (string | null)[] = []\n    reaction(\n        () => s.selection,\n        selection => {\n            ids.push(selection ? selection.id : null)\n        }\n    )\n    s.selection = s.users.get(\"1\")!\n    expect(s.selection.name).toBe(\"Michel\")\n    expect(s.selection === s.users.get(\"1\")).toBe(true)\n    expect(getSnapshot(s).selection).toBe(\"Michel\")\n    applySnapshot(s, Object.assign({}, getSnapshot(s), { selection: \"Mattia\" }))\n    // @ts-expect-error - typescript doesn't know that applySnapshot will update the selection\n    expect(s.selection).toBe(s.users.get(\"2\"))\n    applyPatch(s, { op: \"replace\", path: \"/selection\", value: \"Michel\" })\n    // @ts-expect-error - typescript doesn't know that applyPatch will update the selection\n    expect(s.selection).toBe(s.users.get(\"1\"))\n    s.users.delete(\"1\")\n    // @ts-expect-error - typescript doesn't know how delete will affect the selection\n    expect(s.selection).toBe(null)\n    s.users.put({ id: \"3\", name: \"Michel\" })\n    expect(s.selection.id).toBe(\"3\")\n    expect(ids).toMatchSnapshot()\n    expect(r).toMatchSnapshot()\n    expect(p.patches).toMatchSnapshot()\n    expect(p.inversePatches).toMatchSnapshot()\n})\n\ntest(\"it should support dynamic loading\", async () => {\n    const events: string[] = []\n    const User = types.model({\n        name: types.string,\n        age: 0\n    })\n    const UserByNameReference = types.maybe(\n        types.reference(User, {\n            get(identifier: string, parent): any {\n                return (parent as Instance<typeof Store>).getOrLoadUser(identifier)\n            },\n            set(value) {\n                return value.name\n            }\n        })\n    )\n    const Store = types\n        .model({\n            users: types.array(User),\n            selection: UserByNameReference\n        })\n        .actions(self => ({\n            loadUser: flow(function* loadUser(name: string) {\n                events.push(\"loading \" + name)\n                self.users.push({ name })\n                yield new Promise(resolve => {\n                    setTimeout(resolve, 200)\n                })\n                events.push(\"loaded \" + name)\n                const user = (self.users.find(u => u.name === name)!.age = name.length * 3) // wonderful!\n            })\n        }))\n        .views(self => ({\n            // Important: a view so that the reference will automatically react to the reference being changed!\n            getOrLoadUser(name: string) {\n                const user = self.users.find(u => u.name === name) || null\n                if (!user) {\n                    /*\n                    TODO: this is ugly, but workaround the idea that views should be side effect free.\n                    We need a more elegant solution..\n                */\n                    setImmediate(() => self.loadUser(name))\n                }\n                return user\n            }\n        }))\n    const s = Store.create({\n        users: [],\n        selection: \"Mattia\"\n    })\n    unprotect(s)\n    expect(events).toEqual([])\n    expect(s.users.length).toBe(0)\n    // @ts-expect-error - typescript doesn't know that the user will be loaded\n    expect(s.selection).toBe(null)\n\n    await when(() => s.users.length === 1 && s.users[0].age === 18 && s.users[0].name === \"Mattia\")\n\n    expect(s.selection).toBe(s.users[0])\n    expect(events).toEqual([\"loading Mattia\", \"loaded Mattia\"])\n})\n\ntest(\"custom reference / safe custom reference to another store works\", () => {\n    const Todo = types.model({ id: types.identifier })\n    const TodoStore = types.model({ todos: types.array(Todo) })\n    const OtherStore = types.model({\n        todoRef: types.maybe(\n            types.reference(Todo, {\n                get(id) {\n                    const node = resolveIdentifier(Todo, todos, id)\n                    if (!node) {\n                        throw new Error(\"Invalid ref\")\n                    }\n                    return node\n                },\n                set(value) {\n                    return value.id\n                }\n            })\n        ),\n        safeRef: types.safeReference(Todo, {\n            get(id) {\n                const node = resolveIdentifier(Todo, todos, id)\n                if (!node) {\n                    throw new Error(\"Invalid ref\")\n                }\n                return node\n            },\n            set(value) {\n                return value.id\n            }\n        })\n    })\n    const todos = TodoStore.create({\n        todos: [{ id: \"1\" }, { id: \"2\" }, { id: \"3\" }]\n    })\n    unprotect(todos)\n\n    // from a snapshot\n    const otherStore = OtherStore.create({\n        todoRef: \"1\",\n        safeRef: \"1\"\n    })\n    unprotect(otherStore)\n    expect(otherStore.todoRef!.id).toBe(\"1\")\n    expect(otherStore.safeRef!.id).toBe(\"1\")\n\n    // assigning an id\n    otherStore.todoRef = \"2\" as any\n    otherStore.safeRef = \"2\" as any\n    expect(otherStore.todoRef!.id).toBe(\"2\")\n    expect(otherStore.safeRef!.id).toBe(\"2\")\n\n    // assigning a node directly\n    otherStore.todoRef = todos.todos[2]\n    otherStore.safeRef = todos.todos[2]\n    expect(otherStore.todoRef!.id).toBe(\"3\")\n    expect(otherStore.safeRef!.id).toBe(\"3\")\n\n    // getting the snapshot\n    expect(getSnapshot(otherStore)).toEqual({\n        todoRef: \"3\",\n        safeRef: \"3\"\n    })\n\n    // the removed node should throw on standard refs access\n    // and be set to undefined on safe ones\n    todos.todos.splice(2, 1)\n    expect(() => otherStore.todoRef).toThrow(\"Invalid ref\")\n    expect(otherStore.safeRef).toBeUndefined()\n})\n"
  },
  {
    "path": "__tests__/core/reference-onInvalidated.test.ts",
    "content": "import {\n    types,\n    OnReferenceInvalidated,\n    Instance,\n    ReferenceIdentifier,\n    IAnyStateTreeNode,\n    unprotect,\n    OnReferenceInvalidatedEvent,\n    getSnapshot,\n    applySnapshot,\n    clone,\n    destroy\n} from \"../../src\"\nimport { describe, expect, it, test } from \"bun:test\"\n\nconst Todo = types.model({ id: types.identifier })\n\nconst createSnapshot = (partialSnapshot: any) => ({\n    todos: [{ id: \"1\" }, { id: \"2\" }, { id: \"3\" }, { id: \"4\" }],\n    ...partialSnapshot\n})\n\nconst createStore = (\n    partialSnapshot: any,\n    onInvalidated?: OnReferenceInvalidated<Instance<typeof Todo>>,\n    customRef = false\n) => {\n    const refOptions = {\n        onInvalidated,\n        get(identifier: ReferenceIdentifier, parent: IAnyStateTreeNode | null) {\n            return (parent as Instance<typeof Store>).todos.find(t => t.id === identifier)\n        },\n        set(value: Instance<typeof Todo>): ReferenceIdentifier {\n            return value.id\n        }\n    }\n\n    if (!customRef) {\n        // @ts-ignore\n        delete refOptions.get\n        // @ts-ignore\n        delete refOptions.set\n    }\n\n    const Store = types.model({\n        todos: types.array(Todo),\n        onInv: types.maybe(types.reference(Todo, refOptions as any)),\n        single: types.safeReference(Todo),\n        deep: types.optional(\n            types.model({\n                single: types.safeReference(Todo)\n            }),\n            {}\n        ),\n        arr: types.array(types.safeReference(Todo)),\n        map: types.map(types.safeReference(Todo))\n    })\n\n    const s = Store.create(createSnapshot(partialSnapshot))\n    unprotect(s)\n    return s\n}\n\nfor (const customRef of [false, true]) {\n    describe(`onInvalidated - customRef: ${customRef}`, () => {\n        test(\"from snapshot without accessing the referenced node\", () => {\n            let ev: OnReferenceInvalidatedEvent<Instance<typeof Todo>> | undefined\n            let oldRefId!: string\n            let calls = 0\n            const onInv: OnReferenceInvalidated<Instance<typeof Todo>> = ev1 => {\n                calls++\n                oldRefId = ev1.invalidTarget!.id\n                expect(ev1.invalidId).toBe(oldRefId)\n                ev = ev1\n                ev1.removeRef()\n            }\n            const store = createStore({ onInv: \"1\" }, onInv)\n\n            expect(calls).toBe(0)\n            store.todos.splice(0, 1)\n            expect(calls).toBe(1)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"1\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n\n            store.onInv = store.todos[0]\n            expect(calls).toBe(1)\n            store.todos.splice(0, 1)\n            expect(calls).toBe(2)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"2\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n        })\n\n        test(\"applying snapshot without accesing the referenced node\", () => {\n            let ev: OnReferenceInvalidatedEvent<Instance<typeof Todo>> | undefined\n            let oldRefId!: string\n            let calls = 0\n            const onInv: OnReferenceInvalidated<Instance<typeof Todo>> = ev1 => {\n                calls++\n                oldRefId = ev1.invalidTarget!.id\n                expect(ev1.invalidId).toBe(oldRefId)\n                ev = ev1\n                ev1.removeRef()\n            }\n            const store = createStore({}, onInv)\n            expect(calls).toBe(0)\n            applySnapshot(store, createSnapshot({ onInv: \"1\" }))\n            expect(calls).toBe(0)\n            store.todos.splice(0, 1)\n            expect(calls).toBe(1)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"1\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n\n            store.onInv = store.todos[0]\n            expect(calls).toBe(1)\n            store.todos.splice(0, 1)\n            expect(calls).toBe(2)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"2\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n        })\n\n        test(\"runtime change\", () => {\n            let ev: OnReferenceInvalidatedEvent<Instance<typeof Todo>> | undefined\n            let oldRefId!: string\n            let calls = 0\n            const onInv: OnReferenceInvalidated<Instance<typeof Todo>> = ev1 => {\n                calls++\n                oldRefId = ev1.invalidTarget!.id\n                expect(ev1.invalidId).toBe(oldRefId)\n                ev = ev1\n                ev1.removeRef()\n            }\n            const store = createStore({}, onInv)\n\n            expect(calls).toBe(0)\n            store.onInv = store.todos[1]\n            expect(calls).toBe(0)\n            store.onInv = store.todos[0]\n            expect(calls).toBe(0)\n            store.todos.remove(store.todos[0])\n            expect(calls).toBe(1)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"1\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n\n            store.onInv = store.todos[0]\n            expect(calls).toBe(1)\n            store.todos.remove(store.todos[0])\n            expect(calls).toBe(2)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"2\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n        })\n\n        test(\"replacing ref\", () => {\n            let ev: OnReferenceInvalidatedEvent<Instance<typeof Todo>> | undefined\n            let oldRefId!: string\n            let calls = 0\n            const onInv: OnReferenceInvalidated<Instance<typeof Todo>> = ev1 => {\n                calls++\n                oldRefId = ev1.invalidTarget!.id\n                expect(ev1.invalidId).toBe(oldRefId)\n                ev = ev1\n                ev1.replaceRef(store.todos[1])\n            }\n            const store = createStore({}, onInv)\n\n            expect(calls).toBe(0)\n            store.onInv = store.todos[0]\n            expect(calls).toBe(0)\n            store.todos.remove(store.todos[0])\n            expect(calls).toBe(1)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"1\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv!.id).toBe(\"2\")\n            expect(getSnapshot(store).onInv).toBe(\"2\")\n        })\n\n        test(\"cloning works\", () => {\n            let ev: OnReferenceInvalidatedEvent<Instance<typeof Todo>> | undefined\n            let oldRefId!: string\n            let calls = 0\n            const onInv: OnReferenceInvalidated<Instance<typeof Todo>> = ev1 => {\n                calls++\n                oldRefId = ev1.invalidTarget!.id\n                expect(ev1.invalidId).toBe(oldRefId)\n                ev = ev1\n                ev1.removeRef()\n            }\n            const store1 = createStore({}, onInv)\n\n            expect(calls).toBe(0)\n            store1.onInv = store1.todos[0]\n            expect(calls).toBe(0)\n\n            const store = clone(store1)\n            unprotect(store)\n            expect(calls).toBe(0)\n            store.onInv = store.todos[0]\n            expect(calls).toBe(0)\n            store.todos.remove(store.todos[0])\n            expect(calls).toBe(1)\n            expect(ev!.parent).toBe(store)\n            expect(oldRefId).toBe(\"1\")\n            expect(ev!.removeRef).toBeTruthy()\n            expect(ev!.replaceRef).toBeTruthy()\n            expect(store.onInv).toBeUndefined()\n            expect(getSnapshot(store).onInv).toBeUndefined()\n            // make sure other ref stil points to the right one\n            expect(store1.onInv).toBe(store1.todos[0])\n        })\n    })\n}\n\ndescribe(\"safeReference\", () => {\n    test(\"model property\", () => {\n        const store = createStore({})\n        expect(store.single).toBeUndefined()\n        store.single = store.todos[0]\n        expect(store.single).toBe(store.todos[0])\n        store.todos.remove(store.todos[0])\n        expect(store.single).toBeUndefined()\n    })\n\n    test(\"deep model property\", () => {\n        const store = createStore({})\n        expect(store.deep.single).toBeUndefined()\n        store.deep.single = store.todos[0]\n        expect(store.deep.single).toBe(store.todos[0])\n        store.todos.remove(store.todos[0])\n        expect(store.deep.single).toBeUndefined()\n    })\n\n    test(\"array child\", () => {\n        const store = createStore({})\n        expect(store.arr.length).toBe(0)\n\n        store.arr.push(store.todos[0])\n        store.arr.push(store.todos[2])\n        expect(store.arr.length).toBe(2)\n        expect(store.arr[0]!.id).toBe(\"1\")\n        expect(store.arr[1]!.id).toBe(\"3\")\n\n        store.todos.splice(0, 1)\n        expect(store.arr.length).toBe(1)\n        expect(store.arr[0]!.id).toBe(\"3\")\n    })\n\n    test(\"map child\", () => {\n        const store = createStore({})\n        expect(store.map.size).toBe(0)\n\n        store.map.set(\"a\", store.todos[0])\n        store.map.set(\"c\", store.todos[2])\n        expect(store.map.size).toBe(2)\n        expect(store.map.get(\"a\")!.id).toBe(\"1\")\n        expect(store.map.get(\"c\")!.id).toBe(\"3\")\n\n        store.todos.splice(0, 1)\n        expect(store.map.size).toBe(1)\n        expect(store.map.get(\"c\")!.id).toBe(\"3\")\n    })\n\n    test(\"invalid references in a snapshot should be removed\", () => {\n        const store = createStore({ single: \"100\", arr: [\"100\", \"1\"], map: { a: \"100\", b: \"1\" } })\n        expect(store.single).toBeUndefined()\n        expect(store.arr.length).toBe(1)\n        expect(store.arr[0]!.id).toBe(\"1\")\n        expect(store.map.size).toBe(1)\n        expect(store.map.get(\"b\")!.id).toBe(\"1\")\n\n        // check reassignation still works\n        store.single = store.todos[0]\n        expect(store.single).toBe(store.todos[0])\n        store.todos.remove(store.todos[0])\n        expect(store.single).toBeUndefined()\n    })\n\n    test(\"setting it to an invalid id and then accessing it should still result in an error\", () => {\n        const store = createStore({})\n        store.single = \"100\" as any\n        expect(() => {\n            const s = store.single\n        }).toThrow(\"Failed to resolve reference\")\n    })\n})\n\ntest(\"#1115 - safe reference doesn't become invalidated when the reference has never been acessed\", () => {\n    const MyRefModel = types.model(\"MyRefModel\", {\n        id: types.identifier\n    })\n\n    const SafeRef = types.model(\"SafeRef\", {\n        ref: types.safeReference(MyRefModel)\n    })\n\n    const RootModel = types\n        .model(\"RootModel\", {\n            mapOfRef: types.map(MyRefModel),\n            arrayOfSafeRef: types.array(SafeRef)\n        })\n        .actions(self => ({\n            deleteSqr(id: string) {\n                self.mapOfRef.delete(id)\n            }\n        }))\n\n    const rootModel = RootModel.create({\n        mapOfRef: {\n            sqr1: {\n                id: \"sqr1\"\n            },\n            sqr2: {\n                id: \"sqr2\"\n            }\n        },\n        arrayOfSafeRef: [\n            {\n                ref: \"sqr2\"\n            },\n            {\n                ref: \"sqr1\"\n            },\n            {\n                ref: \"sqr2\"\n            }\n        ]\n    })\n\n    expect(getSnapshot(rootModel.arrayOfSafeRef)).toEqual([\n        {\n            ref: \"sqr2\"\n        },\n        {\n            ref: \"sqr1\"\n        },\n        {\n            ref: \"sqr2\"\n        }\n    ])\n\n    rootModel.deleteSqr(\"sqr1\")\n    expect(getSnapshot(rootModel.arrayOfSafeRef)).toEqual([\n        {\n            ref: \"sqr2\"\n        },\n        {\n            ref: undefined\n        },\n        {\n            ref: \"sqr2\"\n        }\n    ])\n\n    rootModel.deleteSqr(\"sqr2\")\n    expect(getSnapshot(rootModel.arrayOfSafeRef)).toEqual([\n        {\n            ref: undefined\n        },\n        {\n            ref: undefined\n        },\n        {\n            ref: undefined\n        }\n    ])\n})\n\ndescribe(\"safeReference with acceptsUndefined: false\", () => {\n    const MyRefModel = types.model(\"MyRefModel\", {\n        id: types.identifier\n    })\n\n    const SafeRef = types.safeReference(MyRefModel, { acceptsUndefined: false })\n\n    it(\"removes invalidates items from map/array\", () => {\n        const Store = types.model({\n            todos: types.array(MyRefModel),\n            arr: types.array(SafeRef),\n            map: types.map(SafeRef)\n        })\n\n        const store = Store.create({\n            todos: [{ id: \"1\" }, { id: \"2\" }],\n            arr: [\"1\", \"2\"],\n            map: {\n                a1: \"1\",\n                a2: \"2\"\n            }\n        })\n        unprotect(store)\n\n        // just to check TS is happy with this\n        const arr: Instance<typeof MyRefModel>[] = store.arr\n\n        store.todos.splice(0, 1)\n        expect(store.arr.length).toBe(1)\n        expect(store.map.size).toBe(1)\n    })\n\n    if (process.env.NODE_ENV !== \"production\") {\n        it(\"throws when a model property is invalidated\", () => {\n            const Store = types.model({\n                todos: types.array(MyRefModel),\n                single: SafeRef\n            })\n\n            const store = Store.create({\n                todos: [{ id: \"1\" }, { id: \"2\" }],\n                single: \"1\"\n            })\n            unprotect(store)\n\n            expect(() => {\n                store.todos.splice(0, 1)\n            }).toThrow(\"value `undefined` is not assignable to type\")\n        })\n\n        it(\"does not accept undefined in the array\", () => {\n            const Store = types.model({\n                todos: types.array(MyRefModel),\n                arr: types.array(SafeRef)\n            })\n\n            expect(() =>\n                Store.create({\n                    todos: [{ id: \"1\" }, { id: \"2\" }],\n                    arr: [\"1\", undefined as any]\n                })\n            ).toThrow(\"value `undefined` is not assignable to type\")\n        })\n\n        it(\"does not accept undefined in the map\", () => {\n            const Store = types.model({\n                todos: types.array(MyRefModel),\n                map: types.map(SafeRef)\n            })\n\n            expect(() =>\n                Store.create({\n                    todos: [{ id: \"1\" }, { id: \"2\" }],\n                    map: {\n                        a1: \"1\",\n                        a2: undefined as any\n                    }\n                })\n            ).toThrow(\"value `undefined` is not assignable to type\")\n        })\n    }\n})\n\ntest(\"#1275 - removing an object from a map should result in the snapshot of references being modified\", () => {\n    const Item = types.model({\n        id: types.identifier\n    })\n\n    const Root = types.model({\n        items: types.map(Item),\n        refs: types.array(types.safeReference(Item))\n    })\n\n    const thing = Root.create({\n        items: { aa: { id: \"a\" }, bb: { id: \"b\" }, cc: { id: \"c\" } },\n        refs: [\"a\", \"b\", \"c\"]\n    })\n    unprotect(thing)\n\n    destroy(thing.items.get(\"bb\")!)\n    expect(getSnapshot(thing.refs)).toEqual([\"a\", \"c\"])\n})\n"
  },
  {
    "path": "__tests__/core/reference.test.ts",
    "content": "import { reaction, autorun, isObservable, configure } from \"mobx\"\nimport {\n    types,\n    getSnapshot,\n    applySnapshot,\n    onPatch,\n    applyPatch,\n    unprotect,\n    detach,\n    resolveIdentifier,\n    getRoot,\n    cast,\n    SnapshotOut,\n    IAnyModelType,\n    Instance,\n    SnapshotOrInstance,\n    isAlive,\n    destroy,\n    castToReferenceSnapshot,\n    tryReference,\n    isValidReference,\n    isStateTreeNode,\n    addDisposer\n} from \"../../src\"\nimport { expect, jest, test } from \"bun:test\"\n\ntest(\"it should support prefixed paths in maps\", () => {\n    const User = types.model({\n        id: types.identifier,\n        name: types.string\n    })\n    const UserStore = types.model({\n        user: types.reference(User),\n        users: types.map(User)\n    })\n    const store = UserStore.create({\n        user: \"17\",\n        users: {\n            \"17\": { id: \"17\", name: \"Michel\" },\n            \"18\": { id: \"18\", name: \"Veria\" }\n        }\n    })\n    unprotect(store)\n    expect(store.users.get(\"17\")!.name).toBe(\"Michel\")\n    expect(store.users.get(\"18\")!.name).toBe(\"Veria\")\n    expect(store.user.name).toBe(\"Michel\")\n    store.user = store.users.get(\"18\")!\n    expect(store.user.name).toBe(\"Veria\")\n    store.users.get(\"18\")!.name = \"Noa\"\n    expect(store.user.name).toBe(\"Noa\")\n    expect(getSnapshot(store)).toEqual({\n        user: \"18\",\n        users: { \"17\": { id: \"17\", name: \"Michel\" }, \"18\": { id: \"18\", name: \"Noa\" } }\n    } as SnapshotOut<typeof store>)\n})\n\ntest(\"it should support prefixed paths in arrays\", () => {\n    const User = types.model({\n        id: types.identifier,\n        name: types.string\n    })\n    const UserStore = types.model({\n        user: types.reference(User),\n        users: types.array(User)\n    })\n    const store = UserStore.create({\n        user: \"17\",\n        users: [\n            { id: \"17\", name: \"Michel\" },\n            { id: \"18\", name: \"Veria\" }\n        ]\n    })\n    unprotect(store)\n    expect(store.users[0].name).toBe(\"Michel\")\n    expect(store.users[1].name).toBe(\"Veria\")\n    expect(store.user.name).toBe(\"Michel\")\n    store.user = store.users[1]\n    expect(store.user.name).toBe(\"Veria\")\n    store.users[1].name = \"Noa\"\n    expect(store.user.name).toBe(\"Noa\")\n    expect(getSnapshot(store)).toEqual({\n        user: \"18\",\n        users: [\n            { id: \"17\", name: \"Michel\" },\n            { id: \"18\", name: \"Noa\" }\n        ]\n    } as SnapshotOut<typeof store>)\n})\n\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"identifiers are required\", () => {\n        const Todo = types.model({\n            id: types.identifier\n        })\n        expect(Todo.is({})).toBe(false)\n        expect(Todo.is({ id: \"x\" })).toBe(true)\n        expect(() => (Todo.create as any)()).toThrow(\n            \" `undefined` is not assignable to type: `identifier` (Value is not a valid identifier, expected a string)\"\n        )\n    })\n\n    test(\"identifiers cannot be modified\", () => {\n        const Todo = types.model({\n            id: types.identifier\n        })\n        const todo = Todo.create({ id: \"x\" })\n        unprotect(todo)\n        expect(() => (todo.id = \"stuff\")).toThrow(\n            \"[mobx-state-tree] Tried to change identifier from 'x' to 'stuff'. Changing identifiers is not allowed.\"\n        )\n        expect(() => applySnapshot(todo, { id: \"stuff\" })).toThrow(\n            \"[mobx-state-tree] Tried to change identifier from 'x' to 'stuff'. Changing identifiers is not allowed.\"\n        )\n    })\n}\n\ntest(\"it should resolve refs during creation, when using path\", () => {\n    const values: number[] = []\n    const Book = types.model({\n        id: types.identifier,\n        price: types.number\n    })\n    const BookEntry = types\n        .model({\n            book: types.reference(Book)\n        })\n        .views(self => ({\n            get price() {\n                return self.book.price * 2\n            }\n        }))\n    const Store = types.model({\n        books: types.array(Book),\n        entries: types.optional(types.array(BookEntry), [])\n    })\n    const s = Store.create({\n        books: [{ id: \"3\", price: 2 }]\n    })\n    unprotect(s)\n    reaction(\n        () => s.entries.reduce((a, e) => a + e.price, 0),\n        v => values.push(v)\n    )\n    s.entries.push({ book: castToReferenceSnapshot(s.books[0]) })\n    expect(s.entries[0].price).toBe(4)\n    expect(s.entries.reduce((a, e) => a + e.price, 0)).toBe(4)\n    const entry = BookEntry.create({ book: castToReferenceSnapshot(s.books[0]) }) // N.B. ref is initially not resolvable!\n    s.entries.push(entry)\n    expect(s.entries[1].price).toBe(4)\n    expect(s.entries.reduce((a, e) => a + e.price, 0)).toBe(8)\n    expect(values).toEqual([4, 8])\n})\n\ntest(\"it should resolve refs over late types\", () => {\n    const Book = types.model({\n        id: types.identifier,\n        price: types.number\n    })\n    const BookEntry = types\n        .model({\n            book: types.reference(types.late(() => Book))\n        })\n        .views(self => ({\n            get price() {\n                return self.book.price * 2\n            }\n        }))\n    const Store = types.model({\n        books: types.array(Book),\n        entries: types.array(BookEntry)\n    })\n    const s = Store.create({\n        books: [{ id: \"3\", price: 2 }]\n    })\n    unprotect(s)\n    s.entries.push({ book: castToReferenceSnapshot(s.books[0]) })\n    expect(s.entries[0].price).toBe(4)\n    expect(s.entries.reduce((a, e) => a + e.price, 0)).toBe(4)\n})\n\ntest(\"it should resolve refs during creation, when using generic reference\", () => {\n    const values: number[] = []\n    const Book = types.model({\n        id: types.identifier,\n        price: types.number\n    })\n    const BookEntry = types\n        .model({\n            book: types.reference(Book)\n        })\n        .views(self => ({\n            get price() {\n                return self.book.price * 2\n            }\n        }))\n    const Store = types.model({\n        books: types.array(Book),\n        entries: types.optional(types.array(BookEntry), [])\n    })\n    const s = Store.create({\n        books: [{ id: \"3\", price: 2 }]\n    })\n    unprotect(s)\n    reaction(\n        () => s.entries.reduce((a, e) => a + e.price, 0),\n        v => values.push(v)\n    )\n    s.entries.push({ book: castToReferenceSnapshot(s.books[0]) })\n    expect(s.entries[0].price).toBe(4)\n    expect(s.entries.reduce((a, e) => a + e.price, 0)).toBe(4)\n    const entry = BookEntry.create({ book: castToReferenceSnapshot(s.books[0]) }) // can refer to book, even when not part of tree yet\n    expect(getSnapshot(entry)).toEqual({ book: \"3\" })\n    s.entries.push(entry)\n    expect(values).toEqual([4, 8])\n})\n\ntest(\"identifiers should support subtypes of types.string and types.number\", () => {\n    const M = types.model({\n        id: types.refinement(types.identifierNumber, n => n > 5)\n    })\n    expect(M.is({})).toBe(false)\n    expect(M.is({ id: \"test\" })).toBe(false)\n    expect(M.is({ id: \"6\" })).toBe(false)\n    expect(M.is({ id: \"4\" })).toBe(false)\n    expect(M.is({ id: 6 })).toBe(true)\n    expect(M.is({ id: 4 })).toBe(false)\n\n    const S = types.model({\n        mies: types.map(M),\n        ref: types.reference(M)\n    })\n    const s = S.create({ mies: { \"7\": { id: 7 } }, ref: \"7\" })\n    expect(s.mies.get(\"7\")).toBeTruthy()\n    expect(s.ref).toBe(s.mies.get(\"7\")!)\n})\n\ntest(\"string identifiers should not accept numbers\", () => {\n    const F = types.model({\n        id: types.identifier\n    })\n    expect(F.is({ id: \"4\" })).toBe(true)\n    expect(F.is({ id: 4 })).toBe(false)\n    const F2 = types.model({\n        id: types.identifier\n    })\n    expect(F2.is({ id: \"4\" })).toBe(true)\n    expect(F2.is({ id: 4 })).toBe(false)\n})\n\ntest(\"122 - identifiers should support numbers as well\", () => {\n    const F = types.model({\n        id: types.identifierNumber\n    })\n    expect(\n        F.create({\n            id: 3\n        }).id\n    ).toBe(3)\n\n    expect(F.is({ id: 4 })).toBe(true)\n    expect(F.is({ id: \"4\" })).toBe(false)\n    expect(F.is({ id: \"bla\" })).toBe(false)\n})\n\ntest(\"self reference with a late type\", () => {\n    const Book = types.model(\"Book\", {\n        id: types.identifier,\n        genre: types.string,\n        reference: types.reference(types.late((): IAnyModelType => Book))\n    })\n    const Store = types\n        .model(\"Store\", {\n            books: types.array(Book)\n        })\n        .actions(self => {\n            function addBook(book: SnapshotOrInstance<typeof Book>) {\n                self.books.push(book)\n            }\n            return {\n                addBook\n            }\n        })\n    const s = Store.create({\n        books: [{ id: \"1\", genre: \"thriller\", reference: \"\" }]\n    })\n    const book2 = Book.create({\n        id: \"2\",\n        genre: \"romance\",\n        reference: castToReferenceSnapshot(s.books[0])\n    })\n    s.addBook(book2)\n    expect((s.books[1].reference as Instance<typeof Book>).genre).toBe(\"thriller\")\n})\n\ntest(\"when applying a snapshot, reference should resolve correctly if value added after\", () => {\n    const Box = types.model({\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const Factory = types.model({\n        selected: types.reference(Box),\n        boxes: types.array(Box)\n    })\n    expect(() =>\n        Factory.create({\n            selected: 1,\n            boxes: [\n                { id: 1, name: \"hello\" },\n                { id: 2, name: \"world\" }\n            ]\n        })\n    ).not.toThrow()\n})\n\ntest(\"it should fail when reference snapshot is ambiguous\", () => {\n    const Box = types.model(\"Box\", {\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const Arrow = types.model(\"Arrow\", {\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const BoxOrArrow = types.union(Box, Arrow)\n    const Factory = types.model({\n        selected: types.reference(BoxOrArrow),\n        boxes: types.array(Box),\n        arrows: types.array(Arrow)\n    })\n    const store = Factory.create({\n        selected: 2,\n        boxes: [\n            { id: 1, name: \"hello\" },\n            { id: 2, name: \"world\" }\n        ],\n        arrows: [{ id: 2, name: \"arrow\" }]\n    })\n    expect(() => {\n        // tslint:disable-next-line:no-unused-expression\n        store.selected // store.boxes[1] // throws because it can't know if you mean a box or an arrow!\n    }).toThrow(\n        \"[mobx-state-tree] Cannot resolve a reference to type '(Box | Arrow)' with id: '2' unambigously, there are multiple candidates: /boxes/1, /arrows/0\"\n    )\n    unprotect(store)\n    // first update the reference, than create a new matching item! Ref becomes ambigous now...\n    store.selected = 1 as any // valid assignment\n    expect(store.selected).toBe(store.boxes[0]) // unambigous identifier\n    let err!: Error\n    autorun(() => store.selected, {\n        onError(e) {\n            err = e\n        }\n    })\n    expect(store.selected).toBe(store.boxes[0]) // unambigous identifier\n    store.arrows.push({ id: 1, name: \"oops\" })\n    expect(err.message).toBe(\n        \"[mobx-state-tree] Cannot resolve a reference to type '(Box | Arrow)' with id: '1' unambigously, there are multiple candidates: /boxes/0, /arrows/1\"\n    )\n})\n\ntest(\"it should support array of references\", () => {\n    const Box = types.model({\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const Factory = types.model({\n        selected: types.array(types.reference(Box)),\n        boxes: types.array(Box)\n    })\n    const store = Factory.create({\n        selected: [],\n        boxes: [\n            { id: 1, name: \"hello\" },\n            { id: 2, name: \"world\" }\n        ]\n    })\n    unprotect(store)\n    expect(() => {\n        store.selected.push(store.boxes[0])\n    }).not.toThrow()\n    expect(getSnapshot(store.selected)).toEqual([1])\n    expect(() => {\n        store.selected.push(store.boxes[1])\n    }).not.toThrow()\n    expect(getSnapshot(store.selected)).toEqual([1, 2])\n})\n\ntest(\"it should restore array of references from snapshot\", () => {\n    const Box = types.model({\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const Factory = types.model({\n        selected: types.array(types.reference(Box)),\n        boxes: types.array(Box)\n    })\n    const store = Factory.create({\n        selected: [1, 2],\n        boxes: [\n            { id: 1, name: \"hello\" },\n            { id: 2, name: \"world\" }\n        ]\n    })\n    unprotect(store)\n    expect(store.selected[0] === store.boxes[0]).toEqual(true)\n    expect(store.selected[1] === store.boxes[1]).toEqual(true)\n})\n\ntest(\"it should support map of references\", () => {\n    const Box = types.model({\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const Factory = types.model({\n        selected: types.map(types.reference(Box)),\n        boxes: types.array(Box)\n    })\n    const store = Factory.create({\n        selected: {},\n        boxes: [\n            { id: 1, name: \"hello\" },\n            { id: 2, name: \"world\" }\n        ]\n    })\n    unprotect(store)\n    expect(() => {\n        store.selected.set(\"from\", store.boxes[0])\n    }).not.toThrow()\n    expect(getSnapshot(store.selected)).toEqual({ from: 1 })\n    expect(() => {\n        store.selected.set(\"to\", store.boxes[1])\n    }).not.toThrow()\n    expect(getSnapshot(store.selected)).toEqual({ from: 1, to: 2 })\n})\n\ntest(\"it should restore map of references from snapshot\", () => {\n    const Box = types.model({\n        id: types.identifierNumber,\n        name: types.string\n    })\n    const Factory = types.model({\n        selected: types.map(types.reference(Box)),\n        boxes: types.array(Box)\n    })\n    const store = Factory.create({\n        selected: { from: 1, to: 2 },\n        boxes: [\n            { id: 1, name: \"hello\" },\n            { id: 2, name: \"world\" }\n        ]\n    })\n    unprotect(store)\n    expect(store.selected.get(\"from\") === store.boxes[0]).toEqual(true)\n    expect(store.selected.get(\"to\") === store.boxes[1]).toEqual(true)\n})\n\ntest(\"it should support relative lookups\", () => {\n    const Node = types.model({\n        id: types.identifierNumber,\n        children: types.optional(types.array(types.late((): IAnyModelType => Node)), [])\n    })\n    const root = Node.create({\n        id: 1,\n        children: [\n            {\n                id: 2,\n                children: [\n                    {\n                        id: 4\n                    }\n                ]\n            },\n            {\n                id: 3\n            }\n        ]\n    })\n    unprotect(root)\n    expect(getSnapshot(root)).toEqual({\n        id: 1,\n        children: [\n            { id: 2, children: [{ id: 4, children: [] }] },\n            { id: 3, children: [] }\n        ]\n    })\n    expect(resolveIdentifier(Node, root, 1)).toBe(root)\n    expect(resolveIdentifier(Node, root, 4)).toBe(root.children[0].children[0])\n    expect(resolveIdentifier(Node, root.children[0].children[0], 3)).toBe(root.children[1])\n    const n2 = detach(root.children[0])\n    unprotect(n2)\n    expect(resolveIdentifier(Node, n2, 2)).toBe(n2)\n    expect(resolveIdentifier(Node, root, 2)).toBeUndefined()\n    expect(resolveIdentifier(Node, root, 4)).toBeUndefined()\n    expect(resolveIdentifier(Node, n2, 3)).toBeUndefined()\n    expect(resolveIdentifier(Node, n2, 4)).toBe(n2.children[0])\n    expect(resolveIdentifier(Node, n2.children[0], 2)).toBe(n2)\n    const n5 = Node.create({ id: 5 })\n    expect(resolveIdentifier(Node, n5, 4)).toBeUndefined()\n    n2.children.push(n5)\n    expect(resolveIdentifier(Node, n5, 4)).toBe(n2.children[0])\n    expect(resolveIdentifier(Node, n2.children[0], 5)).toBe(n5)\n})\n\ntest(\"References are non-nullable by default\", () => {\n    const Todo = types.model({\n        id: types.identifierNumber\n    })\n    const Store = types.model({\n        todo: types.maybe(Todo),\n        ref: types.reference(Todo),\n        maybeRef: types.maybe(types.reference(Todo))\n    })\n    expect(Store.is({})).toBe(false)\n    expect(Store.is({ ref: 3 })).toBe(true)\n    expect(Store.is({ ref: null })).toBe(false)\n    expect(Store.is({ ref: undefined })).toBe(false)\n    expect(Store.is({ ref: 3, maybeRef: 3 })).toBe(true)\n    expect(Store.is({ ref: 3, maybeRef: undefined })).toBe(true)\n    let store = Store.create({\n        todo: { id: 3 },\n        ref: 3\n    })\n    expect(store.ref).toBe(store.todo!)\n    expect(store.maybeRef).toBeUndefined()\n    store = Store.create({\n        todo: { id: 3 },\n        ref: 4\n    })\n    unprotect(store)\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(store.maybeRef).toBeUndefined()\n        expect(() => store.ref).toThrow(\n            \"[mobx-state-tree] Failed to resolve reference '4' to type 'AnonymousModel' (from node: /ref)\"\n        )\n        store.maybeRef = 3 as any // valid assignment\n        expect(store.maybeRef).toBe(store.todo!)\n        store.maybeRef = 4 as any // valid assignment\n        expect(() => store.maybeRef).toThrow(\n            \"[mobx-state-tree] Failed to resolve reference '4' to type 'AnonymousModel' (from node: /maybeRef)\"\n        )\n        store.maybeRef = undefined\n        expect(store.maybeRef).toBe(undefined)\n        expect(() => ((store as any).ref = undefined)).toThrow(/Error while converting/)\n    }\n})\n\ntest(\"References are described properly\", () => {\n    const Todo = types.model({\n        id: types.identifierNumber\n    })\n    const Store = types.model({\n        todo: types.maybe(Todo),\n        ref: types.reference(Todo),\n        maybeRef: types.maybe(types.reference(Todo))\n    })\n    expect(Store.describe()).toBe(\n        \"{ todo: ({ id: identifierNumber } | undefined?); ref: reference(AnonymousModel); maybeRef: (reference(AnonymousModel) | undefined?) }\"\n    )\n})\n\ntest(\"References in recursive structures\", () => {\n    const Folder = types.model(\"Folder\", {\n        id: types.identifierNumber,\n        name: types.string,\n        files: types.array(types.string)\n    })\n    const Tree = types\n        .model(\"Tree\", {\n            // sadly, this becomes any, and further untypeable...\n            children: types.array(types.late((): IAnyModelType => Tree)),\n            data: types.maybeNull(types.reference(Folder))\n        })\n        .actions(self => {\n            function addFolder(data: SnapshotOrInstance<typeof Folder>) {\n                const folder3 = Folder.create(data)\n                getRoot<typeof Storage>(self).putFolderHelper(folder3)\n                self.children.push(\n                    Tree.create({ data: castToReferenceSnapshot(folder3), children: [] })\n                )\n            }\n            return { addFolder }\n        })\n\n    const Storage = types\n        .model(\"Storage\", {\n            objects: types.map(Folder),\n            tree: Tree\n        })\n        .actions(self => ({\n            putFolderHelper(aFolder: SnapshotOrInstance<typeof Folder>) {\n                self.objects.put(aFolder)\n            }\n        }))\n    const store = Storage.create({ objects: {}, tree: { children: [], data: null } })\n    const folder = { id: 1, name: \"Folder 1\", files: [\"a.jpg\", \"b.jpg\"] }\n    store.tree.addFolder(folder)\n    expect(getSnapshot(store)).toEqual({\n        objects: {\n            \"1\": {\n                files: [\"a.jpg\", \"b.jpg\"],\n                id: 1,\n                name: \"Folder 1\"\n            }\n        },\n        tree: {\n            children: [\n                {\n                    children: [],\n                    data: 1\n                }\n            ],\n            data: null\n        }\n    })\n    expect(store.objects.get(\"1\")).toBe(store.tree.children[0].data)\n    const folder2 = { id: 2, name: \"Folder 2\", files: [\"c.jpg\", \"d.jpg\"] }\n    store.tree.children[0].addFolder(folder2)\n    expect(getSnapshot(store)).toEqual({\n        objects: {\n            \"1\": {\n                files: [\"a.jpg\", \"b.jpg\"],\n                id: 1,\n                name: \"Folder 1\"\n            },\n            \"2\": {\n                files: [\"c.jpg\", \"d.jpg\"],\n                id: 2,\n                name: \"Folder 2\"\n            }\n        },\n        tree: {\n            children: [\n                {\n                    children: [\n                        {\n                            children: [],\n                            data: 2\n                        }\n                    ],\n                    data: 1\n                }\n            ],\n            data: null\n        }\n    })\n    expect(store.objects.get(\"1\")).toBe(store.tree.children[0].data)\n    expect(store.objects.get(\"2\")).toBe(store.tree.children[0].children[0].data)\n})\n\ntest(\"it should applyPatch references in array\", () => {\n    const Item = types.model(\"Item\", {\n        id: types.identifier,\n        name: types.string\n    })\n    const Folder = types\n        .model(\"Folder\", {\n            id: types.identifier,\n            objects: types.map(Item),\n            hovers: types.array(types.reference(Item))\n        })\n        .actions(self => {\n            function addObject(anItem: typeof Item.Type) {\n                self.objects.put(anItem)\n            }\n            function addHover(anItem: typeof Item.Type) {\n                self.hovers.push(anItem)\n            }\n            function removeHover(anItem: typeof Item.Type) {\n                self.hovers.remove(anItem)\n            }\n            return {\n                addObject,\n                addHover,\n                removeHover\n            }\n        })\n    const folder = Folder.create({ id: \"folder 1\", objects: {}, hovers: [] })\n    folder.addObject({ id: \"item 1\", name: \"item name 1\" })\n    const item = folder.objects.get(\"item 1\")!\n    const snapshot = getSnapshot(folder)\n    const newStore = Folder.create(snapshot)\n    onPatch(folder, data => {\n        applyPatch(newStore, data)\n    })\n    folder.addHover(item)\n    expect(getSnapshot(newStore)).toEqual({\n        id: \"folder 1\",\n        objects: {\n            \"item 1\": {\n                id: \"item 1\",\n                name: \"item name 1\"\n            }\n        },\n        hovers: [\"item 1\"]\n    })\n    folder.removeHover(item)\n    expect(getSnapshot(newStore)).toEqual({\n        id: \"folder 1\",\n        objects: {\n            \"item 1\": {\n                id: \"item 1\",\n                name: \"item name 1\"\n            }\n        },\n        hovers: []\n    })\n})\n\ntest(\"it should applySnapshot references in array\", () => {\n    const Item = types.model(\"Item\", {\n        id: types.identifier,\n        name: types.string\n    })\n    const Folder = types.model(\"Folder\", {\n        id: types.identifier,\n        objects: types.map(Item),\n        hovers: types.array(types.reference(Item))\n    })\n    const folder = Folder.create({\n        id: \"folder 1\",\n        objects: {\n            \"item 1\": {\n                id: \"item 1\",\n                name: \"item name 1\"\n            }\n        },\n        hovers: [\"item 1\"]\n    })\n    const snapshot = JSON.parse(JSON.stringify(getSnapshot(folder)))\n    expect(snapshot).toEqual({\n        id: \"folder 1\",\n        objects: {\n            \"item 1\": {\n                id: \"item 1\",\n                name: \"item name 1\"\n            }\n        },\n        hovers: [\"item 1\"]\n    })\n    snapshot.hovers = []\n    applySnapshot(folder, snapshot)\n    expect(getSnapshot(folder)).toEqual({\n        id: \"folder 1\",\n        objects: {\n            \"item 1\": {\n                id: \"item 1\",\n                name: \"item name 1\"\n            }\n        },\n        hovers: []\n    })\n    snapshot.hovers = [\"item 1\"]\n    applySnapshot(folder, snapshot)\n    expect(getSnapshot(folder)).toEqual({\n        id: \"folder 1\",\n        objects: {\n            \"item 1\": {\n                id: \"item 1\",\n                name: \"item name 1\"\n            }\n        },\n        hovers: [\"item 1\"]\n    })\n})\n\ntest(\"array of references should work fine\", () => {\n    const B = types.model(\"Block\", { id: types.identifier })\n    const S = types\n        .model(\"Store\", {\n            blocks: types.array(B),\n            blockRefs: types.array(types.reference(B))\n        })\n        .actions(self => {\n            return {\n                order() {\n                    const res = self.blockRefs.slice()\n                    self.blockRefs.replace([res[1], res[0]])\n                }\n            }\n        })\n    const a = S.create({ blocks: [{ id: \"1\" }, { id: \"2\" }], blockRefs: [\"1\", \"2\"] })\n    a.order()\n    expect(a.blocks[0].id).toBe(\"1\")\n    expect(a.blockRefs[0].id).toBe(\"2\")\n})\n\ntest(\"should serialize references correctly\", () => {\n    const M = types.model({\n        id: types.identifierNumber\n    })\n    const S = types.model({\n        mies: types.map(M),\n        ref: types.maybe(types.reference(M))\n    })\n\n    const s = S.create({\n        mies: {\n            7: {\n                id: 7\n            }\n        }\n    })\n    unprotect(s)\n\n    expect(Array.from(s.mies.keys())).toEqual([\"7\"])\n    expect(s.mies.get(\"7\")!.id).toBe(7)\n    expect(s.mies.get(7 as any)).toBe(s.mies.get(\"7\")!) // maps automatically normalizes the key\n\n    s.mies.put({\n        id: 8\n    })\n    expect(Array.from(s.mies.keys())).toEqual([\"7\", \"8\"])\n\n    s.ref = 8 as any\n    expect(s.ref!.id).toBe(8) // resolved from number\n    expect(getSnapshot(s).ref).toBe(8) // ref serialized as number\n\n    s.ref = \"7\" as any // resolved from string\n    expect(s.ref!.id).toBe(7) // resolved from string\n    expect(getSnapshot(s).ref).toBe(\"7\") // ref serialized as string (number would be ok as well)\n\n    s.ref = s.mies.get(\"8\")!\n    expect(s.ref.id).toBe(8) // resolved from instance\n    expect(getSnapshot(s).ref).toBe(8) // ref serialized as number\n\n    s.ref = \"9\" as any // unresolvable\n    expect(getSnapshot(s).ref).toBe(\"9\") // snapshot preserved as it was unresolvable\n\n    s.mies.set(9 as any, {\n        id: 9\n    })\n    expect(Array.from(s.mies.keys())).toEqual([\"7\", \"8\", \"9\"])\n    expect(s.mies.get(\"9\")!.id).toBe(9)\n    expect(getSnapshot(s).ref).toBe(\"9\") // ref serialized as string (number would be ok as well)\n})\n\ntest(\"#1052 - Reference returns destroyed model after subtree replacing\", () => {\n    const Todo = types.model(\"Todo\", {\n        id: types.identifierNumber,\n        title: types.string\n    })\n\n    const Todos = types.model(\"Todos\", {\n        items: types.array(Todo)\n    })\n\n    const Store = types\n        .model(\"Store\", {\n            todos: Todos,\n            last: types.maybe(types.reference(Todo)),\n            lastWithId: types.maybe(types.reference(Todo)),\n            counter: -1\n        })\n        .actions(self => ({\n            load() {\n                self.counter++\n                self.todos = Todos.create({\n                    items: [\n                        { id: 1, title: \"Get Coffee \" + self.counter },\n                        { id: 2, title: \"Write simpler code \" + self.counter }\n                    ]\n                })\n            },\n            select(todo: Instance<typeof Todo>) {\n                self.last = todo\n                self.lastWithId = todo.id as any\n            }\n        }))\n\n    const store = Store.create({ todos: {} })\n    store.load()\n\n    expect(store.last).toBeUndefined()\n    expect(store.lastWithId).toBeUndefined()\n\n    const reactionFn = jest.fn()\n    const reactionDisposer = reaction(() => store.last, reactionFn)\n    const reactionFn2 = jest.fn()\n    const reactionDisposer2 = reaction(() => store.lastWithId, reactionFn2)\n\n    try {\n        store.select(store.todos.items[0])\n\n        expect(isAlive(store.last!)).toBe(true)\n        expect(isObservable(store.last)).toBe(true)\n        expect(reactionFn).toHaveBeenCalledTimes(1)\n        expect(store.last!.title).toBe(\"Get Coffee 0\")\n\n        expect(isAlive(store.lastWithId!)).toBe(true)\n        expect(isObservable(store.lastWithId)).toBe(true)\n        expect(reactionFn2).toHaveBeenCalledTimes(1)\n        expect(store.lastWithId!.title).toBe(\"Get Coffee 0\")\n\n        store.load()\n\n        expect(isAlive(store.last!)).toBe(true)\n        expect(isObservable(store.last)).toBe(true)\n        expect(reactionFn).toHaveBeenCalledTimes(2)\n        expect(store.last!.title).toBe(\"Get Coffee 1\")\n\n        expect(isAlive(store.lastWithId!)).toBe(true)\n        expect(isObservable(store.lastWithId)).toBe(true)\n        expect(reactionFn2).toHaveBeenCalledTimes(2)\n        expect(store.lastWithId!.title).toBe(\"Get Coffee 1\")\n    } finally {\n        reactionDisposer()\n        reactionDisposer2()\n    }\n})\n\ntest(\"#1080 - does not crash trying to resolve a reference to a destroyed+recreated model\", () => {\n    configure({\n        useProxies: \"never\"\n    })\n\n    const Branch = types.model(\"Branch\", {\n        id: types.identifierNumber,\n        name: types.string\n    })\n\n    const User = types.model(\"User\", {\n        id: types.identifierNumber,\n        email: types.maybeNull(types.string),\n        branches: types.maybeNull(types.array(Branch))\n    })\n\n    const BranchStore = types\n        .model(\"BranchStore\", {\n            activeBranch: types.maybeNull(types.reference(Branch))\n        })\n        .actions(self => ({\n            setActiveBranch(branchId: any) {\n                self.activeBranch = branchId\n            }\n        }))\n\n    const RootStore = types\n        .model(\"RootStore\", {\n            user: types.maybeNull(User),\n            branchStore: types.maybeNull(BranchStore)\n        })\n        .actions(self => ({\n            setUser(snapshot: typeof userSnapshot) {\n                self.user = cast(snapshot)\n            },\n            setBranchStore(snapshot: typeof branchStoreSnapshot) {\n                self.branchStore = cast(snapshot)\n            },\n            destroyUser() {\n                destroy(self.user!)\n            },\n            destroyBranchStore() {\n                destroy(self.branchStore!)\n            }\n        }))\n\n    const userSnapshot = {\n        id: 1,\n        email: \"test@test.com\",\n        branches: [\n            {\n                id: 1,\n                name: \"Branch 1\"\n            },\n            {\n                id: 2,\n                name: \"Branch 2\"\n            }\n        ]\n    }\n\n    const branchStoreSnapshot = {}\n    const rootStore = RootStore.create({ user: userSnapshot, branchStore: branchStoreSnapshot })\n\n    rootStore.branchStore!.setActiveBranch(1)\n    expect(rootStore.branchStore!.activeBranch).toEqual({\n        id: 1,\n        name: \"Branch 1\"\n    })\n\n    rootStore.destroyUser()\n    rootStore.destroyBranchStore()\n\n    rootStore.setUser(userSnapshot)\n    rootStore.setBranchStore(branchStoreSnapshot)\n\n    rootStore.branchStore!.setActiveBranch(2)\n    expect(rootStore.branchStore!.activeBranch).toEqual({\n        id: 2,\n        name: \"Branch 2\"\n    })\n})\n\ntest(\"tryReference / isValidReference\", () => {\n    const Todo = types.model({ id: types.identifier })\n\n    const TodoStore = types\n        .model({\n            todos: types.array(Todo),\n            ref1: types.maybe(types.reference(Todo)),\n            ref2: types.maybeNull(types.reference(Todo)),\n            ref3: types.maybe(types.reference(Todo))\n        })\n        .actions(self => ({\n            clearRef3() {\n                self.ref3 = undefined\n            },\n            afterCreate() {\n                addDisposer(\n                    self,\n                    reaction(\n                        () => isValidReference(() => self.ref3),\n                        valid => {\n                            if (!valid) {\n                                this.clearRef3()\n                            }\n                        },\n                        { fireImmediately: true }\n                    )\n                )\n            }\n        }))\n\n    const store = TodoStore.create({\n        todos: [{ id: \"1\" }, { id: \"2\" }, { id: \"3\" }]\n    })\n\n    expect(tryReference(() => store.ref1)).toBeUndefined()\n    expect(tryReference(() => store.ref2)).toBeUndefined()\n    expect(isValidReference(() => store.ref1)).toBe(false)\n    expect(isValidReference(() => store.ref2)).toBe(false)\n\n    unprotect(store)\n    store.ref1 = store.todos[0]\n    store.ref2 = store.todos[1]\n    store.ref3 = store.todos[2]\n\n    expect(isStateTreeNode(store.ref1)).toBe(true)\n    expect(isStateTreeNode(store.ref2)).toBe(true)\n\n    expect(tryReference(() => store.ref1)).toBeDefined()\n    expect(tryReference(() => store.ref2)).toBeDefined()\n    expect(isValidReference(() => store.ref1)).toBe(true)\n    expect(isValidReference(() => store.ref2)).toBe(true)\n\n    store.todos = cast([])\n\n    expect(tryReference(() => store.ref1)).toBeUndefined()\n    expect(tryReference(() => store.ref2)).toBeUndefined()\n    expect(isValidReference(() => store.ref1)).toBe(false)\n    expect(isValidReference(() => store.ref2)).toBe(false)\n\n    // the reaction should have triggered and set this to undefined\n    expect(store.ref3).toBeUndefined()\n\n    expect(() => tryReference(() => 5 as any)).toThrow(\n        \"The reference to be checked is not one of node, null or undefined\"\n    )\n    expect(() => isValidReference(() => 5 as any)).toThrow(\n        \"The reference to be checked is not one of node, null or undefined\"\n    )\n})\n\ntest(\"#1162 - reference to union\", () => {\n    const M1 = types.model({ id: types.identifier, type: types.string, sum: types.string })\n    const M2 = types.model({\n        id: types.identifier,\n        type: types.string,\n        data: types.string\n    })\n    const AnyModel = types.union(\n        {\n            dispatcher(snapshot) {\n                switch (snapshot.type) {\n                    case \"type1\":\n                        return M1\n                    case \"type2\":\n                        return M2\n                    default:\n                        throw new Error()\n                }\n            }\n        },\n        M1,\n        M2\n    )\n\n    const Store = types.model({\n        arr: types.array(AnyModel),\n        selected: types.reference(AnyModel)\n    })\n\n    const s = Store.create({\n        selected: \"num1\",\n        arr: [\n            { id: \"num1\", type: \"type1\", sum: \"1\" },\n            { id: \"num2\", type: \"type1\", sum: \"2\" },\n            { id: \"num3\", type: \"type2\", data: \"3\" }\n        ]\n    })\n    unprotect(s)\n\n    expect(s.selected.id).toBe(\"num1\")\n    expect(s.selected.type).toBe(\"type1\")\n    expect((s.selected as Instance<typeof M1>).sum).toBe(\"1\")\n\n    s.selected = \"num2\" as any\n    expect(s.selected.id).toBe(\"num2\")\n    expect(s.selected.type).toBe(\"type1\")\n    expect((s.selected as Instance<typeof M1>).sum).toBe(\"2\")\n\n    s.selected = \"num3\" as any\n    expect(s.selected.id).toBe(\"num3\")\n    expect(s.selected.type).toBe(\"type2\")\n    expect((s.selected as Instance<typeof M2>).data).toBe(\"3\")\n})\n"
  },
  {
    "path": "__tests__/core/refinement.test.ts",
    "content": "import { getSnapshot, types } from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"it should allow if type and predicate is correct\", () => {\n    const Factory = types.model({\n        number: types.refinement(\n            \"positive number\",\n            types.optional(types.number, 0),\n            s => typeof s === \"number\" && s >= 0\n        )\n    })\n    const doc = Factory.create({ number: 42 })\n    expect(getSnapshot(doc)).toEqual({ number: 42 })\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should throw if a correct type with failing predicate is given\", () => {\n        const Factory = types.model({\n            number: types.refinement(\n                \"positive number\",\n                types.optional(types.number, 0),\n                s => typeof s === \"number\" && s >= 0\n            )\n        })\n        expect(() => {\n            Factory.create({ number: \"givenStringInstead\" } as any)\n        }).toThrow(\n            `[mobx-state-tree] Error while converting \\`{\\\"number\\\":\\\"givenStringInstead\\\"}\\` to \\`AnonymousModel\\`:\\n\\n    at path \\\"/number\\\" value \\`\\\"givenStringInstead\\\"\\` is not assignable to type: \\`positive number\\` (Value is not a number).`\n        )\n        expect(() => {\n            Factory.create({ number: -4 })\n        }).toThrow(\n            `[mobx-state-tree] Error while converting \\`{\\\"number\\\":-4}\\` to \\`AnonymousModel\\`:\\n\\n    at path \\\"/number\\\" value \\`-4\\` is not assignable to type: \\`positive number\\` (Value does not respect the refinement predicate).`\n        )\n    })\n    test(\"it should throw custom error message with failing predicate is given\", () => {\n        const Factory = types.model({\n            number: types.refinement(\n                types.optional(types.number, 0),\n                s => typeof s === \"number\" && s >= 0,\n                s => \"A positive number was expected\"\n            )\n        })\n        expect(() => {\n            Factory.create({ number: \"givenStringInstead\" } as any)\n        }).toThrow(\n            `[mobx-state-tree] Error while converting \\`{\\\"number\\\":\\\"givenStringInstead\\\"}\\` to \\`AnonymousModel\\`:\\n\\n    at path \\\"/number\\\" value \\`\\\"givenStringInstead\\\"\\` is not assignable to type: \\`number\\` (Value is not a number).`\n        )\n        expect(() => {\n            Factory.create({ number: -4 })\n        }).toThrow(\n            `[mobx-state-tree] Error while converting \\`{\\\"number\\\":-4}\\` to \\`AnonymousModel\\`:\\n\\n    at path \"/number\" value \\`-4\\` is not assignable to type: \\`number\\` (A positive number was expected).`\n        )\n    })\n}\n"
  },
  {
    "path": "__tests__/core/reflection.test.ts",
    "content": "import {\n    types,\n    getMembers,\n    getPropertyMembers,\n    IAnyStateTreeNode,\n    getType,\n    IAnyModelType,\n    IModelReflectionData,\n    IModelReflectionPropertiesData,\n    flow\n} from \"../../src\"\nimport { expect, test } from \"bun:test\"\n\nconst User = types.model(\"User\", {\n    id: types.identifier,\n    name: types.string\n})\n\nconst Model = types\n    .model({\n        isPerson: false,\n        users: types.optional(types.map(User), {}),\n        dogs: types.array(User),\n        user: types.maybe(types.late(() => User))\n    })\n    .volatile(self => ({\n        volatileProperty: { propName: \"halo\" }\n    }))\n    .actions(self => {\n        function actionName() {\n            return 1\n        }\n        return {\n            actionName,\n            generatorAction: flow(function* generatorAction() {\n                const promise = new Promise(resolve => {\n                    resolve(true)\n                })\n                yield promise\n            })\n        }\n    })\n    .views(self => ({\n        get viewName() {\n            return 1\n        }\n    }))\n\nfunction expectPropertyMembersToMatchMembers(\n    propertyMembers: IModelReflectionPropertiesData,\n    members: IModelReflectionData\n) {\n    expect(propertyMembers).toEqual({\n        name: members.name,\n        properties: members.properties\n    })\n}\n\ntest(\"reflection - model\", () => {\n    const node = Model.create()\n    const reflection = getMembers(node)\n    expect(reflection.name).toBe(\"AnonymousModel\")\n    expect(reflection.actions.includes(\"actionName\")).toBe(true)\n    expect(reflection.actions.includes(\"generatorAction\")).toBe(true)\n    expect(reflection.flowActions.includes(\"generatorAction\")).toBe(true)\n    expect(reflection.flowActions.includes(\"actionName\")).toBe(false)\n    expect(reflection.views.includes(\"viewName\")).toBe(true)\n    expect(reflection.views.includes(\"actionName\")).toBe(false)\n    expect(reflection.volatile.includes(\"volatileProperty\")).toBe(true)\n    expect(!!reflection.properties.users).toBe(true)\n    expect(!!reflection.properties.isPerson).toBe(true)\n\n    const typeReflection = getPropertyMembers(Model)\n    expectPropertyMembersToMatchMembers(typeReflection, reflection)\n    const reflection2 = getPropertyMembers(node)\n    expectPropertyMembersToMatchMembers(reflection2, reflection)\n})\ntest(\"reflection - map\", () => {\n    const node = Model.create({\n        users: { \"1\": { id: \"1\", name: \"Test\" } }\n    })\n    const node2 = node.users.get(\"1\")!\n    const reflection = getMembers(node2)\n    expect(reflection.name).toBe(\"User\")\n    expect(!!reflection.properties.id).toBe(true)\n    expect(!!reflection.properties.name).toBe(true)\n\n    const typeReflection = getPropertyMembers(getType(node2) as IAnyModelType)\n    expectPropertyMembersToMatchMembers(typeReflection, reflection)\n    const reflection2 = getPropertyMembers(node2)\n    expectPropertyMembersToMatchMembers(reflection2, reflection)\n})\ntest(\"reflection - array\", () => {\n    const node = Model.create({\n        dogs: [{ id: \"1\", name: \"Test\" }]\n    })\n    const node2 = node.dogs[0]\n    const reflection = getMembers(node2)\n    expect(!!reflection.properties.id).toBe(true)\n    expect(!!reflection.properties.name).toBe(true)\n\n    const typeReflection = getPropertyMembers(getType(node2) as IAnyModelType)\n    expectPropertyMembersToMatchMembers(typeReflection, reflection)\n    const reflection2 = getPropertyMembers(node2)\n    expectPropertyMembersToMatchMembers(reflection2, reflection)\n})\ntest(\"reflection - late\", () => {\n    const node = Model.create({\n        user: { id: \"5\", name: \"Test\" }\n    })\n    const empty: IAnyStateTreeNode = {}\n    const reflection = getMembers(node.user || empty)\n    const keys = Object.keys(reflection.properties || {})\n    expect(keys.includes(\"name\")).toBe(true)\n    expect(reflection.properties.name.describe()).toBe(\"string\")\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"reflection - throw on non model node for getMembers\", () => {\n        const node = Model.create({\n            users: { \"1\": { id: \"1\", name: \"Test\" } }\n        })\n        expect(() => (node.users ? getMembers(node.users) : {})).toThrow()\n    })\n\n    test(\"reflection - throw on non model type/node for getMembers\", () => {\n        expect(() => getPropertyMembers(types.array(types.number) as any)).toThrow()\n\n        const node = Model.create({\n            users: { \"1\": { id: \"1\", name: \"Test\" } }\n        })\n        expect(() => getPropertyMembers(node.users)).toThrow()\n    })\n}\ntest(\"reflection - can retrieve property names\", () => {\n    const node = Model.create()\n    const reflection = getMembers(node)\n    const keys = Object.keys(reflection.properties)\n    expect(keys.includes(\"users\")).toBe(true)\n    expect(keys.includes(\"isPerson\")).toBe(true)\n})\ntest(\"reflection - property contains type\", () => {\n    const TestModel = types.model({\n        string: types.string,\n        optional: false\n    })\n    const node = TestModel.create({\n        string: \"hello\"\n    })\n    const reflection = getMembers(node)\n    expect(reflection.properties.string).toBe(types.string)\n    expect(reflection.properties.optional).toMatchObject(types.optional(types.boolean, false))\n})\ntest(\"reflection - members chained\", () => {\n    const ChainedModel = types\n        .model({\n            isPerson: false\n        })\n        .actions(self => {\n            return {\n                actionName() {\n                    return 1\n                }\n            }\n        })\n        .actions(self => {\n            return {\n                anotherAction() {\n                    return 1\n                }\n            }\n        })\n        .actions(self => {\n            function flowActionName() {\n                return 1\n            }\n            return {\n                flowActionName,\n                generatorAction: flow(function* generatorAction() {\n                    const promise = new Promise(resolve => {\n                        resolve(true)\n                    })\n                    yield promise\n                })\n            }\n        })\n        .views(self => ({\n            get viewName() {\n                return 1\n            }\n        }))\n        .views(self => ({\n            anotherView(prop: string) {\n                return 1\n            }\n        }))\n    const node = ChainedModel.create()\n    const reflection = getMembers(node)\n    const keys = Object.keys(reflection.properties || {})\n    expect(keys.includes(\"isPerson\")).toBe(true)\n    expect(reflection.actions.includes(\"actionName\")).toBe(true)\n    expect(reflection.actions.includes(\"anotherAction\")).toBe(true)\n    expect(reflection.actions.includes(\"flowActionName\")).toBe(true)\n    expect(reflection.actions.includes(\"generatorAction\")).toBe(true)\n    expect(reflection.flowActions.includes(\"generatorAction\")).toBe(true)\n    expect(reflection.flowActions.includes(\"flowActionName\")).toBe(false)\n    expect(reflection.views.includes(\"viewName\")).toBe(true)\n    expect(reflection.views.includes(\"anotherView\")).toBe(true)\n    expect(reflection.views.includes(\"actionName\")).toBe(false)\n    expect(reflection.views.includes(\"anotherAction\")).toBe(false)\n    expect(reflection.views.includes(\"flowActionName\")).toBe(false)\n})\ntest(\"reflection - conditionals respected\", () => {\n    let swap = true\n    const ConditionalModel = types\n        .model({\n            isPerson: false\n        })\n        .actions(self => ({\n            actionName0() {\n                return 1\n            }\n        }))\n        .actions((self): { actionName1(): number } | { actionName2(): number } => {\n            if (swap) {\n                return {\n                    actionName1() {\n                        return 1\n                    }\n                }\n            } else {\n                return {\n                    actionName2() {\n                        return 1\n                    }\n                }\n            }\n        })\n        .views(self => {\n            if (swap) {\n                return {\n                    get view1() {\n                        return 1\n                    }\n                }\n            } else {\n                return {\n                    get view2() {\n                        return 1\n                    }\n                }\n            }\n        })\n    // swap true\n    const node = ConditionalModel.create()\n    const reflection = getMembers(node)\n    expect(reflection.actions.includes(\"actionName0\")).toBe(true)\n    expect(reflection.actions.includes(\"actionName1\")).toBe(true)\n    expect(reflection.actions.includes(\"actionName2\")).toBe(false)\n    expect(reflection.views.includes(\"view1\")).toBe(true)\n    expect(reflection.views.includes(\"view2\")).toBe(false)\n    swap = false\n    const node2 = ConditionalModel.create()\n    const reflection2 = getMembers(node2)\n    expect(reflection.actions.includes(\"actionName0\")).toBe(true)\n    expect(reflection2.actions.includes(\"actionName1\")).toBe(false)\n    expect(reflection2.actions.includes(\"actionName2\")).toBe(true)\n    expect(reflection2.views.includes(\"view1\")).toBe(false)\n    expect(reflection2.views.includes(\"view2\")).toBe(true)\n})\n"
  },
  {
    "path": "__tests__/core/snapshotProcessor.test.ts",
    "content": "import { observable } from \"mobx\"\nimport {\n    types,\n    getSnapshot,\n    unprotect,\n    cast,\n    detach,\n    clone,\n    SnapshotIn,\n    getNodeId,\n    Instance,\n    onSnapshot\n} from \"../../src\"\nimport { describe, expect, jest, test } from \"bun:test\"\n\ndescribe(\"snapshotProcessor\", () => {\n    describe(\"over a model type\", () => {\n        const M = types.model({\n            x: types.string\n        })\n\n        test(\"no processors\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {})\n            })\n            const model = Model.create({ m: { x: \"hi\" } })\n            unprotect(model)\n            expect(model.m.x).toBe(\"hi\")\n            expect(getSnapshot(model).m.x).toBe(\"hi\")\n            // reconciliation\n            model.m = { x: \"ho\" }\n            expect(model.m.x).toBe(\"ho\")\n            expect(getSnapshot(model).m.x).toBe(\"ho\")\n        })\n\n        test(\"pre processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: { x: number }) {\n                        return {\n                            ...sn,\n                            x: String(sn.x)\n                        }\n                    }\n                })\n            })\n            const model = Model.create({ m: { x: 5 } })\n            unprotect(model)\n            expect(model.m.x).toBe(\"5\")\n            expect(getSnapshot(model).m.x).toBe(\"5\")\n            // reconciliation\n            model.m = cast({ x: 6 })\n            expect(model.m.x).toBe(\"6\")\n            expect(getSnapshot(model).m.x).toBe(\"6\")\n        })\n\n        test(\"post processor\", () => {\n            let model: Instance<typeof Model>\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    postProcessor(sn, node): { x: number; val?: string } {\n                        expect(node).toBeTruthy()\n\n                        return {\n                            ...sn,\n                            x: Number(sn.x),\n                            val: node.x\n                        }\n                    }\n                })\n            })\n            model = Model.create({\n                m: { x: \"5\" }\n            })\n            unprotect(model)\n            expect(model.m.x).toBe(\"5\")\n            expect(getSnapshot(model).m.x).toBe(5)\n            expect(getSnapshot(model).m.val).toBe(\"5\")\n            // reconciliation\n            model.m = cast({ x: \"6\" })\n            expect(model.m.x).toBe(\"6\")\n            expect(getSnapshot(model).m.x).toBe(6)\n            expect(getSnapshot(model).m.val).toBe(\"6\")\n        })\n\n        test(\"post processor that observes other observables recomputes when they change\", () => {\n            let model: Instance<typeof Model>\n            const atom = observable.box(\"foo\")\n\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    postProcessor(sn, node): { x: number; val: string } {\n                        return {\n                            ...sn,\n                            x: Number(sn.x),\n                            val: atom.get()\n                        }\n                    }\n                })\n            })\n            model = Model.create({\n                m: { x: \"5\" }\n            })\n            const newSnapshot = jest.fn()\n            onSnapshot(model, newSnapshot)\n            expect(getSnapshot(model).m.val).toBe(\"foo\")\n            atom.set(\"bar\")\n            expect(getSnapshot(model).m.val).toBe(\"bar\")\n            expect(newSnapshot).toHaveBeenCalledTimes(1)\n        })\n\n        test(\"pre and post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: { x: number }) {\n                        return {\n                            ...sn,\n                            x: String(sn.x)\n                        }\n                    },\n                    postProcessor(sn): { x: number } {\n                        return {\n                            ...sn,\n                            x: Number(sn.x)\n                        }\n                    }\n                })\n            })\n            const model = Model.create({\n                m: { x: 5 }\n            })\n            unprotect(model)\n            expect(model.m.x).toBe(\"5\")\n            expect(getSnapshot(model).m.x).toBe(5)\n            // reconciliation\n            model.m = cast({ x: 6 })\n            expect(model.m.x).toBe(\"6\")\n            expect(getSnapshot(model).m.x).toBe(6)\n            // cloning\n            expect(getSnapshot(clone(model.m)).x).toBe(6)\n        })\n    })\n\n    describe(\"over a literal type\", () => {\n        const M = types.string\n\n        test(\"no processors\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {})\n            })\n            const model = Model.create({ m: \"hi\" })\n            unprotect(model)\n            expect(model.m).toBe(\"hi\")\n            expect(getSnapshot(model).m).toBe(\"hi\")\n            // reconciliation\n            model.m = \"ho\"\n            expect(model.m).toBe(\"ho\")\n            expect(getSnapshot(model).m).toBe(\"ho\")\n        })\n\n        test(\"pre processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: number) {\n                        return String(sn)\n                    }\n                })\n            })\n            const model = Model.create({ m: 5 })\n            unprotect(model)\n            expect(model.m).toBe(\"5\")\n            expect(getSnapshot(model).m).toBe(\"5\")\n            // reconciliation\n            model.m = 6 as any\n            expect(model.m).toBe(\"6\")\n            expect(getSnapshot(model).m).toBe(\"6\")\n        })\n\n        test(\"post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    postProcessor(sn, node): number {\n                        expect(node).toMatch(/5|6/)\n                        return Number(sn)\n                    }\n                })\n            })\n            const model = Model.create({\n                m: \"5\"\n            })\n            unprotect(model)\n            expect(model.m).toBe(\"5\")\n            expect(getSnapshot(model).m).toBe(5)\n            // reconciliation\n            model.m = \"6\"\n            expect(model.m).toBe(\"6\")\n            expect(getSnapshot(model).m).toBe(6)\n        })\n\n        test(\"pre and post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: number) {\n                        return String(sn)\n                    },\n                    postProcessor(sn): number {\n                        return Number(sn)\n                    }\n                })\n            })\n            const model = Model.create({\n                m: 5\n            })\n            unprotect(model)\n            expect(model.m).toBe(\"5\")\n            expect(getSnapshot(model).m).toBe(5)\n            // reconciliation\n            model.m = \"6\"\n            expect(model.m).toBe(\"6\")\n            expect(getSnapshot(model).m).toBe(6)\n            // cloning\n            expect(getSnapshot(clone(model)).m).toBe(6)\n        })\n    })\n\n    describe(\"over an array type\", () => {\n        const M = types.array(types.string)\n\n        test(\"no processors\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {})\n            })\n            const model = Model.create({ m: [\"hi\"] })\n            unprotect(model)\n            expect(model.m[0]).toBe(\"hi\")\n            expect(getSnapshot(model).m[0]).toBe(\"hi\")\n            // reconciliation\n            model.m = cast([\"ho\"])\n            expect(model.m[0]).toBe(\"ho\")\n            expect(getSnapshot(model).m[0]).toBe(\"ho\")\n        })\n\n        test(\"pre processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: number[]) {\n                        return sn.map(n => String(n))\n                    }\n                })\n            })\n            const model = Model.create({ m: [5] })\n            unprotect(model)\n            expect(model.m[0]).toBe(\"5\")\n            expect(getSnapshot(model).m[0]).toBe(\"5\")\n            // reconciliation\n            model.m = cast([6])\n            expect(model.m[0]).toBe(\"6\")\n            expect(getSnapshot(model).m[0]).toBe(\"6\")\n        })\n\n        test(\"post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    postProcessor(sn, node): number[] {\n                        expect(node).toBeDefined()\n                        expect(node.length).toEqual(1)\n                        return sn.map(n => Number(n))\n                    }\n                })\n            })\n            const model = Model.create({\n                m: [\"5\"]\n            })\n            unprotect(model)\n            expect(model.m[0]).toBe(\"5\")\n            expect(getSnapshot(model).m[0]).toBe(5)\n            // reconciliation\n            model.m = cast([\"6\"])\n            expect(model.m[0]).toBe(\"6\")\n            expect(getSnapshot(model).m[0]).toBe(6)\n        })\n\n        test(\"pre and post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: number[]) {\n                        return sn.map(n => String(n))\n                    },\n                    postProcessor(sn): number[] {\n                        return sn.map(n => Number(n))\n                    }\n                })\n            })\n            const model = Model.create({\n                m: [5]\n            })\n            unprotect(model)\n            expect(model.m[0]).toBe(\"5\")\n            expect(getSnapshot(model).m[0]).toBe(5)\n            // reconciliation\n            model.m = cast([6])\n            expect(model.m[0]).toBe(\"6\")\n            expect(getSnapshot(model).m[0]).toBe(6)\n            // cloning\n            expect(getSnapshot(clone(model.m))[0]).toBe(6)\n        })\n    })\n\n    describe(\"over a map type\", () => {\n        const M = types.map(types.string)\n\n        test(\"no processors\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {})\n            })\n            const model = Model.create({ m: { x: \"hi\" } })\n            unprotect(model)\n            expect(model.m.get(\"x\")).toBe(\"hi\")\n            expect(getSnapshot(model).m.x).toBe(\"hi\")\n            // reconciliation\n            model.m.set(\"x\", \"ho\")\n            expect(model.m.get(\"x\")).toBe(\"ho\")\n            expect(getSnapshot(model).m.x).toBe(\"ho\")\n        })\n\n        test(\"pre processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: { x: number }) {\n                        return {\n                            ...sn,\n                            x: String(sn.x)\n                        }\n                    }\n                })\n            })\n            const model = Model.create({ m: { x: 5 } })\n            unprotect(model)\n            expect(model.m.get(\"x\")).toBe(\"5\")\n            expect(getSnapshot(model).m.x).toBe(\"5\")\n            // reconciliation\n            model.m = cast({ x: 6 })\n            expect(model.m.get(\"x\")).toBe(\"6\")\n            expect(getSnapshot(model).m.x).toBe(\"6\")\n        })\n\n        test(\"post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    postProcessor(sn, node): { x: number } {\n                        expect(node.size).toBe(1)\n\n                        return {\n                            ...sn,\n                            x: Number(sn.x)\n                        }\n                    }\n                })\n            })\n            const model = Model.create({\n                m: { x: \"5\" }\n            })\n            unprotect(model)\n            expect(model.m.get(\"x\")).toBe(\"5\")\n            expect(getSnapshot(model).m.x).toBe(5)\n            // reconciliation\n            model.m = cast({ x: \"6\" })\n            expect(model.m.get(\"x\")).toBe(\"6\")\n            expect(getSnapshot(model).m.x).toBe(6)\n        })\n\n        test(\"pre and post processor\", () => {\n            const Model = types.model({\n                m: types.snapshotProcessor(M, {\n                    preProcessor(sn: { x: number }) {\n                        return {\n                            ...sn,\n                            x: String(sn.x)\n                        }\n                    },\n                    postProcessor(sn): { x: number } {\n                        return {\n                            ...sn,\n                            x: Number(sn.x)\n                        }\n                    }\n                })\n            })\n            const model = Model.create({\n                m: { x: 5 }\n            })\n            unprotect(model)\n            expect(model.m.get(\"x\")).toBe(\"5\")\n            expect(getSnapshot(model).m.x).toBe(5)\n            // reconciliation\n            model.m = cast({ x: 6 })\n            expect(model.m.get(\"x\")).toBe(\"6\")\n            expect(getSnapshot(model).m.x).toBe(6)\n            // cloning\n            expect(getSnapshot(clone(model.m)).x).toBe(6)\n        })\n    })\n\n    test(\"chained transforms\", () => {\n        const TL = types.snapshotProcessor(types.string, {\n            preProcessor(sn: string) {\n                return sn.trimLeft()\n            },\n            postProcessor(sn): string {\n                return \"_\" + sn\n            }\n        })\n        const TB = types.snapshotProcessor(TL, {\n            preProcessor(sn: string) {\n                return sn.trimRight()\n            },\n            postProcessor(sn): string {\n                return sn + \"_\"\n            }\n        })\n        const M = types.model({\n            name: TB\n        })\n\n        const t = TB.create(\" hello \")\n        expect(t).toBe(\"hello\")\n\n        const m = M.create({\n            name: \" hello \"\n        })\n        expect(m.name).toBe(\"hello\")\n        expect(getSnapshot(m).name).toBe(\"_hello_\")\n    })\n\n    describe(\"moving nodes around with a pre-processor\", () => {\n        const Task = types.model(\"Task\", { x: types.number })\n        const Store = types.model({\n            a: types.array(\n                types.snapshotProcessor(\n                    Task,\n                    {\n                        preProcessor(sn: { x: string }) {\n                            return {\n                                x: Number(sn.x)\n                            }\n                        }\n                    },\n                    \"PTask\"\n                )\n            ),\n            b: types.array(Task)\n        })\n\n        test(\"moving from a to b\", () => {\n            const s = Store.create({\n                a: [{ x: \"1\" }]\n            })\n            unprotect(s)\n            const n = s.a[0]\n            detach(n)\n            expect(s.a.length).toBe(0)\n            expect(getSnapshot(n)).toEqual({ x: 1 })\n\n            s.b.push(n)\n            expect(s.b.length).toBe(1)\n            expect(getSnapshot(s.b)).toEqual([{ x: 1 }])\n        })\n\n        test(\"moving from b to a\", () => {\n            const s = Store.create({\n                b: [{ x: 1 }]\n            })\n            unprotect(s)\n            const n = s.b[0]\n            detach(n)\n            expect(s.b.length).toBe(0)\n            expect(getSnapshot(n)).toEqual({ x: 1 })\n\n            s.a.push(n)\n            expect(s.a.length).toBe(1)\n            expect(getSnapshot(s.a)).toEqual([{ x: 1 }])\n        })\n    })\n\n    describe(\"moving nodes around with a post-processor\", () => {\n        const Task = types.model({ x: types.number })\n        const Store = types.model({\n            a: types.array(\n                types.snapshotProcessor(Task, {\n                    postProcessor(sn): { x: string } {\n                        return {\n                            x: String(sn.x)\n                        }\n                    }\n                })\n            ),\n            b: types.array(Task)\n        })\n\n        test(\"moving from a to b\", () => {\n            const s = Store.create({\n                a: [{ x: 1 }]\n            })\n            unprotect(s)\n            const n = s.a[0]\n            detach(n)\n            expect(s.a.length).toBe(0)\n            expect(getSnapshot(n)).toEqual({ x: \"1\" })\n\n            s.b.push(n)\n            expect(s.b.length).toBe(1)\n            // @ts-expect-error - post processor gets applied here and messes up the types\n            expect(getSnapshot(s.b)).toEqual([{ x: \"1\" }])\n        })\n\n        test(\"moving from b to a\", () => {\n            const s = Store.create({\n                b: [{ x: 1 }]\n            })\n            unprotect(s)\n            const n = s.b[0]\n            detach(n)\n            expect(s.b.length).toBe(0)\n            expect(getSnapshot(n)).toEqual({ x: 1 })\n\n            s.a.push(n)\n            expect(s.a.length).toBe(1)\n            expect(getSnapshot(s.a)).toEqual([{ x: \"1\" }])\n        })\n    })\n\n    describe(\"assigning instances works\", () => {\n        const Todo = types.model(\"Todo\", {\n            id: types.identifier\n        })\n\n        const TodoWithProcessor = types.snapshotProcessor(Todo, {\n            preProcessor(snapshot: { id: string }) {\n                return snapshot\n            }\n        })\n\n        const Store = types\n            .model(\"TodoStore\", {\n                todos: types.map(TodoWithProcessor),\n                instance: types.optional(TodoWithProcessor, { id: \"new\" })\n            })\n            .actions(self => ({\n                addTodo(todo: { id: string }) {\n                    self.todos.put(todo)\n                },\n                setInstance(next: { id: string }) {\n                    self.instance = next\n                }\n            }))\n\n        test(\"using instances in maps work\", () => {\n            const store = Store.create()\n            const todo = TodoWithProcessor.create({ id: \"map\" })\n\n            store.addTodo(todo)\n\n            expect(store.todos.size).toBe(1)\n            expect(getSnapshot(store.todos)).toEqual({ map: { id: \"map\" } })\n        })\n\n        test(\"using instances as values works\", () => {\n            const store = Store.create()\n            const todo = TodoWithProcessor.create({ id: \"map\" })\n\n            store.setInstance(todo)\n\n            expect(store.instance).toBe(todo)\n        })\n\n        test(\"using the non processed type in place of the processed one works\", () => {\n            const store = Store.create()\n            const todo = Todo.create({ id: \"map\" })\n\n            store.setInstance(todo)\n\n            expect(store.instance).toBe(todo)\n        })\n\n        test(\"using the processed type in place of the non processed one works\", () => {\n            const store = types\n                .model(\"Store\", { instance: Todo })\n                .actions(self => ({\n                    setInstance(next: { id: string }) {\n                        self.instance = next\n                    }\n                }))\n                .create({ instance: { id: \"new\" } })\n\n            const todo = TodoWithProcessor.create({ id: \"map\" })\n\n            store.setInstance(todo)\n\n            expect(store.instance).toBe(todo)\n        })\n    })\n\n    test(\"cached initial snapshots are ok\", () => {\n        const M2 = types.snapshotProcessor(types.model({ x: types.number }), {\n            preProcessor(sn: { x: number }) {\n                return { ...sn, x: 0 }\n            }\n        })\n        const M1 = types.model({ m2: M2 })\n        const M = types.model({ m1: M1 })\n\n        const m = M.create({ m1: { m2: { x: 10 } } })\n        expect(getSnapshot(m)).toEqual({\n            m1: { m2: { x: 0 } }\n        })\n    })\n\n    test(\"works with IType.is\", () => {\n        const Model = types.model({ x: types.number })\n        const model = Model.create({ x: 1 })\n        expect(Model.is(model)).toBe(true)\n        expect(Model.is({ x: 1 })).toBe(true)\n\n        const ProcessedModel = types.snapshotProcessor(Model, {\n            preProcessor(sn: { y: number }) {\n                const copy = { ...sn, x: sn.y }\n                // @ts-ignore\n                delete copy.y\n                return copy\n            },\n            postProcessor(sn: { x: number }) {\n                const copy = { ...sn, y: sn.x }\n                // @ts-ignore\n                delete copy.x\n                return copy\n            }\n        })\n\n        const processedModel = ProcessedModel.create({ y: 1 })\n        expect(ProcessedModel.is(processedModel)).toBe(true)\n        expect(ProcessedModel.is({ y: 1 })).toBe(true)\n        expect(ProcessedModel.is(Model)).toBe(false)\n    })\n\n    test(\".is checks instances against the underlying type\", () => {\n        const ModelA = types.model({ x: types.number })\n        const ModelB = types.model({ x: types.number })\n        const modelA = ModelA.create({ x: 1 })\n        const modelB = ModelB.create({ x: 2 })\n\n        // despite having the same snapshot type, .is is false for the instance of one against the other because they are not the same type\n        expect(ModelA.is(modelA)).toBe(true)\n        expect(ModelB.is(modelA)).toBe(false)\n        expect(ModelA.is(modelB)).toBe(false)\n        expect(ModelB.is(modelB)).toBe(true)\n\n        const ProcessedModel = types.snapshotProcessor(ModelA, {})\n\n        const processedModel = ProcessedModel.create({ x: 3 })\n        expect(ProcessedModel.is(processedModel)).toBe(true)\n        expect(ModelA.is(processedModel)).toBe(true)\n        expect(ModelB.is(processedModel)).toBe(false)\n        expect(ProcessedModel.is(modelA)).toBe(true)\n        expect(ProcessedModel.is(modelB)).toBe(false)\n    })\n\n    describe(\"1776 - reconciliation in an array\", () => {\n        test(\"model with transformed property is reconciled\", () => {\n            const SP = types.snapshotProcessor(\n                types.model({\n                    id: types.identifier,\n                    x: types.number\n                }),\n                {\n                    preProcessor(sn: { id: string; y: number }) {\n                        if (\"x\" in sn) {\n                            // Ensure snapshot don't run through preprocessor twice\n                            throw new Error(\"sn has already been preprocessed\")\n                        }\n                        return { id: sn.id, x: sn.y }\n                    }\n                }\n            )\n            const Store = types.model({ items: types.array(SP) }).actions(self => ({\n                setItems(items: SnapshotIn<typeof SP>[]) {\n                    self.items = cast(items)\n                }\n            }))\n            const store = Store.create({ items: [{ id: \"1\", y: 0 }] })\n            const oldNodeId = getNodeId(store.items[0])\n            store.setItems([{ id: \"1\", y: 1 }])\n            expect(getNodeId(store.items[0])).toBe(oldNodeId)\n        })\n\n        test(\"model with transformed identifier attribute is reconciled\", () => {\n            const SP = types.snapshotProcessor(\n                types.model({\n                    id: types.identifier\n                }),\n                {\n                    preProcessor(sn: { foo: string }) {\n                        return { id: sn.foo }\n                    }\n                }\n            )\n            const Store = types.model({ items: types.array(SP) }).actions(self => ({\n                setItems(items: SnapshotIn<typeof SP>[]) {\n                    self.items = cast(items)\n                }\n            }))\n            const store = Store.create({ items: [{ foo: \"1\" }] })\n            const oldNodeId = getNodeId(store.items[0])\n            store.setItems([{ foo: \"1\" }])\n            expect(getNodeId(store.items[0])).toBe(oldNodeId)\n        })\n    })\n\n    describe(\"single node reconcilication\", () => {\n        test(\"model with transformed property is reconciled\", () => {\n            const SP = types.snapshotProcessor(\n                types.model({\n                    id: types.identifier,\n                    x: types.number\n                }),\n                {\n                    preProcessor(sn: { id: string; y: number }) {\n                        if (\"x\" in sn) {\n                            // Ensure snapshot don't run through preprocessor twice\n                            throw new Error(\"sn has already been preprocessed\")\n                        }\n                        return { id: sn.id, x: sn.y }\n                    }\n                }\n            )\n            const Store = types.model({ item: SP }).actions(self => ({\n                setItem(item: SnapshotIn<typeof SP>) {\n                    self.item = cast(item)\n                }\n            }))\n            const store = Store.create({ item: { id: \"1\", y: 0 } })\n            const oldNodeId = getNodeId(store.item)\n            store.setItem({ id: \"1\", y: 1 })\n            expect(getNodeId(store.item)).toBe(oldNodeId)\n            expect(store.item.x).toBe(1)\n        })\n\n        test(\"model with transformed identifier property is reconciled\", () => {\n            const SP = types.snapshotProcessor(\n                types.model({\n                    id: types.identifier\n                }),\n                {\n                    preProcessor(sn: { foo: string }) {\n                        return { id: sn.foo }\n                    }\n                }\n            )\n            const Store = types.model({ item: SP }).actions(self => ({\n                setItem(item: SnapshotIn<typeof SP>) {\n                    self.item = cast(item)\n                }\n            }))\n            const store = Store.create({ item: { foo: \"1\" } })\n            const oldNodeId = getNodeId(store.item)\n            store.setItem({ foo: \"1\" })\n            expect(getNodeId(store.item)).toBe(oldNodeId)\n            expect(store.item.id).toBe(\"1\")\n        })\n\n        test(\"1791 - model wrapped with maybe is reconciled\", () => {\n            const SP = types.snapshotProcessor(\n                types.model({\n                    id: types.identifier,\n                    x: types.number\n                }),\n                {\n                    preProcessor(sn: { id: string; y: number }) {\n                        return { id: sn.id, x: sn.y }\n                    }\n                }\n            )\n            const Store = types.model({ item: types.maybe(SP) }).actions(self => ({\n                setItem(item: SnapshotIn<typeof SP>) {\n                    self.item = cast(item)\n                }\n            }))\n            const store = Store.create({ item: { id: \"1\", y: 0 } })\n            const oldNodeId = getNodeId(store.item!)\n            store.setItem({ id: \"1\", y: 1 })\n            expect(getNodeId(store.item!)).toBe(oldNodeId)\n            expect(store.item?.x).toBe(1)\n        })\n\n        test(\"model wrapped with optional is reconciled\", () => {\n            const SP = types.snapshotProcessor(\n                types.model({\n                    id: types.identifier,\n                    x: types.number\n                }),\n                {\n                    preProcessor(sn: { id: string; y: number }) {\n                        return { id: sn.id, x: sn.y }\n                    }\n                }\n            )\n            const Store = types\n                .model({ item: types.optional(SP, { id: \"1\", y: 0 }) })\n                .actions(self => ({\n                    setItem(item?: SnapshotIn<typeof SP>) {\n                        self.item = cast(item)\n                    }\n                }))\n            const store = Store.create()\n            const oldNodeId = getNodeId(store.item!)\n            expect(store.item?.x).toBe(0)\n            store.setItem({ id: \"1\", y: 1 })\n            expect(getNodeId(store.item!)).toBe(oldNodeId)\n            expect(store.item?.x).toBe(1)\n            store.setItem(undefined)\n            expect(getNodeId(store.item!)).toBe(oldNodeId)\n            expect(store.item?.x).toBe(0)\n        })\n    })\n\n    test(\"1777 - preProcessor wrapped in maybe accepts undefined\", () => {\n        const SP = types.snapshotProcessor(\n            types.model({\n                id: types.identifier,\n                x: types.number\n            }),\n            {\n                preProcessor(sn: { id: string; y: number }) {\n                    return { id: sn.id, x: sn.y }\n                }\n            }\n        )\n        const Store = types.model({ item: types.maybe(SP) }).actions(self => ({\n            setItem(item?: SnapshotIn<typeof SP>) {\n                self.item = cast(item)\n            }\n        }))\n        const store = Store.create()\n        expect(store.item).toBeUndefined()\n        store.setItem({ id: \"1\", y: 1 })\n        expect(store.item?.x).toBe(1)\n        store.setItem(undefined)\n        expect(store.item).toBeUndefined()\n    })\n\n    test(\"1849 - Wrapped unions don't cause infinite recursion\", () => {\n        const Store = types\n            .model({\n                prop: types.optional(\n                    types.snapshotProcessor(\n                        types.union(types.literal(\"a\"), types.literal(\"b\")),\n                        {}\n                    ),\n                    \"a\"\n                )\n            })\n            .actions(self => ({\n                setProp(prop: typeof self.prop) {\n                    self.prop = prop\n                }\n            }))\n\n        const store = Store.create()\n        expect(store.prop).toBe(\"a\")\n        expect(() => store.setProp(\"b\")).not.toThrow()\n        expect(store.prop).toBe(\"b\")\n    })\n\n    if (process.env.NODE_ENV !== \"production\") {\n        test(\"it should fail if given incorrect processor\", () => {\n            expect(() => {\n                types.model({\n                    m: types.snapshotProcessor(types.number, {\n                        postProcessor: {} as any\n                    })\n                })\n            }).toThrow(\"[mobx-state-tree] postSnapshotProcessor must be a function\")\n        })\n    }\n})\n"
  },
  {
    "path": "__tests__/core/string.test.ts",
    "content": "import { types } from \"../../src\"\nimport { Hook, NodeLifeCycle } from \"../../src/internal\"\nimport { describe, expect, it, test } from \"bun:test\"\n\ndescribe(\"types.string\", () => {\n    describe(\"methods\", () => {\n        describe(\"create\", () => {\n            describe(\"with no arguments\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    it(\"should throw an error in development\", () => {\n                        expect(() => {\n                            types.string.create()\n                        }).toThrow()\n                    })\n                }\n            })\n            describe(\"with a string argument\", () => {\n                it(\"should return a string\", () => {\n                    const s = types.string.create(\"foo\")\n                    expect(typeof s).toBe(\"string\")\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    1,\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error(),\n                    NaN,\n                    Infinity\n                ]\n\n                if (process.env.NODE_ENV !== \"production\") {\n                    testCases.forEach(testCase => {\n                        it(`should throw an error when passed ${JSON.stringify(testCase)}`, () => {\n                            expect(() => {\n                                types.string.create(testCase as any)\n                            }).toThrow()\n                        })\n                    })\n                }\n            })\n        })\n        describe(\"describe\", () => {\n            it(\"should return the value 'string'\", () => {\n                const description = types.string.describe()\n                expect(description).toBe(\"string\")\n            })\n        })\n        describe(\"getSnapshot\", () => {\n            it(\"should return the value passed in\", () => {\n                const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                const snapshot = types.string.getSnapshot(s)\n                expect(snapshot).toBe(\"foo\")\n            })\n        })\n        describe(\"getSubtype\", () => {\n            it(\"should return null\", () => {\n                const subtype = types.string.getSubTypes()\n                expect(subtype).toBe(null)\n            })\n        })\n        describe(\"instantiate\", () => {\n            if (process.env.NODE_ENV !== \"production\") {\n                describe(\"with invalid arguments\", () => {\n                    it(\"should not throw an error\", () => {\n                        expect(() => {\n                            // @ts-ignore\n                            types.string.instantiate()\n                        }).not.toThrow()\n                    })\n                })\n            }\n            describe(\"with a string argument\", () => {\n                it(\"should return an object\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    expect(typeof s).toBe(\"object\")\n                })\n            })\n        })\n        describe(\"is\", () => {\n            describe(\"with a string argument\", () => {\n                it(\"should return true\", () => {\n                    const result = types.string.is(\"foo\")\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    1,\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error(),\n                    NaN,\n                    Infinity\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = types.string.is(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        describe(\"isAssignableFrom\", () => {\n            describe(\"with a string argument\", () => {\n                it(\"should return true\", () => {\n                    const result = types.string.isAssignableFrom(types.string)\n                    expect(result).toBe(true)\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    types.Date,\n                    types.boolean,\n                    types.finite,\n                    types.float,\n                    types.identifier,\n                    types.identifierNumber,\n                    types.integer,\n                    types.null,\n                    types.number,\n                    types.undefined\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return false when passed ${JSON.stringify(testCase)}`, () => {\n                        const result = types.string.isAssignableFrom(testCase as any)\n                        expect(result).toBe(false)\n                    })\n                })\n            })\n        })\n        // TODO: we need to test this, but to be honest I'm not sure what the expected behavior is on single string nodes.\n        describe.skip(\"reconcile\", () => {})\n        describe(\"validate\", () => {\n            describe(\"with a string argument\", () => {\n                it(\"should return with no validation errors\", () => {\n                    const result = types.string.validate(\"foo\", [])\n                    expect(result).toEqual([])\n                })\n            })\n            describe(\"with argument of different types\", () => {\n                const testCases = [\n                    null,\n                    undefined,\n                    1,\n                    true,\n                    [],\n                    function () {},\n                    new Date(),\n                    /a/,\n                    new Map(),\n                    new Set(),\n                    Symbol(),\n                    new Error(),\n                    NaN,\n                    Infinity\n                ]\n\n                testCases.forEach(testCase => {\n                    it(`should return with a validation error when passed ${JSON.stringify(\n                        testCase\n                    )}`, () => {\n                        const result = types.string.validate(testCase as any, [])\n                        expect(result).toEqual([\n                            {\n                                context: [],\n                                message: \"Value is not a string\",\n                                value: testCase\n                            }\n                        ])\n                    })\n                })\n            })\n        })\n    })\n    describe(\"properties\", () => {\n        describe(\"flags\", () => {\n            test(\"return the correct value\", () => {\n                const flags = types.string.flags\n                expect(flags).toBe(1)\n            })\n        })\n        describe(\"identifierAttribute\", () => {\n            // We don't have a way to set the identifierAttribute on a primitive type, so this should return undefined.\n            test(\"returns undefined\", () => {\n                const identifierAttribute = types.string.identifierAttribute\n                expect(identifierAttribute).toBeUndefined()\n            })\n        })\n        describe(\"isType\", () => {\n            test(\"returns true\", () => {\n                const isType = types.string.isType\n                expect(isType).toBe(true)\n            })\n        })\n        describe(\"name\", () => {\n            test('returns \"string\"', () => {\n                const name = types.string.name\n                expect(name).toBe(\"string\")\n            })\n        })\n    })\n    describe(\"instance\", () => {\n        describe(\"methods\", () => {\n            describe(\"aboutToDie\", () => {\n                it(\"calls the beforeDetach hook\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    let called = false\n                    s.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n                    s.aboutToDie()\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"die\", () => {\n                it(\"kills the node\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    s.die()\n                    expect(s.isAlive).toBe(false)\n                })\n                it(\"should mark the node as dead\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    s.die()\n                    expect(s.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"finalizeCreation\", () => {\n                it(\"should mark the node as finalized\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    s.finalizeCreation()\n                    expect(s.state).toBe(NodeLifeCycle.FINALIZED)\n                })\n            })\n            describe(\"finalizeDeath\", () => {\n                it(\"should mark the node as dead\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    s.finalizeDeath()\n                    expect(s.state).toBe(NodeLifeCycle.DEAD)\n                })\n            })\n            describe(\"getReconciliationType\", () => {\n                it(\"should return the correct type\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    const type = s.getReconciliationType()\n                    expect(type).toBe(types.string)\n                })\n            })\n            describe(\"getSnapshot\", () => {\n                it(\"should return the value passed in\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    const snapshot = s.getSnapshot()\n                    expect(snapshot).toBe(\"foo\")\n                })\n            })\n            describe(\"registerHook\", () => {\n                it(\"should register a hook and call it\", () => {\n                    const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                    let called = false\n                    s.registerHook(Hook.beforeDestroy, () => {\n                        called = true\n                    })\n\n                    s.die()\n\n                    expect(called).toBe(true)\n                })\n            })\n            describe(\"setParent\", () => {\n                if (process.env.NODE_ENV !== \"production\") {\n                    describe(\"with null\", () => {\n                        it(\"should throw an error\", () => {\n                            const s = types.string.instantiate(null, \"\", {}, \"foo\")\n                            expect(() => {\n                                s.setParent(null, \"foo\")\n                            }).toThrow()\n                        })\n                    })\n                    describe(\"with a parent object\", () => {\n                        it(\"should throw an error\", () => {\n                            const Parent = types.model({\n                                child: types.string\n                            })\n\n                            const parent = Parent.create({ child: \"foo\" })\n\n                            const s = types.string.instantiate(null, \"\", {}, \"bar\")\n\n                            expect(() => {\n                                // @ts-ignore\n                                s.setParent(parent, \"bar\")\n                            }).toThrow(\n                                \"[mobx-state-tree] assertion failed: scalar nodes cannot change their parent\"\n                            )\n                        })\n                    })\n                }\n            })\n        })\n    })\n})\n"
  },
  {
    "path": "__tests__/core/this.test.ts",
    "content": "import { types } from \"../../src\"\nimport { isObservableProp, isComputedProp } from \"mobx\"\nimport { expect, test } from \"bun:test\"\n\n// MWE: disabled test, `this` isn't supposed to work, and afaik nowhere advertised\ntest.skip(\"this support\", () => {\n    const M = types\n        .model({ x: 5 })\n        .views(self => ({\n            get x2() {\n                return self.x * 2\n            },\n            get x4() {\n                return this.x2 * 2\n            },\n            boundTo() {\n                return this\n            },\n            innerBoundTo() {\n                return () => this\n            },\n            isThisObservable() {\n                return (\n                    isObservableProp(this, \"x2\") &&\n                    isObservableProp(this, \"x4\") &&\n                    isObservableProp(this, \"localState\") &&\n                    isComputedProp(this, \"x2\")\n                )\n            }\n        }))\n        .volatile(self => ({\n            localState: 3,\n            getLocalState() {\n                return this.localState\n            },\n            getLocalState2() {\n                return this.getLocalState() * 2\n            }\n        }))\n\n        .actions(self => {\n            return {\n                xBy(by: number) {\n                    return self.x * by\n                },\n                setX(x: number) {\n                    self.x = x\n                },\n                setThisX(x: number) {\n                    ;(this as any).x = x // this should not affect self.x\n                },\n                setXBy(x: number) {\n                    this.setX(this.xBy(x))\n                },\n                setLocalState(x: number) {\n                    self.localState = x\n                }\n            }\n        })\n\n    const mi = M.create()\n\n    expect(mi.isThisObservable()).toBe(true)\n\n    expect(mi.boundTo()).toBe(mi)\n    expect(mi.innerBoundTo()()).toBe(mi)\n\n    expect(mi.x).toBe(5)\n\n    mi.setX(6)\n    expect(mi.x).toBe(6)\n\n    mi.setXBy(2)\n    expect(mi.x).toBe(12)\n    expect(mi.x2).toBe(12 * 2)\n    expect(mi.x4).toBe(12 * 4)\n    expect(mi.xBy(2)).toBe(24)\n\n    expect(mi.localState).toBe(3)\n    expect(mi.getLocalState()).toBe(3)\n    expect(mi.getLocalState2()).toBe(3 * 2)\n\n    mi.setLocalState(6)\n    expect(mi.localState).toBe(6)\n    expect(mi.getLocalState()).toBe(6)\n    expect(mi.getLocalState2()).toBe(6 * 2)\n\n    mi.setLocalState(7)\n    expect(mi.localState).toBe(7)\n\n    // make sure attempts to modify this (as long as it is not an action) doesn't affect self\n    const oldX = mi.x\n    mi.setThisX(oldX + 1)\n    expect(mi.x).toBe(oldX)\n})\n"
  },
  {
    "path": "__tests__/core/type-system.test.ts",
    "content": "import { describe, expect, test } from \"bun:test\"\nimport {\n    IAnyType,\n    IType,\n    Instance,\n    ModelPrimitive,\n    ModelPropertiesDeclaration,\n    SnapshotIn,\n    SnapshotOrInstance,\n    SnapshotOut,\n    TypeOfValue,\n    cast,\n    castToSnapshot,\n    getParent,\n    getRoot,\n    getSnapshot,\n    isArrayType,\n    isFrozenType,\n    isIdentifierType,\n    isLateType,\n    isLiteralType,\n    isMapType,\n    isModelType,\n    isOptionalType,\n    isPrimitiveType,\n    isReferenceType,\n    isRefinementType,\n    isStateTreeNode,\n    isUnionType,\n    types,\n    unprotect,\n    type IOptionalIType,\n    type ISimpleType\n} from \"../../src\"\nimport type {\n    DatePrimitive,\n    IAnyComplexType,\n    IAnyModelType,\n    IArrayType,\n    IMapType,\n    IReferenceType,\n    IUnionType\n} from \"../../src/internal\"\n\ntype DifferingKeys<ActualT, ExpectedT> = {\n    [K in keyof ActualT | keyof ExpectedT]: K extends keyof ActualT\n        ? K extends keyof ExpectedT\n            ? Exact<ActualT[K], ExpectedT[K]> extends never\n                ? K\n                : never\n            : K\n        : K\n}[keyof ActualT | keyof ExpectedT] &\n    string\n\ntype NotExactErrorMessage<ActualT, ExpectedT> =\n    ActualT extends Record<string, any>\n        ? ExpectedT extends Record<string, any>\n            ? `Mismatched property: ${DifferingKeys<ActualT, ExpectedT>}`\n            : \"Expected a non-object type, but received an object\"\n        : ExpectedT extends Record<string, any>\n          ? \"Expected an object type, but received a non-object type\"\n          : \"Types are not exactly equal\"\n\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? A : never) : never\n\nconst assertTypesEqual = <ActualT, ExpectedT>(\n    t: ActualT,\n    u: Exact<ActualT, ExpectedT> extends never\n        ? NotExactErrorMessage<ActualT, ExpectedT>\n        : ExpectedT\n): [ActualT, ExpectedT] => [t, u] as any\n\nconst _: unknown = undefined\n\nconst createTestFactories = () => {\n    const Box = types.model({\n        width: 0,\n        height: 0\n    })\n    const Square = types.model({\n        width: 0,\n        height: 0\n    })\n    const Cube = types.model({\n        width: 0,\n        height: 0,\n        depth: 0\n    })\n    return { Box, Square, Cube }\n}\ntest(\"it should recognize a valid snapshot\", () => {\n    const { Box } = createTestFactories()\n    expect(Box.is({ width: 1, height: 2 })).toEqual(true)\n    expect(Box.is({ width: 1, height: 2, depth: 3 })).toEqual(true)\n})\ntest(\"it should recognize an invalid snapshot\", () => {\n    const { Box } = createTestFactories()\n    expect(Box.is({ width: \"1\", height: \"2\" })).toEqual(false)\n})\ntest(\"it should check valid nodes as well\", () => {\n    const { Box } = createTestFactories()\n    const doc = Box.create()\n    expect(Box.is(doc)).toEqual(true)\n})\ntest(\"it should check invalid nodes as well\", () => {\n    const { Box } = createTestFactories()\n    const doc = Box.create()\n    expect(\n        types\n            .model({\n                anotherAttr: types.number\n            })\n            .is(doc)\n    ).toEqual(false)\n})\ntest(\"it should do typescript type inference correctly\", () => {\n    const A = types\n        .model({\n            x: types.number,\n            y: types.maybeNull(types.string)\n        })\n        .views(self => ({\n            get z(): string {\n                return \"hi\"\n            }\n        }))\n        .actions(self => {\n            function method() {\n                const x: string = self.z + self.x + self.y\n                anotherMethod(x)\n            }\n            function anotherMethod(x: string) {}\n            return {\n                method,\n                anotherMethod\n            }\n        })\n    // factory is invokable\n    const a = A.create({ x: 2, y: \"7\" })\n    unprotect(a)\n    // property can be used as proper type\n    const z: number = a.x\n    // property can be assigned to correctly\n    a.x = 7\n    // wrong type cannot be assigned\n    // MANUAL TEST: not ok: a.x = \"stuff\"\n    // sub factories work\n    const B = types.model({\n        sub: types.maybe(A)\n    })\n    const b = B.create()\n    unprotect(b)\n    // sub fields can be reassigned\n    b.sub = A.create({\n        // MANUAL TEST not ok: z: 4,\n        x: 3\n    })\n    // sub fields have proper type\n    b.sub.x = 4\n    const d: string | null = b.sub.y\n    a.y = null\n    const zz: string = a.z\n    // Manual test not assignable:\n    // a.z = \"test\"\n    b.sub.method()\n    expect(true).toBe(true) // suppress no asserts warning\n    // snapshots are of the proper type\n    const snapshot = getSnapshot(a)\n    const sx: number = snapshot.x\n    const sy: string | null = snapshot.y\n    expect(sx).toBe(7)\n    expect(sy).toBe(null)\n})\ntest(\"#66 - it should accept superfluous fields\", () => {\n    const Item = types.model({\n        id: types.number,\n        name: types.string\n    })\n    expect(Item.is({})).toBe(false)\n    expect(Item.is({ id: 3 })).toBe(false)\n    expect(Item.is({ id: 3, name: \"\" })).toBe(true)\n    expect(Item.is({ id: 3, name: \"\", description: \"\" })).toBe(true)\n    const a = Item.create({ id: 3, name: \"\", description: \"bla\" } as any)\n    expect((a as any).description).toBe(undefined)\n})\ntest(\"#66 - it should not require defaulted fields\", () => {\n    const Item = types.model({\n        id: types.number,\n        name: types.optional(types.string, \"boo\")\n    })\n    expect(Item.is({})).toBe(false)\n    expect(Item.is({ id: 3 })).toBe(true)\n    expect(Item.is({ id: 3, name: \"\" })).toBe(true)\n    expect(Item.is({ id: 3, name: \"\", description: \"\" })).toBe(true)\n    const a = Item.create({ id: 3, description: \"bla\" } as any)\n    expect((a as any).description).toBe(undefined)\n    expect(a.name).toBe(\"boo\")\n})\ntest(\"#66 - it should be possible to omit defaulted fields\", () => {\n    const Item = types.model({\n        id: types.number,\n        name: \"boo\"\n    })\n    expect(Item.is({})).toBe(false)\n    expect(Item.is({ id: 3 })).toBe(true)\n    expect(Item.is({ id: 3, name: \"\" })).toBe(true)\n    expect(Item.is({ id: 3, name: \"\", description: \"\" })).toBe(true)\n    const a = Item.create({ id: 3, description: \"bla\" } as any)\n    expect((a as any).description).toBe(undefined)\n    expect(a.name).toBe(\"boo\")\n})\ntest(\"#66 - it should pick the correct type of defaulted fields\", () => {\n    const Item = types.model({\n        id: types.number,\n        name: \"boo\"\n    })\n    const a = Item.create({ id: 3 })\n    unprotect(a)\n    expect(a.name).toBe(\"boo\")\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => ((a as any).name = 3)).toThrow(\n            `[mobx-state-tree] Error while converting \\`3\\` to \\`string\\`:\\n\\n    value \\`3\\` is not assignable to type: \\`string\\` (Value is not a string).`\n        )\n    }\n})\ntest(\"cannot create factories with null values\", () => {\n    expect(() =>\n        types.model({\n            x: null\n        } as any)\n    ).toThrow()\n})\ntest(\"can create factories with maybe primitives\", () => {\n    const F = types.model({\n        x: types.maybeNull(types.string)\n    })\n    expect(F.is(undefined)).toBe(false)\n    expect(F.is({})).toBe(true)\n    expect(F.is({ x: null })).toBe(true)\n    expect(F.is({ x: \"test\" })).toBe(true)\n    expect(F.is({ x: 3 })).toBe(false)\n    expect(F.create().x).toBe(null)\n    expect(F.create({ x: undefined }).x).toBe(null)\n    expect(F.create({ x: \"\" }).x).toBe(\"\")\n    expect(F.create({ x: \"3\" }).x).toBe(\"3\")\n})\ntest(\"it is possible to refer to a type\", () => {\n    const Todo = types\n        .model({\n            title: types.string\n        })\n        .actions(self => {\n            function setTitle(v: string) {}\n            return {\n                setTitle\n            }\n        })\n    function x(): typeof Todo.Type {\n        return Todo.create({ title: \"test\" })\n    }\n    const z = x()\n    unprotect(z)\n    z.setTitle(\"bla\")\n    z.title = \"bla\"\n    // z.title = 3 // Test manual: should give compile error\n    expect(true).toBe(true) // suppress no asserts warning\n})\ntest(\".Type should not be callable\", () => {\n    const Todo = types\n        .model({\n            title: types.string\n        })\n        .actions(self => {\n            function setTitle(v: string) {}\n            return {\n                setTitle\n            }\n        })\n    expect(() => Todo.Type).toThrow()\n})\ntest(\".SnapshotType should not be callable\", () => {\n    const Todo = types\n        .model({\n            title: types.string\n        })\n        .actions(self => {\n            function setTitle(v: string) {}\n            return {\n                setTitle\n            }\n        })\n    expect(() => Todo.SnapshotType).toThrow()\n})\ntest(\"types instances with compatible snapshots should not be interchangeable\", () => {\n    const A = types.model(\"A\", {}).actions(self => {\n        function doA() {}\n        return {\n            doA\n        }\n    })\n    const B = types.model(\"B\", {}).actions(self => {\n        function doB() {}\n        return {\n            doB\n        }\n    })\n    const C = types.model(\"C\", {\n        x: types.maybe(A)\n    })\n    expect(A.is({})).toBe(true)\n    expect(A.is(B.create())).toBe(false) // if this yielded true, then `B.create().doA()` should work!\n    expect(A.is(getSnapshot(B.create()))).toBe(true)\n    const c = C.create()\n    unprotect(c)\n    expect(() => {\n        c.x = undefined\n    }).not.toThrow()\n    expect(() => {\n        c.x = cast({})\n    }).not.toThrow()\n    expect(() => {\n        c.x = A.create()\n    }).not.toThrow()\n    expect(() => {\n        ;(c as any).x = B.create()\n    }).toThrow()\n})\ntest(\"it handles complex types correctly\", () => {\n    const Todo = types\n        .model({\n            title: types.string\n        })\n        .actions(self => {\n            function setTitle(v: string) {}\n            return {\n                setTitle\n            }\n        })\n    const Store = types\n        .model({\n            todos: types.map(Todo)\n        })\n        .views(self => {\n            function getActualAmount() {\n                return self.todos.size\n            }\n            return {\n                get amount() {\n                    return getActualAmount()\n                },\n                getAmount(): number {\n                    return self.todos.size + getActualAmount()\n                }\n            }\n        })\n        .actions(self => {\n            function setAmount() {\n                const x: number = self.todos.size + self.amount + self.getAmount()\n            }\n            return {\n                setAmount\n            }\n        })\n    expect(true).toBe(true) // suppress no asserts warning\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should provide detailed reasons why the value is not applicable\", () => {\n        const Todo = types\n            .model({\n                title: types.string\n            })\n            .actions(self => {\n                function setTitle(v: string) {}\n                return {\n                    setTitle\n                }\n            })\n        const Store = types\n            .model({\n                todos: types.map(Todo)\n            })\n            .views(self => ({\n                get amount() {\n                    return self.todos.size\n                },\n                getAmount(): number {\n                    return self.todos.size + self.todos.size\n                }\n            }))\n            .actions(self => {\n                function setAmount() {\n                    const x: number = self.todos.size + self.amount + self.getAmount()\n                }\n                return {\n                    setAmount\n                }\n            })\n        expect(() =>\n            Store.create({\n                todos: { \"1\": { title: true, setTitle: \"hello\" } },\n                amount: 1,\n                getAmount: \"hello\"\n            } as any)\n        ).toThrow(\n            // MWE: TODO: Ideally (like in MST =< 0.9):\n            // at path \"/todos/1/setTitle\" value \\`\"hello\"\\` is not assignable  (Action properties should not be provided in the snapshot).\n            // at path \"/amount\" value \\`1\\` is not assignable  (Computed properties should not be provided in the snapshot).\n            // at path \"/getAmount\" value \\`\"hello\"\\` is not assignable  (View properties should not be provided in the snapshot).`\n            `[mobx-state-tree] Error while converting \\`{\"todos\":{\"1\":{\"title\":true,\"setTitle\":\"hello\"}},\"amount\":1,\"getAmount\":\"hello\"}\\` to \\`AnonymousModel\\`:\n\n    at path \"/todos/1/title\" value \\`true\\` is not assignable to type: \\`string\\` (Value is not a string).`\n        )\n    })\n}\ntest(\"it should type compose correctly\", () => {\n    const Car = types\n        .model({\n            wheels: 3\n        })\n        .actions(self => {\n            let connection = null as any as Promise<any>\n            function drive() {}\n            function afterCreate() {\n                connection = Promise.resolve(true)\n            }\n            return {\n                drive,\n                afterCreate\n            }\n        })\n    const Logger = types\n        .model({\n            logNode: \"test\"\n        })\n        .actions(self => {\n            function log(msg: string) {}\n            return {\n                log\n            }\n        })\n    const LoggableCar = types.compose(Car, Logger)\n    const x = LoggableCar.create({ wheels: 3, logNode: \"test\" /* compile error: x: 7  */ })\n    // x.test() // compile error\n    x.drive()\n    x.log(\"z\")\n})\ntest(\"it should extend {pre,post}ProcessSnapshot on compose\", () => {\n    const CompositionTracker = types\n        .model({\n            composedOf: types.array(types.string),\n            composedWith: types.array(types.string)\n        })\n        .preProcessSnapshot(snapshot => ({\n            ...snapshot,\n            composedOf: (snapshot.composedOf || []).concat(\"CompositionTracker\")\n        }))\n        .postProcessSnapshot(snapshot => ({\n            ...snapshot,\n            composedWith: (snapshot.composedWith || []).concat(\"WagonTracker\")\n        }))\n    const Car = types\n        .model({})\n        .preProcessSnapshot(snapshot => ({\n            ...snapshot,\n            composedOf: ((snapshot as any).composedOf || []).concat(\"Car\")\n        }))\n        .postProcessSnapshot(snapshot => ({\n            ...snapshot,\n            composedWith: ((snapshot as any).composedWith || []).concat(\"Wagon\")\n        }))\n    const Logger = types\n        .model({})\n        .preProcessSnapshot(snapshot => ({\n            ...snapshot,\n            composedOf: ((snapshot as any).composedOf || []).concat(\"CarLogger\")\n        }))\n        .postProcessSnapshot(snapshot => ({\n            ...snapshot,\n            composedWith: ((snapshot as any).composedWith || []).concat(\"WagonLogger\")\n        }))\n\n    const LoggableCar = types.compose(CompositionTracker, Car, Logger).props({\n        composedOf: types.array(types.string),\n        composedWith: types.array(types.string)\n    })\n    const x = LoggableCar.create({})\n    expect(x.composedOf).toContain(\"CompositionTracker\")\n    expect(x.composedOf).toContain(\"Car\")\n    expect(x.composedOf).toContain(\"CarLogger\")\n    expect(x.composedOf.toJSON()).toEqual([\"CompositionTracker\", \"Car\", \"CarLogger\"])\n    expect(getSnapshot(x).composedWith).toContain(\"WagonTracker\")\n    expect(getSnapshot(x).composedWith).toContain(\"Wagon\")\n    expect(getSnapshot(x).composedWith).toContain(\"WagonLogger\")\n    expect(getSnapshot(x).composedWith).toEqual([\"WagonTracker\", \"Wagon\", \"WagonLogger\"])\n})\ntest(\"it should extend types correctly\", () => {\n    const Car = types\n        .model({\n            wheels: 3\n        })\n        .actions(self => {\n            function drive() {}\n            return {\n                drive\n            }\n        })\n    const Logger = types\n        .model(\"Logger\")\n        .props({\n            logNode: \"test\"\n        })\n        .actions(self => {\n            let connection: Promise<any>\n            return {\n                log(msg: string) {},\n                afterCreate() {\n                    connection = Promise.resolve(true)\n                }\n            }\n        })\n    const LoggableCar = types.compose(\"LoggableCar\", Car, Logger)\n    const x = LoggableCar.create({ wheels: 3, logNode: \"test\" /* compile error: x: 7  */ })\n    // x.test() // compile error\n    x.drive()\n    x.log(\"z\")\n})\ntest(\"self referring views\", () => {\n    const Car = types.model({ x: 3 }).views(self => {\n        const views = {\n            get triple() {\n                return self.x + views.double\n            },\n            get double() {\n                return self.x * 2\n            }\n        }\n        return views\n    })\n    expect(Car.create().triple).toBe(9)\n})\n\ntest(\"#922\", () => {\n    expect(() => {\n        const Stateable = types.model(\"Statable\", {\n            state: types.optional(\n                types.enumeration(\"state\", [\"initalized\", \"pending\", \"done\", \"error\"]),\n                \"initalized\"\n            )\n        })\n\n        const Client = types.model(\"Client\", {\n            id: types.identifierNumber,\n            name: types.string\n        })\n\n        const UserClientList = types.compose(\n            \"UserClientList\",\n            Stateable,\n            types.model({\n                items: types.array(Client),\n                month: types.optional(types.Date, () => {\n                    return new Date()\n                })\n            })\n        )\n\n        const NonExtendedUserClientList = types.model(\"NonExtendedUserClientList\", {\n            items: types.array(Client),\n            month: types.optional(types.Date, () => {\n                return new Date()\n            }),\n            state: types.optional(\n                types.enumeration(\"state\", [\"initalized\", \"pending\", \"done\", \"error\"]),\n                \"initalized\"\n            )\n        })\n\n        const User = types.model(\"User\", {\n            name: types.string,\n            clients: types.optional(UserClientList, () => UserClientList.create({}))\n        })\n\n        const NonExtendedUser = types.model(\"User\", {\n            name: types.string,\n            clients: types.optional(NonExtendedUserClientList, () =>\n                NonExtendedUserClientList.create({})\n            )\n        })\n\n        const you = NonExtendedUser.create({\n            name: \"you\"\n        })\n\n        const me = User.create({\n            name: \"me\"\n        })\n    }).not.toThrow()\n})\n\ntest(\"#922 - 2\", () => {\n    expect(() => {\n        types.optional(types.enumeration(\"state\", [\"init\", \"pending\", \"done\", \"error\"]), \"init\")\n    }).not.toThrow()\n})\n\ntest(\"#932\", () => {\n    interface MyInterface {\n        test: string\n    }\n\n    const MyModel = types.model(\"MyModel\", {\n        myField: types.array(types.frozen<MyInterface>())\n    })\n\n    const x = MyModel.create({ myField: [{ test: \"stuff\" }] })\n    const a: string = x.myField[0].test\n})\n\ntest(\"932 - 2\", () => {\n    type MyType = string\n    const ModelA = types.model(\"ModelA\", {\n        myField: types.maybe(types.frozen<MyType>())\n    })\n    const x = ModelA.create({})\n    const y = x.myField // y is string | undefined\n\n    const ModelA2 = types.model(\"ModelA\", {\n        myField: types.frozen<MyType>()\n    })\n    const x2 = ModelA2.create({\n        myField: \"test\" // mandatory\n    })\n    const y2: string = x2.myField // string only\n})\n\ntest(\"#923\", () => {\n    const Foo = types.model(\"Foo\", {\n        name: types.optional(types.string, \"\")\n    })\n\n    const Bar = types.model(\"Bar\", {\n        foos: types.optional(types.array(Foo), [])\n    })\n\n    types.optional(types.map(Bar), {}) // Should have no compile error!\n})\n\ntest(\"snapshot type of reference must be string | number\", () => {\n    const M = types.model({ id: types.identifier, a: \"bar\" })\n    const R = types.reference(M)\n\n    const S = types.model({ realM: M, refM: R })\n    const s = S.create({\n        realM: { id: \"5\" },\n        refM: \"5\"\n    })\n    const sn: string | number = getSnapshot(s.refM)\n})\n\ntest(\"#951\", () => {\n    const C = types.model({ a: 123 })\n\n    // model as root\n    const ModelWithC = types.model({ c: C })\n    const modelInstance = ModelWithC.create({ c: C.create() })\n\n    // getRoot\n    const modelRoot1 = getRoot<typeof ModelWithC>(modelInstance.c)\n    const modelCR1: Instance<typeof C> = modelRoot1.c\n    const modelRoot2 = getRoot<Instance<typeof ModelWithC>>(modelInstance.c)\n    const modelCR2: Instance<typeof C> = modelRoot2.c\n\n    // getParent\n    const modelParent1 = getParent<typeof ModelWithC>(modelInstance.c)\n    const modelCP1: Instance<typeof ModelWithC> = modelParent1\n    const modelParent2 = getParent<Instance<typeof ModelWithC>>(modelInstance.c)\n    const modelCP2: Instance<typeof ModelWithC> = modelParent2\n\n    // array as root\n    const ArrayOfC = types.array(C)\n    const arrayInstance = ArrayOfC.create([C.create()])\n\n    // getRoot\n    const arrayRoot1 = getRoot<typeof ArrayOfC>(arrayInstance[0])\n    const arrayCR1: Instance<typeof C> = arrayRoot1[0]\n\n    // getParent\n    const arrayParent1 = getParent<typeof ArrayOfC>(arrayInstance[0])\n    const arrayCP1: Instance<typeof ArrayOfC> = arrayParent1\n\n    // map as root\n    const MapOfC = types.map(C)\n    const mapInstance = MapOfC.create({ a: C.create() })\n\n    // getRoot\n    const mapRoot1 = getRoot<typeof MapOfC>(mapInstance.get(\"a\")!)\n    const mapC1: Instance<typeof C> = mapRoot1.get(\"a\")!\n\n    // getParent\n    const mapParent1 = getRoot<typeof MapOfC>(mapInstance.get(\"a\")!)\n    const mapCP1: Instance<typeof MapOfC> = mapParent1\n})\n\ntest(\"cast and SnapshotOrInstance\", () => {\n    const NumberArray = types.array(types.number)\n    const NumberMap = types.map(types.number)\n    const A = types\n        .model({ n: 123, n2: types.number, arr: NumberArray, map: NumberMap })\n        .actions(self => ({\n            // for primitives (although not needed)\n            setN(nn: SnapshotOrInstance<typeof self.n>) {\n                self.n = cast(nn)\n            },\n            setN2(nn: SnapshotOrInstance<typeof types.number>) {\n                self.n = cast(nn)\n            },\n            setN3(nn: SnapshotOrInstance<number>) {\n                self.n = cast(nn)\n            },\n            setN4(nn: number) {\n                self.n = cast(nn)\n            },\n            setN5() {\n                self.n = cast(5)\n            },\n\n            // for arrays\n            setArr(nn: SnapshotOrInstance<typeof self.arr>) {\n                self.arr = cast(nn)\n            },\n            setArr2(nn: SnapshotOrInstance<typeof NumberArray>) {\n                self.arr = cast(nn)\n            },\n            setArr3(nn: SnapshotIn<typeof NumberArray>) {\n                self.arr = cast(nn)\n            },\n            setArr31(nn: number[]) {\n                self.arr = cast(nn)\n            },\n            setArr4() {\n                // it works even without specifying the target type, magic!\n                self.arr = cast([2, 3, 4])\n                self.arr = cast(NumberArray.create([2, 3, 4]))\n            },\n\n            // for maps\n            setMap(nn: SnapshotOrInstance<typeof self.map>) {\n                self.map = cast(nn)\n            },\n            setMap2(nn: SnapshotOrInstance<typeof NumberMap>) {\n                self.map = cast(nn)\n            },\n            setMap3(nn: SnapshotIn<typeof NumberMap>) {\n                self.map = cast(nn)\n            },\n            setMap31(nn: { [k: string]: number }) {\n                self.map = cast(nn)\n            },\n            setMap4() {\n                // it works even without specifying the target type, magic!\n                self.map = cast({ a: 2, b: 3 })\n                self.map = cast(NumberMap.create({ a: 2, b: 3 }))\n            }\n        }))\n\n    const C = types\n        .model({ a: A, maybeA: types.maybe(A), maybeNullA: types.maybeNull(A) })\n        .actions(self => ({\n            // for submodels, using typeof self.var\n            setA(na: SnapshotOrInstance<typeof self.a>) {\n                self.a = cast(na)\n                // we just want to check it compiles\n                if (0 !== 0) {\n                    self.maybeA = cast(na)\n                    self.maybeNullA = cast(na)\n                }\n            },\n            // for submodels, using the type directly\n            setA2(na: SnapshotOrInstance<typeof A>) {\n                self.a = cast(na)\n                // we just want to check it compiles\n                if (0 !== 0) {\n                    self.maybeA = cast(na)\n                    self.maybeNullA = cast(na)\n                }\n            },\n            setA3(na: SnapshotIn<typeof A>) {\n                self.a = cast(na)\n                // we just want to check it compiles\n                if (0 !== 0) {\n                    self.maybeA = cast(na)\n                    self.maybeNullA = cast(na)\n                }\n            },\n            setA4(na: Instance<typeof self.a>) {\n                self.a = cast(na)\n                // we just want to check it compiles\n                if (0 !== 0) {\n                    self.maybeA = cast(na)\n                    self.maybeNullA = cast(na)\n                }\n            },\n            setA5() {\n                // it works even without specifying the target type, magic!\n                self.a = cast({ n2: 5 })\n                self.a = cast(A.create({ n2: 5 }))\n                // we just want to check it compiles\n                if (0 !== 0) {\n                    self.maybeA = cast({ n2: 5 })\n                    self.maybeA = cast(A.create({ n2: 5 }))\n                    self.maybeNullA = cast({ n2: 5 })\n                    self.maybeNullA = cast(A.create({ n2: 5 }))\n                }\n            }\n        }))\n\n    const c = C.create({ a: { n2: 5 } })\n    unprotect(c)\n    // all below works\n    c.setA({ n2: 5 })\n    c.setA(A.create({ n2: 5 }))\n    c.setA2({ n2: 5 })\n    c.setA2(A.create({ n2: 5 }))\n    c.setA3({ n2: 5 })\n    // c.setA3(A.create({ n2: 5 })) // this one doesn't work (as expected, it wants the creation type)\n    // c.setA4({n2: 5}) // this one doesn't work (as expected, it wants the instance type)\n    c.setA4(A.create({ n2: 5 }))\n    c.setA5()\n\n    c.a.setN(1)\n    c.a.setN2(1)\n    c.a.setN3(1)\n    c.a.setN4(1)\n    c.a.setN5()\n\n    c.a.setArr([])\n    c.a.setArr(NumberArray.create([]))\n    c.a.setArr2([])\n    c.a.setArr2(NumberArray.create([]))\n    c.a.setArr3([])\n    c.a.setArr3(NumberArray.create([]))\n    c.a.setArr4()\n\n    c.a.setMap({ a: 2, b: 3 })\n    c.a.setMap(NumberMap.create({ a: 2, b: 3 }))\n    c.a.setMap2({ a: 2, b: 3 })\n    c.a.setMap2(NumberMap.create({ a: 2, b: 3 }))\n    c.a.setMap3({ a: 2, b: 3 })\n    // c.a.setMap3(NumberMap.create({ a: 2, b: 3 })) // doesn't work (as expected, wants a plain object)\n    c.a.setMap4()\n\n    const arr = types.array(A).create()\n    unprotect(arr)\n    arr[0] = cast({ n2: 5 })\n\n    const map = types.map(A).create()\n    unprotect(map)\n    map.set(\"a\", cast({ n2: 5 })) // not really needed in this case, but whatever :)\n\n    // this does not compile, yay!\n    /*\n    cast([])\n    cast({ a: 5 })\n    cast(NumberArray.create([]))\n    cast(A.create({ n2: 5 }))\n    cast({ a: 2, b: 5 })\n    cast(NumberMap.create({ a: 2, b: 3 }))\n    */\n})\n\ntest(\"#994\", () => {\n    const Cinema = types.model(\"Cinema\", {\n        id: types.identifier,\n        name: types.maybe(types.string)\n    })\n\n    const ref = types.reference(Cinema) // should compile ok on TS3\n})\n\ntest(\"castToSnapshot\", () => {\n    const firstModel = types.model({ brew1: types.map(types.number) })\n    const secondModel = types.model({ brew2: types.map(firstModel) }).actions(self => ({ do() {} }))\n    const appMod = types.model({ aaa: secondModel })\n\n    const storeSnapshot: SnapshotIn<typeof secondModel> = {\n        brew2: { outside: { brew1: { inner: 222 } } }\n    }\n    const storeInstance = secondModel.create(storeSnapshot)\n    const storeSnapshotOrInstance1: SnapshotOrInstance<typeof secondModel> =\n        secondModel.create(storeSnapshot)\n    const storeSnapshotOrInstance2: SnapshotOrInstance<typeof secondModel> = storeSnapshot\n\n    appMod.create({ aaa: castToSnapshot(storeInstance) })\n    appMod.create({ aaa: castToSnapshot(storeSnapshot) })\n    appMod.create({ aaa: castToSnapshot(storeSnapshotOrInstance1) })\n    appMod.create({ aaa: castToSnapshot(storeSnapshotOrInstance2) })\n    // appMod.create({ aaa: castToSnapshot(5) }) // should not compile\n})\n\ntest(\"create correctly chooses if the snapshot is needed or not - #920\", () => {\n    const X = types.model({\n        test: types.string\n    })\n    const T = types.model({\n        test: types.refinement(X, s => s.test.length > 5)\n    })\n    // T.create() // manual test: expects compilation error\n    // T.create({}) // manual test: expects compilation error\n    T.create({\n        test: { test: \"hellothere\" }\n    })\n\n    const T2 = types.model({\n        test: types.maybe(X)\n    })\n    T2.create() // ok\n    T2.create({}) // ok\n\n    const A = types.model({\n        test: \"bla\"\n    })\n    A.create() // ok\n    A.create({}) // ok\n\n    const B = types.array(types.string)\n    B.create() // ok\n    B.create([\"hi\"]) // ok\n\n    const C = types.map(types.string)\n    C.create() // ok\n    C.create({ hi: \"hi\" }) // ok\n\n    const D = types.number\n    // D.create() // manual test: expects compilation error\n    D.create(5) // ok\n\n    const E = types.optional(types.number, 5)\n    E.create() // ok\n    E.create(6) // ok\n\n    const F = types.frozen<number>()\n    // F.create() // manual test: compilation error\n    F.create(6) // ok\n\n    const FF = types.frozen<number | undefined>()\n    FF.create() // ok\n    FF.create(undefined) // ok\n\n    const G = types.frozen(5)\n    G.create() // ok\n    G.create(6) // ok\n\n    const H = types.frozen<any>(5)\n    H.create() // ok\n    H.create(6) // ok\n\n    const I = types.optional(types.frozen<number>(), 6)\n    I.create()\n    I.create(7)\n})\n\ntest(\"#1117\", () => {\n    const Failsafe = <C, S, T>(\n        t: IType<C, S, T>,\n        handleProblem: (\n            value: C,\n            validationError: ReturnType<IType<C, S, T>[\"validate\"]>\n        ) => void = (value, error) => {\n            console.error(\"Skipping value: typecheck error on\", value)\n            console.error(error)\n        }\n    ) =>\n        types.custom<C, T | null>({\n            name: `Failsafe<${t.name}>`,\n            fromSnapshot(snapshot: C) {\n                try {\n                    return t.create(snapshot) // this should compile\n                } catch (e) {\n                    handleProblem(snapshot, e as any)\n                    return null\n                }\n            },\n            toSnapshot(x) {\n                if (isStateTreeNode(x)) return getSnapshot(x)\n                return x as any as C\n            },\n            isTargetType(v): v is T | null {\n                if (isFrozenType(t)) {\n                    return t.is(v)\n                }\n                return false\n            },\n            getValidationMessage() {\n                return \"\"\n            }\n        })\n})\n\ntest(\"MST array type should be assignable to plain array type\", () => {\n    {\n        const Todo = types\n            .model({\n                done: false,\n                name: types.string\n            })\n            .actions(self => ({\n                toggleDone() {\n                    self.done = !self.done\n                }\n            }))\n        const TodoArray = types.array(Todo)\n\n        const todoArray = TodoArray.create([{ done: true, name: \"todo1\" }, { name: \"todo2\" }])\n        unprotect(todoArray)\n        const otherTodoArray: Array<Instance<typeof Todo>> = todoArray\n        otherTodoArray.push(cast({ done: false, name: \"todo2\" }))\n    }\n\n    {\n        const T = types.model({\n            a: types.optional(types.array(types.number), [])\n        })\n\n        const arr: Array<number> = T.create().a\n    }\n\n    {\n        const T = types.model({\n            a: types.optional(types.array(types.number), [], [5])\n        })\n\n        const arr: Array<number> = T.create({\n            a: 5\n        }).a\n    }\n})\n\ntest(\"can get snapshot from submodel (submodel is IStateNodeTree\", () => {\n    const T = types.model({\n        a: types.model({ x: 5 })\n    })\n    const t = T.create({ a: {} })\n    const sn = getSnapshot(t.a).x\n})\n\ntest(\"can extract type from complex objects\", () => {\n    const T = types.maybe(\n        types.model({\n            a: types.model({\n                x: 5\n            })\n        })\n    )\n    const t = T.create({\n        a: {}\n    })!\n\n    type OriginalType = TypeOfValue<typeof t>\n    const T2: OriginalType = T\n})\n\ntest(\"#1268\", () => {\n    const Book = types.model({\n        id: types.identifier\n    })\n\n    const BooksStore = types.model({\n        books: types.array(types.reference(Book))\n    })\n\n    const RootStore = types.model({\n        booksStore: BooksStore\n    })\n\n    const booksStore = BooksStore.create({ books: [] })\n\n    const rootStore = RootStore.create({ booksStore: castToSnapshot(booksStore) })\n})\n\ntest(\"#1307 optional can be omitted in .create\", () => {\n    const Model1 = types.model({ name: types.optional(types.string, \"\") })\n    const model1 = Model1.create({})\n    assertTypesEqual(model1.name, _ as string)\n\n    const Model2 = types.model({ name: \"\" })\n    const model2 = Model2.create({})\n    assertTypesEqual(model2.name, _ as string)\n})\n\ntest(\"#1307 custom types failing\", () => {\n    const createCustomType = <ICustomType extends ModelPrimitive | IAnyType>({\n        CustomType\n    }: {\n        CustomType: ICustomType\n    }) => {\n        return types\n            .model(\"Example\", {\n                someProp: types.boolean,\n                someType: CustomType\n            })\n            .views(self => ({\n                get isSomePropTrue(): boolean {\n                    return self.someProp\n                }\n            }))\n    }\n})\n\ntest(\"#1343\", () => {\n    function createTypeA<T extends ModelPropertiesDeclaration>(t: T) {\n        return types.model(\"TypeA\", t).views(self => ({\n            get someView() {\n                return null\n            }\n        }))\n    }\n\n    function createTypeB<T extends ModelPropertiesDeclaration>(t: T) {\n        return types\n            .model(\"TypeB\", {\n                a: createTypeA(t)\n            })\n            .views(self => ({\n                get someViewFromA() {\n                    return self.a.someView\n                }\n            }))\n    }\n})\n\ntest(\"#1330\", () => {\n    const ChildStore = types\n        .model(\"ChildStore\", {\n            foo: types.string,\n            bar: types.boolean\n        })\n        .views(self => ({\n            get root(): IRootStore {\n                return getRoot<IRootStore>(self)\n            }\n        }))\n        .actions(self => ({\n            test() {\n                const { childStore } = self.root\n                // childStore and childStore.foo is properly inferred in TS 3.4 but not in 3.5\n                console.log(childStore.foo)\n            }\n        }))\n\n    interface IRootStore extends Instance<typeof RootStore> {}\n\n    const RootStore = types.model(\"RootStore\", {\n        childStore: ChildStore,\n        test: \"\"\n    })\n\n    assertTypesEqual(\n        RootStore.create({\n            childStore: {\n                foo: \"a\",\n                bar: true\n            }\n        }).childStore.root.test,\n        _ as string\n    )\n})\n\ntest(\"maybe / optional type inference verification\", () => {\n    const T = types.model({\n        a: types.string,\n        b: \"test\",\n        c: types.maybe(types.string),\n        d: types.maybeNull(types.string),\n        e: types.optional(types.string, \"test\")\n    })\n\n    interface ITC extends SnapshotIn<typeof T> {}\n    interface ITS extends SnapshotOut<typeof T> {}\n\n    assertTypesEqual(\n        _ as ITC,\n        _ as {\n            a: string\n            b?: string\n            c?: string | undefined\n            d?: string | null\n            e?: string\n        }\n    )\n\n    assertTypesEqual(\n        _ as ITS,\n        _ as {\n            a: string\n            b: string\n            c: string | undefined\n            d: string | null\n            e: string\n        }\n    )\n})\n\ntest(\"object creation with no props\", () => {\n    const MyType = types.model().views(_self => ({\n        get test() {\n            return 5\n        }\n    }))\n\n    MyType.create()\n    MyType.create({})\n\n    // Instances can be created with their own instance type\n    MyType.create(MyType.create())\n\n    // Instances can be created with a snapshot of themselves\n    true || MyType.create(getSnapshot(MyType.create()))\n\n    // TODO @ts-expect-error -- symbols aren't props (but may be one day)\n    // This currently is allowed, because excess property checking doesn't happen against symbols.\n    // See https://github.com/microsoft/TypeScript/issues/44794\n    true || MyType.create({ [Symbol(\"test\")]: 5 })\n\n    // @ts-expect-error -- this is a view, not a prop\n    true || MyType.create({ test: 5 })\n\n    // @ts-expect-error -- unknown prop\n    true || MyType.create({ another: 5 })\n})\n\ntest(\"object creation when composing with a model with no props\", () => {\n    const EmptyType = types.model().views(_self => ({\n        get test() {\n            return 5\n        }\n    }))\n\n    const NonEmptyType = types.model({\n        value: types.optional(types.number, 0),\n        negate: types.boolean\n    })\n\n    const Composed = types.compose(EmptyType, NonEmptyType)\n\n    Composed.create({ negate: true })\n    Composed.create({ negate: false })\n    Composed.create({ value: 5, negate: true })\n\n    // Instances can be created with their own instance type\n    Composed.create(Composed.create({ negate: true }))\n\n    // @ts-expect-error -- symbols aren't props (but may be one day)\n    true || Composed.create({ [Symbol(\"test\")]: 5 })\n\n    // @ts-expect-error -- this is a view, not a prop\n    true || Composed.create({ test: 5 })\n\n    // @ts-expect-error -- unknown prop\n    true || Composed.create({ another: 5 })\n})\n\ntest(\"union type inference verification for small number of types\", () => {\n    const T = types.union(types.boolean, types.literal(\"test\"), types.maybe(types.number))\n\n    type ITC = SnapshotIn<typeof T>\n    type ITS = SnapshotOut<typeof T>\n\n    assertTypesEqual(_ as ITC, _ as boolean | \"test\" | number | undefined)\n    assertTypesEqual(_ as ITS, _ as boolean | \"test\" | number | undefined)\n})\n\ntest(\"union type inference verification for a large number of types\", () => {\n    const T = types.union(\n        types.literal(\"a\"),\n        types.literal(\"b\"),\n        types.literal(\"c\"),\n        types.literal(\"d\"),\n        types.literal(\"e\"),\n        types.literal(\"f\"),\n        types.literal(\"g\"),\n        types.literal(\"h\"),\n        types.literal(\"i\"),\n        types.literal(\"j\")\n    )\n\n    type ITC = SnapshotIn<typeof T>\n    type ITS = SnapshotOut<typeof T>\n\n    assertTypesEqual(_ as ITC, _ as \"a\" | \"b\" | \"c\" | \"d\" | \"e\" | \"f\" | \"g\" | \"h\" | \"i\" | \"j\")\n    assertTypesEqual(_ as ITS, _ as \"a\" | \"b\" | \"c\" | \"d\" | \"e\" | \"f\" | \"g\" | \"h\" | \"i\" | \"j\")\n})\n\ntest(\"#2186 substitutability type verification for model types extending a common base\", () => {\n    const BaseType = types.model()\n    const SubTypeOptional = BaseType.props({ a: \"\" })\n    const SubTypeRequired = BaseType.props({ a: types.string })\n    const SubTypeRequiredWithAnotherOptional = SubTypeRequired.props({ a: types.string, b: 5 })\n\n    true || ((t: typeof BaseType) => t.create())(SubTypeOptional)\n    true ||\n        ((t: typeof SubTypeRequired) => t.create({ a: \"123\" }))(SubTypeRequiredWithAnotherOptional)\n    true ||\n        ((t: typeof BaseType) => t.create())(\n            // @ts-expect-error -- a is required\n            SubTypeRequired\n        )\n})\n\ntest(\"#2184 - type narrowing functions should narrow to the expected type\", () => {\n    const type: unknown = null\n\n    if (isOptionalType(type)) {\n        assertTypesEqual(type, _ as IOptionalIType<IAnyType, [any, ...any[]]>)\n    } else if (isUnionType(type)) {\n        assertTypesEqual(type, _ as IUnionType<IAnyType[]>)\n    } else if (isFrozenType(type)) {\n        assertTypesEqual(type, _ as ISimpleType<any>)\n    } else if (isMapType(type)) {\n        assertTypesEqual(type, _ as IMapType<IAnyType>)\n    } else if (isArrayType(type)) {\n        assertTypesEqual(type, _ as IArrayType<IAnyType>)\n    } else if (isModelType(type)) {\n        assertTypesEqual(type, _ as IAnyModelType)\n    } else if (isLiteralType(type)) {\n        assertTypesEqual(type, _ as ISimpleType<any>)\n    } else if (isPrimitiveType(type)) {\n        assertTypesEqual(\n            type,\n            _ as\n                | ISimpleType<string>\n                | ISimpleType<number>\n                | ISimpleType<boolean>\n                | typeof types.bigint\n                | typeof DatePrimitive\n        )\n    } else if (isReferenceType(type)) {\n        assertTypesEqual(type, _ as IReferenceType<IAnyComplexType>)\n    } else if (isIdentifierType(type)) {\n        assertTypesEqual(type, _ as ISimpleType<string> | ISimpleType<number>)\n    } else if (isRefinementType(type)) {\n        assertTypesEqual(type, _ as IAnyType)\n    } else if (isLateType(type)) {\n        assertTypesEqual(type, _ as IAnyType)\n    }\n})\n\ndescribe(\"for snapshotProcessor\", () => {\n    const Model = types.model({ name: types.optional(types.string, \"string\") })\n\n    test(\"produces the right types when not customized\", () => {\n        const Processor = types.snapshotProcessor(Model, {\n            preProcessor(snapshot) {\n                assertTypesEqual(snapshot, _ as SnapshotIn<typeof Model>)\n                return snapshot\n            },\n            postProcessor(snapshot) {\n                assertTypesEqual(snapshot, _ as { name: string })\n                return snapshot\n            }\n        })\n\n        type ITC = SnapshotIn<typeof Processor>\n        type ITS = SnapshotOut<typeof Processor>\n\n        assertTypesEqual(_ as ITC, _ as SnapshotIn<typeof Model>)\n        assertTypesEqual(_ as ITS, _ as SnapshotOut<typeof Model>)\n    })\n\n    test(\"produces the right types when customized\", () => {\n        const Processor = types.snapshotProcessor<typeof Model, string, number>(Model, {\n            preProcessor(snapshot) {\n                assertTypesEqual(snapshot, _ as string)\n                return Model.create({ name: snapshot })\n            },\n            postProcessor(snapshot) {\n                assertTypesEqual(snapshot, _ as SnapshotOut<typeof Model>)\n                return snapshot.name.length\n            }\n        })\n\n        type ITC = SnapshotIn<typeof Processor>\n        type ITS = SnapshotOut<typeof Processor>\n\n        assertTypesEqual(_ as ITC, _ as string)\n        assertTypesEqual(_ as ITS, _ as number)\n    })\n})\n\ntest(\"#1627 - union dispatch function is typed\", () => {\n    const model = types.model({ a: types.string })\n    const _union = types.union(\n        {\n            dispatcher(snapshot) {\n                assertTypesEqual(\n                    snapshot,\n                    _ as SnapshotIn<typeof model> | SnapshotIn<typeof types.null>\n                )\n                return snapshot?.a ? model : types.null\n            }\n        },\n        model,\n        types.null\n    )\n\n    const _brokenUnion = types.union(\n        {\n            // @ts-expect-error -- types.string isn't a valid type for the union\n            dispatcher(snapshot) {\n                assertTypesEqual(\n                    snapshot,\n                    _ as SnapshotIn<typeof model> | SnapshotIn<typeof types.null>\n                )\n                return snapshot?.a ? model : types.string\n            }\n        },\n        model,\n        types.null\n    )\n})\n"
  },
  {
    "path": "__tests__/core/union.test.ts",
    "content": "import { configure } from \"mobx\"\nimport {\n    types,\n    hasParent,\n    tryResolve,\n    getSnapshot,\n    applySnapshot,\n    getType,\n    setLivelinessChecking,\n    SnapshotIn,\n    Instance,\n    IAnyType\n} from \"../../src\"\nimport { describe, expect, it, test, beforeEach } from \"bun:test\"\n\nconst createTestFactories = () => {\n    const Box = types.model(\"Box\", {\n        width: types.number,\n        height: types.number\n    })\n    const Square = types.model(\"Square\", {\n        width: types.number\n    })\n    const Cube = types.model(\"Cube\", {\n        width: types.number,\n        height: types.number,\n        depth: types.number\n    })\n    const Plane = types.union(Square, Box)\n    const Heighed = types.union(Box, Cube)\n    const DispatchPlane = types.union(\n        { dispatcher: snapshot => (snapshot && \"height\" in snapshot ? Box : Square) },\n        Box,\n        Square\n    )\n    const Block = types.model(\"Block\", {\n        list: types.array(Heighed)\n    })\n    return { Box, Square, Cube, Plane, DispatchPlane, Heighed, Block }\n}\nconst createLiteralTestFactories = () => {\n    const Man = types.model(\"Man\", { type: types.literal(\"M\") })\n    const Woman = types.model(\"Woman\", { type: types.literal(\"W\") })\n    const All = types.model(\"All\", { type: types.string })\n    const ManWomanOrAll = types.union(Man, Woman, All)\n    return { Man, Woman, All, ManWomanOrAll }\n}\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should complain about multiple applicable types no dispatch method\", () => {\n        const { Box, Square } = createTestFactories()\n        const PlaneNotEager = types.union({ eager: false }, Square, Box)\n        expect(() => {\n            PlaneNotEager.create({ width: 2, height: 2 })\n        }).toThrow(/Error while converting/)\n    })\n}\ntest(\"it should have parent whenever creating or applying from a complex data structure to a model which has Union typed children\", () => {\n    const { Block, Heighed } = createTestFactories()\n    const block = Block.create({\n        list: [{ width: 2, height: 2 }]\n    })\n    const child = tryResolve(block, \"./list/0\")\n    expect(hasParent(child)).toBe(true)\n})\nif (process.env.NODE_ENV !== \"production\") {\n    test(\"it should complain about no applicable types\", () => {\n        const { Heighed } = createTestFactories()\n        expect(() => {\n            Heighed.create({ height: 2 } as any)\n        }).toThrow(/Error while converting/)\n    })\n}\ntest(\"it should be smart enough to discriminate by keys\", () => {\n    const { Box, Plane, Square } = createTestFactories()\n    const doc = types.union(Square, Box).create({ width: 2 })\n    expect(Box.is(doc)).toEqual(false)\n    expect(Square.is(doc)).toEqual(true)\n})\ntest(\"it should discriminate by value type\", () => {\n    const Size = types.model(\"Size\", {\n        width: 0,\n        height: 0\n    })\n    const Picture = types.model(\"Picture\", {\n        url: \"\",\n        size: Size\n    })\n    const Square = types.model(\"Square\", {\n        size: 0\n    })\n    const PictureOrSquare = types.union(Picture, Square)\n    const doc = PictureOrSquare.create({ size: { width: 0, height: 0 } })\n    expect(Picture.is(doc)).toEqual(true)\n    expect(Square.is(doc)).toEqual(false)\n})\ntest(\"it should compute exact union types\", () => {\n    const { Box, Plane, Square } = createTestFactories()\n    expect(Plane.is(Box.create({ width: 3, height: 2 }))).toEqual(true)\n    expect(Plane.is(Square.create({ width: 3 }))).toEqual(true)\n})\ntest(\"it should compute exact union types - 2\", () => {\n    const { Box, DispatchPlane, Square } = createTestFactories()\n    expect(DispatchPlane.is(Box.create({ width: 3, height: 2 }))).toEqual(true)\n    expect(\n        DispatchPlane.is(\n            Square.create({ width: 3, height: 2 } as any /* incorrect type, superfluous attr!*/)\n        )\n    ).toEqual(true)\n})\ntest(\"it should use dispatch to discriminate\", () => {\n    const { Box, DispatchPlane, Square } = createTestFactories()\n    const a = DispatchPlane.create({ width: 3 })\n    expect(getSnapshot(a)).toEqual({ width: 3 })\n})\n\ntest(\"it should eagerly match by ambiguos value\", () => {\n    const { ManWomanOrAll, All, Man } = createLiteralTestFactories()\n    const person = ManWomanOrAll.create({ type: \"Z\" })\n    expect(All.is(person)).toEqual(true)\n    expect(Man.is(person)).toEqual(false)\n})\n\ntest(\"it should eagerly match by ambiguos value - 2\", () => {\n    const { All, Man } = createLiteralTestFactories()\n    const person = types.union(All, Man).create({ type: \"M\" })\n    expect(All.is(person)).toEqual(true)\n    expect(Man.is(person)).toEqual(false) // not matched, All grabbed everything!\n})\n\ntest(\"it should eagerly match by value literal\", () => {\n    const { ManWomanOrAll, All, Man } = createLiteralTestFactories()\n    const person = ManWomanOrAll.create({ type: \"M\" })\n    expect(All.is(person)).toEqual(false)\n    expect(Man.is(person)).toEqual(true)\n})\n\ntest(\"dispatch\", () => {\n    const Odd = types\n        .model({\n            value: types.number\n        })\n        .actions(self => ({\n            isOdd() {\n                return true\n            },\n            isEven() {\n                return false\n            }\n        }))\n    const Even = types.model({ value: types.number }).actions(self => ({\n        isOdd() {\n            return false\n        },\n        isEven() {\n            return true\n        }\n    }))\n    const Num = types.union(\n        { dispatcher: snapshot => (snapshot.value % 2 === 0 ? Even : Odd) },\n        Even,\n        Odd\n    )\n    expect(Num.create({ value: 3 }).isOdd()).toBe(true)\n    expect(Num.create({ value: 3 }).isEven()).toBe(false)\n    expect(Num.create({ value: 4 }).isOdd()).toBe(false)\n    expect(Num.create({ value: 4 }).isEven()).toBe(true)\n    if (process.env.NODE_ENV !== \"production\") {\n        expect(() => {\n            types.union(\n                ((snapshot: any) => (snapshot.value % 2 === 0 ? Even : Odd)) as any, // { dispatcher: snapshot => (snapshot.value % 2 === 0 ? Even : Odd) },\n                Even,\n                Odd\n            )\n        }).toThrow(\"expected object\")\n    }\n})\n\ntest(\"961 - apply snapshot to union should not throw when union keeps models with different properties and snapshot is got by getSnapshot\", () => {\n    const Foo = types.model({ foo: 1 })\n    const Bar = types.model({ bar: 1 })\n    const U = types.union(Foo, Bar)\n\n    const u = U.create({ foo: 1 })\n    applySnapshot(u, getSnapshot(Bar.create()))\n})\n\ndescribe(\"1045 - secondary union types with applySnapshot and ids\", () => {\n    function initTest(\n        useSnapshot: boolean,\n        useCreate: boolean,\n        submodel1First: boolean,\n        type: number\n    ) {\n        setLivelinessChecking(\"error\")\n\n        const Submodel1NoSP = types.model(\"Submodel1\", {\n            id: types.identifier,\n            extraField1: types.string,\n            extraField2: types.maybe(types.string)\n        })\n\n        const Submodel1SP = types.snapshotProcessor(Submodel1NoSP, {\n            preProcessor(sn: SnapshotIn<Instance<typeof Submodel1NoSP>>) {\n                const { id, extraField1, extraField2 } = sn\n                return {\n                    id,\n                    extraField1: extraField1.toUpperCase(),\n                    extraField2: extraField2?.toUpperCase()\n                }\n            }\n        })\n\n        const Submodel2NoSP = types.model(\"Submodel2\", {\n            id: types.identifier,\n            extraField1: types.maybe(types.string),\n            extraField2: types.string\n        })\n\n        const Submodel2SP = types.snapshotProcessor(Submodel2NoSP, {\n            preProcessor(sn: SnapshotIn<Instance<typeof Submodel2NoSP>>) {\n                const { id, extraField1, extraField2 } = sn\n                return {\n                    id,\n                    extraField1: extraField1?.toUpperCase(),\n                    extraField2: extraField2.toUpperCase()\n                }\n            }\n        })\n\n        const Submodel1 = useSnapshot ? Submodel1SP : Submodel1NoSP\n        const Submodel2 = useSnapshot ? Submodel2SP : Submodel2NoSP\n\n        const Submodel = submodel1First\n            ? types.union(Submodel1, Submodel2)\n            : types.union(Submodel2, Submodel1)\n\n        const Model = types.array(Submodel)\n\n        const store = Model.create([{ id: \"id1\", extraField1: \"extraField1\" }])\n\n        return {\n            store,\n            applySn: function () {\n                const sn1 = {\n                    id: \"id1\",\n                    extraField1: \"new extraField1\",\n                    extraField2: \"some value\"\n                }\n                const sn2 = {\n                    id: \"id1\",\n                    extraField1: undefined,\n                    extraField2: \"some value\"\n                }\n                const sn = type === 1 ? sn1 : sn2\n                const submodel = type === 1 ? Submodel1 : Submodel2\n                const expected = useSnapshot\n                    ? {\n                          id: sn.id,\n                          extraField1: sn.extraField1?.toUpperCase(),\n                          extraField2: sn.extraField2?.toUpperCase()\n                      }\n                    : sn\n\n                applySnapshot(store, [useCreate ? (submodel as any).create(sn) : sn])\n\n                expect(store.length).toBe(1)\n                expect(store[0]).toEqual(expected)\n                expect(getType(store[0])).toBe(\n                    useSnapshot ? (submodel.getSubTypes() as IAnyType) : submodel\n                )\n            }\n        }\n    }\n\n    for (const useSnapshot of [false, true]) {\n        describe(useSnapshot ? \"with snapshotProcessor\" : \"without snapshotProcessor\", () => {\n            for (const submodel1First of [true, false]) {\n                describe(submodel1First ? \"submodel1 first\" : \"submodel2 first\", () => {\n                    for (const useCreate of [false, true]) {\n                        describe(useCreate ? \"using create\" : \"not using create\", () => {\n                            for (const type of [2, 1]) {\n                                describe(`snapshot is of type Submodel${type}`, () => {\n                                    beforeEach(() => {\n                                        configure({\n                                            useProxies: \"never\"\n                                        })\n                                    })\n\n                                    it(`apply snapshot works when the node is not touched`, () => {\n                                        const t = initTest(\n                                            useSnapshot,\n                                            useCreate,\n                                            submodel1First,\n                                            type\n                                        )\n                                        t.applySn()\n                                    })\n\n                                    it(`apply snapshot works when the node is touched`, () => {\n                                        const t = initTest(\n                                            useSnapshot,\n                                            useCreate,\n                                            submodel1First,\n                                            type\n                                        )\n                                        // tslint:disable-next-line:no-unused-expression\n                                        t.store[0]\n                                        t.applySn()\n                                    })\n                                })\n                            }\n                        })\n                    }\n                })\n            }\n        })\n    }\n})\n"
  },
  {
    "path": "__tests__/core/volatile.test.ts",
    "content": "import { types, getSnapshot, recordPatches, unprotect } from \"../../src\"\nimport { reaction, isObservableProp, isObservable, autorun, observable } from \"mobx\"\nimport { expect, test } from \"bun:test\"\n\nconst Todo = types\n    .model({\n        done: false\n    })\n    .volatile(self => ({\n        state: Promise.resolve(1)\n    }))\n    .actions(self => ({\n        toggle() {\n            self.done = !self.done\n        },\n        reload() {\n            self.state = Promise.resolve(2)\n        }\n    }))\n\ntest(\"Properties should be readable and writable\", () => {\n    const i = Todo.create()\n    expect(i.state instanceof Promise).toBe(true)\n    i.reload()\n    expect(i.state instanceof Promise).toBe(true)\n})\n\ntest(\"VS should not show up in snapshots\", () => {\n    expect(getSnapshot(Todo.create())).toEqual({ done: false })\n})\n\ntest(\"VS should not show up in patches\", () => {\n    const i = Todo.create()\n    const r = recordPatches(i)\n    i.reload()\n    i.toggle()\n    r.stop()\n    expect(r.patches).toEqual([{ op: \"replace\", path: \"/done\", value: true }])\n})\n\ntest(\"VS be observable\", () => {\n    const promises: Promise<number>[] = []\n    const i = Todo.create()\n    const d = reaction(\n        () => i.state,\n        p => promises.push(p)\n    )\n    i.reload()\n    i.reload()\n    expect(promises.length).toBe(2)\n    d()\n})\n\ntest(\"VS should not be deeply observable\", () => {\n    const i = types\n        .model({})\n        .volatile(self => ({\n            x: { a: 1 }\n        }))\n        .create()\n    unprotect(i)\n    expect(isObservableProp(i, \"x\")).toBe(true)\n    expect(isObservable(i.x)).toBe(false)\n    expect(isObservableProp(i.x, \"a\")).toBe(false)\n    i.x = { a: 2 }\n    expect(isObservableProp(i, \"x\")).toBe(true)\n    expect(isObservable(i.x)).toBe(false)\n    expect(isObservableProp(i.x, \"a\")).toBe(false)\n})\n\ntest(\"VS should not be strongly typed observable\", () => {\n    const i = Todo.create()\n    // TEST: type error i.state = 7\n    i.state.then(() => {}) // it's a promise\n    // TEST: not available on snapshot: getSnapshot(i).state\n    expect(true).toBe(true)\n})\n\ntest(\"VS should not be modifiable without action\", () => {\n    const i = Todo.create()\n    expect(() => {\n        i.state = Promise.resolve(4)\n    }).toThrow(/the object is protected and can only be modified by using an action/)\n})\n\ntest(\"VS should expect a function as an argument\", () => {\n    expect(() => {\n        const t = types\n            .model({})\n            // @ts-ignore\n            .volatile({ state: 1 })\n            .create()\n    }).toThrow(`You passed an object to volatile state as an argument, when function is expected`)\n})\n\ntest(\"VS should not be modifiable when unprotected\", () => {\n    const i = Todo.create()\n    unprotect(i)\n    const p = Promise.resolve(7)\n    expect(() => {\n        i.state = p\n    }).not.toThrow()\n    expect(i.state === p).toBe(true)\n})\n\ntest(\"VS sample from the docs should work (1)\", () => {\n    const T = types.model({}).extend(self => {\n        const localState = observable.box(3)\n\n        return {\n            views: {\n                get x() {\n                    return localState.get()\n                }\n            },\n            actions: {\n                setX(value: number) {\n                    localState.set(value)\n                }\n            }\n        }\n    })\n\n    const t = T.create()\n    expect(t.x).toBe(3)\n    t.setX(5)\n    expect(t.x).toBe(5)\n\n    // now observe it\n    const observed: number[] = []\n    const dispose = autorun(() => {\n        observed.push(t.x)\n    })\n\n    t.setX(7)\n    expect(t.x).toBe(7)\n    expect(observed).toEqual([5, 7])\n    dispose()\n})\n\ntest(\"VS sample from the docs should work (2)\", () => {\n    const T = types.model({}).extend(self => {\n        let localState = 3\n\n        return {\n            views: {\n                getX() {\n                    return localState\n                }\n            },\n            actions: {\n                setX(value: number) {\n                    localState = value\n                }\n            }\n        }\n    })\n\n    const t = T.create()\n    expect(t.getX()).toBe(3)\n    t.setX(5)\n    expect(t.getX()).toBe(5)\n\n    // now observe it (should not be observable)\n    const observed: number[] = []\n    const dispose = autorun(() => {\n        observed.push(t.getX())\n    })\n\n    t.setX(7)\n    expect(t.getX()).toBe(7)\n    expect(observed).toEqual([5])\n    dispose()\n})\n"
  },
  {
    "path": "__tests__/perf/fixture-data.skip.ts",
    "content": "import { rando, createHeros, createMonsters, createTreasure } from \"./fixtures/fixture-data\"\nimport { Hero, Monster, Treasure } from \"./fixtures/fixture-models\"\nimport { expect, test } from \"bun:test\"\n\ntest(\"createHeros\", () => {\n    const data = createHeros(10)\n    expect(data.length).toBe(10)\n    const hero = Hero.create(data[0])\n    expect(hero.descriptionLength > 1).toBe(true)\n})\ntest(\"createMonsters\", () => {\n    const data = createMonsters(10, 10, 10)\n    expect(data.length).toBe(10)\n    expect(data[1].treasures.length).toBe(10)\n    expect(data[0].eatenHeroes.length).toBe(10)\n    const monster = Monster.create(data[0])\n    expect(monster.eatenHeroes && monster.eatenHeroes.length === 10).toBe(true)\n    expect(monster.treasures.length === 10).toBe(true)\n})\ntest(\"createTreasure\", () => {\n    const data = createTreasure(10)\n    expect(data.length).toBe(10)\n    const treasure = Treasure.create(data[1])\n    expect(treasure.gold > 0).toBe(true)\n})\ntest(\"rando sorting\", () => {\n    // i'm going straight to hell for this test... must get coverage to 100%.... no matter the cost.\n    let foundTrue = false\n    let foundFalse = false\n    let result\n    do {\n        result = rando()\n        if (result) {\n            foundTrue = true\n        } else {\n            foundFalse = true\n        }\n    } while (!foundTrue || !foundFalse)\n    expect(foundTrue).toBe(true)\n    expect(foundFalse).toBe(true)\n})\n"
  },
  {
    "path": "__tests__/perf/fixture-models.skip.ts",
    "content": "import { Hero, Monster, Treasure } from \"./fixtures/fixture-models\"\nimport { expect, test } from \"bun:test\"\nconst mst = require(\"../../dist/mobx-state-tree.umd\")\nconst { unprotect } = mst\n\nconst SAMPLE_HERO = {\n    id: 1,\n    name: \"jimmy\",\n    level: 1,\n    role: \"cleric\",\n    description: \"hi\"\n}\ntest(\"Hero computed fields\", () => {\n    const hero = Hero.create(SAMPLE_HERO)\n    expect(hero.descriptionLength).toBe(2)\n})\ntest(\"Tresure\", () => {\n    const treasure = Treasure.create({ gold: 1, trapped: true })\n    expect(treasure.trapped).toBe(true)\n    expect(treasure.gold).toBe(1)\n})\ntest(\"Monster computed fields\", () => {\n    const monster = Monster.create({\n        id: \"foo\",\n        level: 1,\n        maxHp: 3,\n        hp: 1,\n        warning: \"boo!\",\n        createdAt: new Date(),\n        treasures: [\n            { gold: 2, trapped: true },\n            { gold: 3, trapped: true }\n        ],\n        eatenHeroes: [SAMPLE_HERO],\n        hasFangs: true,\n        hasClaws: true,\n        hasWings: true,\n        hasGrowl: true,\n        freestyle: null\n    })\n    expect(monster.isAlive).toBe(true)\n    expect(monster.isFlashingRed).toBe(true)\n    unprotect(monster)\n    expect(monster.weight).toBe(2)\n    monster.level = 0\n    monster.hasFangs = false\n    monster.hasWings = false\n    monster.eatenHeroes = null\n    expect(monster.weight).toBe(1)\n    monster.hp = 0\n    expect(monster.isFlashingRed).toBe(false)\n    expect(monster.isAlive).toBe(false)\n})\n"
  },
  {
    "path": "__tests__/perf/fixtures/fixture-data.ts",
    "content": "import { HeroRoles } from \"./fixture-models\"\n\n/**\n * Creates data containing very few fields.\n *\n * @param count The number of items to create.\n */\nexport function createTreasure(count: number) {\n    const data = []\n    let i = 0\n    do {\n        data.push({\n            trapped: i % 2 === 0,\n            gold: ((count % 10) + 1) * 10\n        })\n        i++\n    } while (i < count)\n    return data\n}\n\n// why yes i DID graduate high school, why do you ask?\nexport const rando = () => (Math.random() > 0.5 ? 1 : 0)\n\nconst titles = [\"Sir\", \"Lady\", \"Baron von\", \"Baroness\", \"Captain\", \"Dread\", \"Fancy\"].sort(rando)\nconst givenNames = [\"Abe\", \"Beth\", \"Chuck\", \"Dora\", \"Ernie\", \"Fran\", \"Gary\", \"Haily\"].sort(rando)\nconst epicNames = [\"Amazing\", \"Brauny\", \"Chafed\", \"Dapper\", \"Egomaniac\", \"Foul\"].sort(rando)\nconst wtf = `Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt,\n    Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains,\n    and Mother of Dragons. `\n/**\n * Creates data with a medium number of fields and data.\n *\n * @param count The number of items to create.\n */\nexport function createHeros(count: number) {\n    const data = []\n    let i = 0\n    let even = true\n    let n1\n    let n2\n    let n3\n    do {\n        n1 = titles[i % titles.length]\n        n2 = givenNames[i % givenNames.length]\n        n3 = epicNames[i % epicNames.length]\n        data.push({\n            id: i,\n            name: `${n1} ${n2} the ${n3}`,\n            level: (count % 100) + 1,\n            role: HeroRoles[i % HeroRoles.length],\n            description: `${wtf} ${wtf} ${wtf}`\n        })\n        even = !even\n        i++\n    } while (i < count)\n    return data\n}\n\n/**\n * Creates data with a large number of fields and data.\n *\n * @param count The number of items to create.\n * @param treasureCount The number of small children to create.\n * @param heroCount The number of medium children to create.\n */\nexport function createMonsters(count: number, treasureCount: number, heroCount: number) {\n    const data = []\n    let i = 0\n    let even = true\n    do {\n        const treasures = createTreasure(treasureCount)\n        const eatenHeroes = createHeros(heroCount)\n        data.push({\n            id: `omg-${i}-run!`,\n            freestyle: `${wtf} ${wtf} ${wtf}${wtf} ${wtf} ${wtf}`,\n            level: (count % 100) + 1,\n            hp: i % 2 === 0 ? 1 : 5 * i,\n            maxHp: 5 * i,\n            warning: \"!!!!!!\",\n            createdAt: new Date(),\n            hasFangs: even,\n            hasClaws: even,\n            hasWings: !even,\n            hasGrowl: !even,\n            fearsFire: even,\n            fearsWater: !even,\n            fearsWarriors: even,\n            fearsClerics: !even,\n            fearsMages: even,\n            fearsThieves: !even,\n            stenchLevel: i % 5,\n            treasures,\n            eatenHeroes\n        })\n        even = !even\n        i++\n    } while (i < count)\n    return data\n}\n"
  },
  {
    "path": "__tests__/perf/fixtures/fixture-models.ts",
    "content": "const mst = require(\"../../../dist/mobx-state-tree.umd\")\nconst { types } = mst\n\n// tiny\nexport const Treasure = types.model(\"Treasure\", {\n    trapped: types.boolean,\n    gold: types.optional(types.number, 0)\n})\n// medium\nexport const HeroRoles = [\"warrior\", \"wizard\", \"cleric\", \"thief\"]\nexport const Hero = types\n    .model(\"Hero\", {\n        id: types.identifierNumber,\n        name: types.string,\n        description: types.string,\n        level: types.optional(types.number, 1),\n        role: types.union(...exports.HeroRoles.map(types.literal))\n    })\n    .views((self: any) => ({\n        get descriptionLength() {\n            return self.description.length\n        }\n    }))\n// large\nexport const Monster = types\n    .model(\"Monster\", {\n        id: types.identifier,\n        freestyle: types.frozen(),\n        level: types.number,\n        maxHp: types.number,\n        hp: types.number,\n        warning: types.maybeNull(types.string),\n        createdAt: types.maybeNull(types.Date),\n        treasures: types.optional(types.array(exports.Treasure), []),\n        eatenHeroes: types.maybeNull(types.array(exports.Hero)),\n        hasFangs: types.optional(types.boolean, false),\n        hasClaws: types.optional(types.boolean, false),\n        hasWings: types.optional(types.boolean, false),\n        hasGrowl: types.optional(types.boolean, false),\n        stenchLevel: types.optional(types.number, 0),\n        fearsFire: types.optional(types.boolean, false),\n        fearsWater: types.optional(types.boolean, false),\n        fearsWarriors: types.optional(types.boolean, false),\n        fearsClerics: types.optional(types.boolean, false),\n        fearsMages: types.optional(types.boolean, false),\n        fearsThieves: types.optional(types.boolean, false),\n        fearsProgrammers: types.optional(types.boolean, true)\n    })\n    .views((self: any) => ({\n        get isAlive() {\n            return self.hp > 0\n        },\n        get isFlashingRed() {\n            return self.hp > 0 && self.hp < self.maxHp && self.hp === 1\n        },\n        get weight() {\n            const victimWeight = self.eatenHeroes ? self.eatenHeroes.length : 0\n            const fangWeight = self.hasFangs ? 10 : 5\n            const wingWeight = self.hasWings ? 12 : 4\n            return (victimWeight + fangWeight + wingWeight) * self.level > 5 ? 2 : 1\n        }\n    }))\n"
  },
  {
    "path": "__tests__/perf/perf.skip.ts",
    "content": "import { smallScenario, mediumScenario, largeScenario } from \"./scenarios\"\nimport { start } from \"./timer\"\nimport { expect, test } from \"bun:test\"\n\n// TODO: Not sure how this should work. This feels super fragile.\nconst TOO_SLOW_MS = 10000\n\ntest(\"performs well on small scenario\", () => {\n    expect(smallScenario(10).elapsed < TOO_SLOW_MS).toBe(true)\n})\n\ntest(\"performs well on medium scenario\", () => {\n    expect(mediumScenario(10).elapsed < TOO_SLOW_MS).toBe(true)\n})\n\ntest(\"performs well on large scenario\", () => {\n    expect(largeScenario(10, 0, 0).elapsed < TOO_SLOW_MS).toBe(true)\n    expect(largeScenario(10, 10, 0).elapsed < TOO_SLOW_MS).toBe(true)\n    expect(largeScenario(10, 0, 10).elapsed < TOO_SLOW_MS).toBe(true)\n    expect(largeScenario(10, 10, 10).elapsed < TOO_SLOW_MS).toBe(true)\n})\n\ntest(\"timer\", async () => {\n    const go = start()\n\n    await new Promise(resolve => setTimeout(resolve, 2))\n    const lap = go(true)\n\n    await new Promise(resolve => setTimeout(resolve, 2))\n    const d = go()\n    expect(lap).not.toBe(0)\n    expect(d).not.toBe(0)\n    expect(lap).not.toBe(d)\n})\n"
  },
  {
    "path": "__tests__/perf/report.ts",
    "content": "import { smallScenario, mediumScenario, largeScenario } from \"./scenarios\"\n\n// here's what we'll be testing\nconst plan = [\n    \"-----------\",\n    \"Small Model\",\n    \"-----------\",\n    () => smallScenario(100),\n    () => smallScenario(1000),\n    () => smallScenario(10000),\n    () => smallScenario(1000),\n    () => smallScenario(100),\n    \"\",\n    \"------------\",\n    \"Medium Model\",\n    \"------------\",\n    () => mediumScenario(100),\n    () => mediumScenario(1000),\n    () => mediumScenario(10000),\n    () => mediumScenario(1000),\n    () => mediumScenario(100),\n    \"\",\n    \"------------------------\",\n    \"Large Model - 0 children\",\n    \"------------------------\",\n    () => largeScenario(100, 0, 0),\n    () => largeScenario(1000, 0, 0),\n    () => largeScenario(100, 0, 0),\n    \"\",\n    \"-------------------------------------------\",\n    \"Large Model - 10 small & 10 medium children\",\n    \"-------------------------------------------\",\n    () => largeScenario(50, 10, 10),\n    () => largeScenario(250, 10, 10),\n    () => largeScenario(50, 10, 10),\n    \"\",\n    \"-------------------------------------------\",\n    \"Large Model - 100 small & 0 medium children\",\n    \"-------------------------------------------\",\n    () => largeScenario(50, 100, 0),\n    () => largeScenario(250, 100, 0),\n    () => largeScenario(50, 100, 0),\n    \"\",\n    \"-------------------------------------------\",\n    \"Large Model - 0 small & 100 medium children\",\n    \"-------------------------------------------\",\n    () => largeScenario(50, 0, 100),\n    () => largeScenario(250, 0, 100),\n    () => largeScenario(50, 0, 100)\n]\n// burn a few to get the juices flowing\nsmallScenario(1000)\nmediumScenario(500)\nlargeScenario(100, 10, 10)\n// remember when this broke the internet?\nfunction leftPad(value: string, length: number, char = \" \"): string {\n    return value.toString().length < length ? leftPad(char + value, length) : value\n}\n// let's start\nplan.forEach(fn => {\n    // strings get printed, i guess.\n    if (typeof fn === \"string\") {\n        console.log(fn)\n        return\n    }\n    // trigger awkward gc up front if we can\n    if (global.gc) {\n        global.gc()\n    }\n    // run the report\n    const result = fn()\n    // calculate some fields\n    const seconds = leftPad((result.elapsed / 1.0).toLocaleString(), 8)\n    const times = leftPad(`x ${result.count.toLocaleString()}`, 10)\n    const avg = leftPad((result.elapsed / result.count).toFixed(1), 4)\n    // print\n    console.log(`${seconds}ms | ${times} | ${avg}ms avg`)\n})\nconsole.log(\"\")\n"
  },
  {
    "path": "__tests__/perf/scenarios.ts",
    "content": "import { start } from \"./timer\"\nimport { Treasure, Hero, Monster } from \"./fixtures/fixture-models\"\nimport { createTreasure, createHeros, createMonsters } from \"./fixtures/fixture-data\"\n\n/**\n * Covers models with a trivial number of fields.\n *\n * @param count The number of records to create.\n */\nexport function smallScenario(count: number) {\n    const data = createTreasure(count) // ready?\n    const time = start()\n    const converted = data.map(d => Treasure.create(d)) // go\n    const elapsed = time()\n    const sanity = converted.length === count\n    return { count, elapsed, sanity }\n}\n/**\n * Covers models with a moderate number of fields + 1 computed field.\n *\n * @param count The number of records to create.\n */\nexport function mediumScenario(count: number) {\n    const data = createHeros(count) // ready?\n    const time = start()\n    const converted = data.map(d => Hero.create(d)) // go\n    const elapsed = time()\n    const sanity = converted.length === count\n    return { count, elapsed, sanity }\n}\n/**\n * Covers models with a large number of fields.\n *\n * @param count The number of records to create.\n * @param smallChildren The number of small children contained within.\n * @param mediumChildren The number of medium children contained within.\n */\nexport function largeScenario(count: number, smallChildren: number, mediumChildren: number) {\n    const data = createMonsters(count, smallChildren, mediumChildren) // ready?\n    const time = start()\n    const converted = data.map(d => Monster.create(d)) // go\n    const elapsed = time()\n    const sanity = converted.length === count\n    return { count, elapsed, sanity }\n}\n"
  },
  {
    "path": "__tests__/perf/timer.ts",
    "content": "/**\n * Start a timer which return a function, which when called show the\n * number of milliseconds since it started.\n *\n * Passing true will give the current lap time.\n *\n * Example:\n * ```ts\n * const time = start()\n * // 1 second later\n * time() // 1.00\n * // 1 more second later\n * time() // 2.00\n * time(true) // 1.00\n * ```\n */\nexport const start = () => {\n    const started = process.hrtime()\n    let last: [number, number] = [started[0], started[1]]\n    return (lapTime = false) => {\n        const final = process.hrtime(lapTime ? last : started)\n        return Math.round((final[0] * 1e9 + final[1]) / 1e6)\n    }\n}\n"
  },
  {
    "path": "__tests__/setup.ts",
    "content": "import { beforeEach, afterEach, mock } from \"bun:test\"\nimport { resetNextActionId, setLivelinessChecking } from \"../src/internal\"\nimport { configure } from \"mobx\"\n\nbeforeEach(() => {\n    setLivelinessChecking(\"warn\")\n    resetNextActionId()\n})\n\nafterEach(() => {\n    mock.restore()\n\n    // Some tests turn off proxy support, so ensure it's always turned back on\n    configure({\n        useProxies: \"always\"\n    })\n})\n"
  },
  {
    "path": "__tests__/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"rootDir\": \"../\",\n    \"module\": \"commonjs\",\n    \"lib\": [\"es2017\", \"es2020.bigint\", \"dom\"],\n    \"target\": \"es6\",\n    \"sourceMap\": false,\n    \"noEmit\": true\n  },\n  \"include\": [\"**/*.ts\"]\n}\n"
  },
  {
    "path": "__tests__/utils.test.ts",
    "content": "import { MstError } from \"../src/utils\"\nimport { describe, expect, test } from \"bun:test\"\n\ndescribe(\"MstError custom error class\", () => {\n    test(\"with default message\", () => {\n        const error = new MstError()\n        expect(error.message).toBe(\"[mobx-state-tree] Illegal state\")\n    })\n\n    test(\"with custom message\", () => {\n        const customMessage = \"custom error message\"\n        const error = new MstError(customMessage)\n        expect(error.message).toBe(`[mobx-state-tree] ${customMessage}`)\n    })\n\n    test(\"instance of MstError\", () => {\n        const error = new MstError()\n        expect(error).toBeInstanceOf(MstError)\n        expect(error).toBeInstanceOf(Error)\n    })\n})\n"
  },
  {
    "path": "bunfig.toml",
    "content": "[test]\nroot = \"./__tests__\"\npreload = [\"./__tests__/setup.ts\"]\n"
  },
  {
    "path": "changelog.md",
    "content": "The manually-updated changelog has been discontinued. For versions > 4.0.0, go here to view changes:\n\n[https://github.com/mobxjs/mobx-state-tree/releases](https://github.com/mobxjs/mobx-state-tree/releases)\n\n# 4.0.0\n\n[BREAKING CHANGE] MST 4.0 requires MobX 6\n\n# 3.17.3\n\nAdd onValidated support to safeReference [#1540 by @orlovcs](https://github.com/mobxjs/mobx-state-tree/pull/1540)\n\n# 3.17.2\n\nFix incorrect access to global `fail` symbol [#1549](https://github.com/mobxjs/mobx-state-tree/pull/1549)\n\n# 3.17.1\n\nRe-release 3.17.0\n\n# 3.17.0\n\n-   Added experimental helpers toGenerator and toGeneratorFunction. [#1543](https://github.com/mobxjs/mobx-state-tree/pull/1543) by [@fruitraccoon](https://github.com/fruitraccoon)\n\n# 3.16.0\n\n-   Added search field to the docs\n-   Custom types can now receive environments as second argument of the `fromSnapshot` option. [#1410](https://github.com/mobxjs/mobx-state-tree/pull/1410) by [@k-g-a](https://github.com/k-g-a)\n-   Added option `maxHistoryLength` to the `UndoManager`, implements [#1417](https://github.com/mobxjs/mobx-state-tree/issues/1417) through [#1426](https://github.com/mobxjs/mobx-state-tree/pull/1426) by [@tibotiber](https://github.com/tibotiber).\n-   Improved TypeScript typings of `flow`, fixes [#1378](https://github.com/mobxjs/mobx-state-tree/pull/1378) through [#1409](https://github.com/mobxjs/mobx-state-tree/pull/1409) by [@nulladdict](https://github.com/nulladdict)\n-   Fixed that calling `createObservableInstanceIfNeeded` would execute an action, even if the function immediately returned. (significant since the extraneous actions would pollute the mobx dev-tools on mere accesses, eg. by ComplexType.prototype.getValue) Fixes [#1421](https://github.com/mobxjs/mobx-state-tree/issues/1421) trough [#1422](https://github.com/mobxjs/mobx-state-tree/pull/1422) by [@Venryx](https://github.com/Venryx)\n-   Fix issue where `snapshotProcessor.is` does not correctly handle model instances. Fixes [#1494](https://github.com/mobxjs/mobx-state-tree/issues/1494) through [#1495](https://github.com/mobxjs/mobx-state-tree/pull/1495) by [@KevinSjoberg](https://github.com/KevinSjoberg)\n-   Make sure that MST no longer requires `setImmediate` to be present, but fallback to other solutions. [#1501](https://github.com/mobxjs/mobx-state-tree/pull/1501) by [@isaachinman](https://github.com/isaachinman)\n\n# 3.15.0\n\n-   Fix for flow typings. This means that now using flows requires at least TypeScript v3.6 and that `castFlowReturn` becomes deprecated.\n-   Fix for empty models / models with all properties set to optional being able to take any value in TypeScript through [#1269](https://github.com/mobxjs/mobx-state-tree/pull/1269) by [@xaviergonz](https://github.com/xaviergonz).\n\n# 3.14.1\n\n-   Made it possible to force full run-time type-checking (for better error messages) in production builds by setting `ENABLE_TYPE_CHECK=true` as environment variable. Fixes [#1332](https://github.com/mobxjs/mobx-state-tree/pull/1332) through [#1337](https://github.com/mobxjs/mobx-state-tree/pull/1337) by [@OverseePublic](https://github.com/OverseePublic)\n-   Fixed an issue where `Type.is` doesn't behave correctly for types that has snapshot processors. Fixes [#1321](https://github.com/mobxjs/mobx-state-tree/issues/1321) through [#1323](https://github.com/mobxjs/mobx-state-tree/pull/1323) by [@Tucker-Eric](https://github.com/Tucker-Eric)\n-   Changed the implementation of the internal `STNValue` type, to fix TS 3.5.3 compatibility. If somebody notices regressions in the TypeScript integration, please report. Fixes [#1343](https://github.com/mobxjs/mobx-state-tree/issues/1343), [#1307](https://github.com/mobxjs/mobx-state-tree/issues/1307)\n-   Added `acceptsUndefined` as option for `safeReference` so it is more suitable to be used inside arrays/maps, through [#1245](https://github.com/mobxjs/mobx-state-tree/pull/1245) by [@xaviergonz](https://github.com/xaviergonz).\n\n# 3.14.0\n\n-   Fixed a regression with `atomic` middleware with async flows [#1250](https://github.com/mobxjs/mobx-state-tree/issues/1250).\n-   Added filter function to `recordActions` to filter out recording some actions. Also added `recording` and `resume` methods.\n-   Added `getRunningActionContext()` to get the currently executing MST action context (if any). Also added the action context helper functions `isActionContextChildOf()` and `isActionContextThisOrChildOf`.\n-   Reduced type nesting to avoid Typescript 3.4 errors about infinite types. Sadly due to this change `types.create` is no longer smart enough in TS to know if skipping the snapshot parameter is valid or not. Through [#1251](https://github.com/mobxjs/mobx-state-tree/pull/1251) by [@xaviergonz](https://github.com/xaviergonz).\n\n# 3.13.0\n\n-   Fixed `Instance<typeof variable>` not giving the proper type in Typescript when the type included both objects and primitives.\n-   Through PR [#1196](https://github.com/mobxjs/mobx-state-tree/pull/1196) by [@xaviergonz](https://github.com/xaviergonz)\n    -   Added `createActionTrackerMiddleware2`, a more easy to use version of the first one, which makes creating middlewares for both sync and async actions more universal.\n    -   Added an optional filter to `recordPatches` to be able to skip recording certain patches.\n    -   `atomic` now uses the new `createActionTrackerMiddleware2`.\n    -   `UndoManager` fixes and improvements:\n        -   Uses the new `createActionTrackerMiddleware2`.\n        -   Added `clearUndo` and `clearRedo` to only clear those.\n        -   Added `undoLevels` and `redoLevels` to know how many undo/redo actions are available.\n        -   Made undo manager actions atomic, so they won't actually do any partial changes if for some reason they fail.\n        -   Fix for `withoutUndo` so it will only skip recording what is inside, not the whole action - fixes [#1195](https://github.com/mobxjs/mobx-state-tree/issues/1195).\n\n# 3.12.2\n\n-   Added more output formats for the library (common-js minified version and umd minified version). Note that now the umd version will be the development version while the new umd.min version will be the production version. This change is to keep it in sync with the parent mobx package. Also the npm package is now leaner since it mistakenly included separatedly compiled js files and source maps.\n\n# 3.12.1\n\n-   Fixed a regression with `getEnv` sometimes not returning the proper environment.\n-   Fixed an issue where `map.put` would not work with snapshots of types with an optional id [#1131](https://github.com/mobxjs/mobx-state-tree/issues/1131) through [#1226](https://github.com/mobxjs/mobx-state-tree/pull/1226) by [@xaviergonz](https://github.com/xaviergonz).\n\n# 3.12.0\n\n-   Added `TypeOfValue<typeof variable>` to extract the type of a complex (non primitive) variable in Typescript.\n-   Fixed some Typescript issues with optional arrays [#1218](https://github.com/mobxjs/mobx-state-tree/issues/1218) through [#1229](https://github.com/mobxjs/mobx-state-tree/pull/1229) by [@xaviergonz](https://github.com/xaviergonz)\n-   Added `getNodeId` to get the internal unique node id for an instance [#1168](https://github.com/mobxjs/mobx-state-tree/issues/1168) through [#1225](https://github.com/mobxjs/mobx-state-tree/pull/1225) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fixed nodes being `pop`/`shift`/`splice` from an array not getting properly destroyed through [#1205](https://github.com/mobxjs/mobx-state-tree/pull/1205) by [@xaviergonz](https://github.com/xaviergonz). Not that this means that in order to access the returned dead nodes data without getting a liveliness error/warning then the returned dead nodes have to be either cloned (`clone`) or their snapshots (`getSnapshot`) have to be used first.\n\n# 3.11.0\n\n-   Added an optional third argument to `types.optional` that allows to set alternative optional values other than just `undefined` through [#1192](https://github.com/mobxjs/mobx-state-tree/pull/1192) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fixed detaching arrays/maps killing their children [#1173](https://github.com/mobxjs/mobx-state-tree/issues/1173) through [#1175](https://github.com/mobxjs/mobx-state-tree/pull/1175) by [@xaviergonz](https://github.com/xaviergonz)\n-   Added `types.snapshotProcessor` [#947](https://github.com/mobxjs/mobx-state-tree/issues/947) through [#1165](https://github.com/mobxjs/mobx-state-tree/pull/1165) by [@xaviergonz](https://github.com/xaviergonz). This feature will eventually deprecate `postProcessSnapshot` and `preProcessSnapshot` from models in a next major version.\n-   Performance improvement for event handlers so they consume less RAM through [#1160](https://github.com/mobxjs/mobx-state-tree/pull/1160) by [@xaviergonz](https://github.com/xaviergonz)\n-   Make liveliness errors give more info to trace their cause [#1142](https://github.com/mobxjs/mobx-state-tree/issues/1142) through [#1147](https://github.com/mobxjs/mobx-state-tree/pull/1147) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.10.2\n\n-   Fixed a regression regarding json paths not being correctly rooted to the base [#1128](https://github.com/mobxjs/mobx-state-tree/issues/1128) through [#1146](https://github.com/mobxjs/mobx-state-tree/pull/1146) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.10.1\n\n-   Fixed mobx 5.9.0 compatibility [#1143](https://github.com/mobxjs/mobx-state-tree/issues/1143) through [#1144](https://github.com/mobxjs/mobx-state-tree/pull/1144) by [@xaviergonz](https://github.com/xaviergonz)\n-   Made liveliness checking in warn mode log an error so the stack trace can be seen [#1142](https://github.com/mobxjs/mobx-state-tree/issues/1142) through [#1145](https://github.com/mobxjs/mobx-state-tree/pull/1145) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fixed JSON path escaping, where '/' and '~' were incorrectly being encoded/decoded as '~0' and '~1' rather than '~1' and '~0'. Also fixed empty keys not being handled correctly by JSON patches [#1128](https://github.com/mobxjs/mobx-state-tree/issues/1128). Fixed through [#1129](https://github.com/mobxjs/mobx-state-tree/pull/1129) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.10.0\n\n-   Fix for safeReference doesn't work when multiple nodes reference a single reference that gets deleted [#1115](https://github.com/mobxjs/mobx-state-tree/issues/1115) through [#1121](https://github.com/mobxjs/mobx-state-tree/pull/1121) by [@xaviergonz](https://github.com/xaviergonz)\n-   Little fix for `castFlowReturn` not typecasting the promise to its actual result.\n-   Made `isAlive(node)` reactive, so it can be reacted upon through [#1100](https://github.com/mobxjs/mobx-state-tree/pull/1100) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fix for unaccessed nodes not unregistering their identifiers [#1112](https://github.com/mobxjs/mobx-state-tree/issues/1112) through [#1113](https://github.com/mobxjs/mobx-state-tree/pull/1113) by [@xaviergonz](https://github.com/xaviergonz)\n-   Added `clear()` to `UndoManager` middleware through [#1118](https://github.com/mobxjs/mobx-state-tree/pull/1118) by [@chemitaxis](https://github.com/chemitaxis)\n\n# 3.9.0\n\n-   TypeScript 3.0 or later is now required when using TypeScript. This brings some improvements:\n    -   `flow` arguments and return types are now correctly inferred automatically. One exception is when the last return of a `flow` is a `Promise`. In these cases `castFlowReturn(somePromise)` needs to be used so the return type can be inferred properly.\n    -   `create` method is now smart enough to warn when no snapshot argument is provided on types that have some mandatory properties.\n-   Added `setLivelinessChecking` and `getLivelinessChecking`, the old `setLivelynessChecking` will eventually be deprecated.\n-   Added `onInvalidated` option for references and `types.safeReference` (see readme) through [#1091](https://github.com/mobxjs/mobx-state-tree/pull/1091) by [@xaviergonz](https://github.com/xaviergonz)\n-   Added `tryReference` and `isValidReference` to use references that might be no longer pointing to any nodes in a safe way through [#1087](https://github.com/mobxjs/mobx-state-tree/pull/1087) by [@xaviergonz](https://github.com/xaviergonz)\n-   Readded `IComplexType` for backwards compatibility.\n\n# 3.8.1\n\n-   Fixed non-initialized nodes not being destroyed [#1080](https://github.com/mobxjs/mobx-state-tree/issues/1080) through [#1082](https://github.com/mobxjs/mobx-state-tree/pull/1082) by [@k-g-a](https://github.com/k-g-a)\n-   Fixed a memory leak in createActionTrackingMiddleware when using flow [#1083](https://github.com/mobxjs/mobx-state-tree/issues/1083) through [#1084](https://github.com/mobxjs/mobx-state-tree/pull/1084) by [@robinfehr](https://github.com/robinfehr)\n\n# 3.8.0\n\n-   Added castToSnapshot/castToReferenceSnapshot methods for TypeScript and fixed some TypeScript typings not being properly detected when using SnapshotIn types through [#1074](https://github.com/mobxjs/mobx-state-tree/pull/1074) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fixed redux middleware throwing an error when a flow is called before it is connected [#1065](https://github.com/mobxjs/mobx-state-tree/issues/1065) through [#1079](https://github.com/mobxjs/mobx-state-tree/pull/1079) by [@mkramb](https://github.com/mkramb) and [@xaviergonz](https://github.com/xaviergonz)\n-   Made `addDisposer` return the passed disposer through [#1059](https://github.com/mobxjs/mobx-state-tree/pull/1059) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.7.1\n\n-   Fixed references to nodes being broken after the node was replaced [#1052](https://github.com/mobxjs/mobx-state-tree/issues/1052), plus speed up of reference resolving when using IDs through [#1053](https://github.com/mobxjs/mobx-state-tree/pull/1053) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.7.0\n\n-   Middleware events now also contain `allParentIds` (chain of causing ids, from root until (excluding) current)\n-   Improved redux dev tools integration, now supporting flows and showing action chains through [#1035](https://github.com/mobxjs/mobx-state-tree/pull/1035) based on a fix by [@bourquep](https://github.com/bourquep)\n\n# 3.6.0\n\n-   Made type Typescript compilation when 'declarations' is set to true + type completion faster thanks to some type optimizations through [#1043](https://github.com/mobxjs/mobx-state-tree/pull/1043) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fix for array reconciliation of union types with ids [#1045](https://github.com/mobxjs/mobx-state-tree/issues/1045) through [#1047](https://github.com/mobxjs/mobx-state-tree/pull/1047) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fixed bug where the eager option for the union type defaulted to true when no options argument was passed but false when it was passed. Now they both default to true when not specified. Fixed through [#1046](https://github.com/mobxjs/mobx-state-tree/pull/1046) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.5.0\n\n-   Fix for afterCreate/afterAttach sometimes throwing an exception when a node was created as part of a view/computed property [#967](https://github.com/mobxjs/mobx-state-tree/issues/967) through [#1026](https://github.com/mobxjs/mobx-state-tree/pull/1026) by [@xaviergonz](https://github.com/xaviergonz). Note that this fix will only work if your installed peer mobx version is >= 4.5.0 or >= 5.5.0\n-   Fix for cast method being broken in Typescript 3.1.1 through [#1028](https://github.com/mobxjs/mobx-state-tree/pull/1028) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.4.0\n\n-   Added getPropertyMembers(typeOrNode) through [#1016](https://github.com/mobxjs/mobx-state-tree/pull/1016) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fix for preProcessSnapshot not copied on compose [#613](https://github.com/mobxjs/mobx-state-tree/issues/613) through [#1013](https://github.com/mobxjs/mobx-state-tree/pull/1013) by [@theRealScoobaSteve](https://github.com/theRealScoobaSteve)\n-   Fix for actions sometimes failing to resolve this to self through [#1014](https://github.com/mobxjs/mobx-state-tree/pull/1014) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fix for preProcessSnapshot not copied on compose [#613](https://github.com/mobxjs/mobx-state-tree/issues/613) through [#1013](https://github.com/mobxjs/mobx-state-tree/pull/1013) by [@theRealScoobaSteve](https://github.com/theRealScoobaSteve)\n-   Improvements to the bookshop example through [#1009](https://github.com/mobxjs/mobx-state-tree/pull/1009) by [@programmer4web](https://github.com/programmer4web)\n-   Fix for a regression with optional identifiers [#1019](https://github.com/mobxjs/mobx-state-tree/issues/1019) through [#1020](https://github.com/mobxjs/mobx-state-tree/pull/1020) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.3.0\n\n-   Fix for references sometimes not intializing its parents [#993](https://github.com/mobxjs/mobx-state-tree/issues/993) through [#997](https://github.com/mobxjs/mobx-state-tree/pull/997) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fix for TS3 issues with reference type [#994](https://github.com/mobxjs/mobx-state-tree/issues/994) through [#995](https://github.com/mobxjs/mobx-state-tree/pull/995) by [@xaviergonz](https://github.com/xaviergonz)\n-   types.optional will now throw if an instance is directly passed as default value [#1002](https://github.com/mobxjs/mobx-state-tree/issues/1002) through [#1003](https://github.com/mobxjs/mobx-state-tree/pull/1003) by [@xaviergonz](https://github.com/xaviergonz)\n-   Doc fixes and improvements by [@AjaxSolutions](https://github.com/AjaxSolutions) and [@agilgur5](https://github.com/agilgur5)\n\n# 3.2.4\n\n-   Further improvements for Typescript support for enumeration by [@xaviergonz](https://github.com/xaviergonz)\n-   Smaller generated .d.ts files through [#990](https://github.com/mobxjs/mobx-state-tree/pull/990) by [@xaviergonz](https://github.com/xaviergonz)\n-   Fix for exception when destroying children of types.maybe through [#985](https://github.com/mobxjs/mobx-state-tree/pull/985) by [@dsabanin](https://github.com/dsabanin)\n\n# 3.2.3\n\n-   Fixed incorrect typing generation for mst-middlewares [#979](https://github.com/mobxjs/mobx-state-tree/issues/979)\n\n# 3.2.2\n\n-   Fixes for the reconciliation algorithm of arrays [#928](https://github.com/mobxjs/mobx-state-tree/issues/928) through [#960](https://github.com/mobxjs/mobx-state-tree/pull/960) by [@liuqiang1357](https://github.com/liuqiang1357)\n-   Better Typescript support for enumeration, compose, union, literal and references by [@xaviergonz](https://github.com/xaviergonz)\n-   Updated dependencies to latest versions by [@xaviergonz](https://github.com/xaviergonz)\n-   [Internal] Cleanup 'createNode' and related codepaths through [#962](https://github.com/mobxjs/mobx-state-tree/pull/962) by [@k-g-a](https://github.com/k-g-a)\n\n# 3.2.1\n\n-   Fix for wrong generated TS import [#968](https://github.com/mobxjs/mobx-state-tree/issues/968) through [#969](https://github.com/mobxjs/mobx-state-tree/pull/969) by [@k-g-a](https://github.com/k-g-a)\n\n# 3.2.0\n\n-   Made the internal CreationType/SnapshotType/Type official via the new [`SnapshotIn`, `SnapshotOut`, `Instance` and `SnapshotOrInstance<typeof X>`](README.md#typeScript-and-mst) by [@xaviergonz](https://github.com/xaviergonz)\n-   A new [`cast` method](README.md#snapshots-can-be-used-to-write-values) that makes automatic casts from instances/input snapshots for assignments by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.1.1\n\n-   Fixed typings of `getParent` and `getRoot`. Fixes [#951](https://github.com/mobxjs/mobx-state-tree/issues/951) through [#953](https://github.com/mobxjs/mobx-state-tree/pull/953) by [@xaviergonz](https://github.com/xaviergonz)\n\n# 3.1.0\n\n-   Fixed issue where snapshot post-processors where not always applied. Fixes [#926](https://github.com/mobxjs/mobx-state-tree/issues/926), [#961](https://github.com/mobxjs/mobx-state-tree/issues/961), through [#959](https://github.com/mobxjs/mobx-state-tree/pull/959) by [@k-g-a](https://github.com/k-g-a)\n\n# 3.0.3\n\n-   Fixed re-adding the same objects to an array. Fixes [#928](https://github.com/mobxjs/mobx-state-tree/issues/928) through [#949](https://github.com/mobxjs/mobx-state-tree/pull/949) by [@Krivega](https://github.com/Krivega)\n\n# 3.0.2\n\n-   Introduced `types.integer`! By [@jayarjo](https://github.com/jayarjo) through [#935](https://github.com/mobxjs/mobx-state-tree/pull/935)\n-   Improved typescript typings, several fixes to the type system. Awesome contribution by [@xaviergonz](https://github.com/xaviergonz) through [#937](https://github.com/mobxjs/mobx-state-tree/pull/937) and [#945](https://github.com/mobxjs/mobx-state-tree/pull/945). Fixes [#922](https://github.com/mobxjs/mobx-state-tree/issues/922), [#930](https://github.com/mobxjs/mobx-state-tree/issues/930), [#932](https://github.com/mobxjs/mobx-state-tree/issues/932), [#923](https://github.com/mobxjs/mobx-state-tree/issues/923)\n-   Improved handling of `types.late`\n\n# 3.0.1 (retracted)\n\n# 3.0.0\n\nWelcome to MobX-state-tree! This version introduces some breaking changes, but nonetheless is an recommended upgrade as all changes should be pretty straight forward and there is no reason anymore to maintain the 2.x range (3.0 is still compatible with MobX 4)\n\n## Most important changes\n\nMST 3 is twice as fast in initializing trees with half the memory consumption compared to version 2:\n\nRunning `yarn speedtest` on Node 9.3:\n\n|                 | MST 2  | MST 3  |\n| --------------- | ------ | ------ |\n| Time            | 24sec  | 12 sec |\n| Mem             | 315MB  | 168MB  |\n| Size (min+gzip) | 14.1KB | 15.0KB |\n\nBeyond that, MST 3 uses TypeScript 2.8, which results in more accurate TypeScript support.\n\nThe type system has been simplified and improved in several areas. Several open issues around maps and (numeric) keys have been resolved. The `frozen` type can now be fully typed. See below for the full details.\n\nAlso, the 'object has died' exception can be suppressed now. One should still address it, but at least it won't be a show-stopper from now on.\n\n## Changes in the type system\n\n-   **[BREAKING]** `types.identifier` can no longer be parameterized with either `types.string` or `types.number`. So instead of `types.identifier()` use `types.identifier`. Identifiers are now always normalized to strings. This reflects what was already happening internally and solves a lot of edge cases. To use numbers as identifiers, `types.identifierNumber` (instead of `types.identifier(types.number)`) can be used, which serializes it's snapshot to a number, but will internally work like a string based identifier\n-   **[BREAKING]** `types.maybe` now serializes to / from `undefined` by default, as it is more and more the common best practice to don't use `null` at all and MST follows this practice. Use `types.maybeNull` for the old behavior (see [#830](https://github.com/mobxjs/mobx-state-tree/issues/830))\n-   **[BREAKING]** `types.frozen` is now a function, and can now be invoked in a few different ways:\n    1.  `types.frozen()` - behaves the same as `types.frozen` in MST 2.\n    2.  `types.frozen(SubType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type. Note that the type will not actually be instantiated, so it can only be used to check the _shape_ of the data. Adding views or actions to `SubType` would be pointless.\n    3.  `types.frozen(someDefaultValue)` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field\n    4.  `types.frozen<TypeScriptType>()` - provide a typescript type, to help in strongly typing the field (design time only)\n-   It is no longer necessary to wrap `types.map` or `types.array` in `types.optional` when used in a `model` type, `map` and `array` are now optional by default when used as property type. See [#906](https://github.com/mobxjs/mobx-state-tree/issues/906)\n-   **[BREAKING]** `postProcessSnapshot` can no longer be declared as action, but, like `preProcessSnapshot`, needs to be defined on the type rather than on the instance.\n-   **[BREAKING]** `types.union` is now eager, which means that if multiple valid types for a value are encountered, the first valid type is picked, rather then throwing. #907 / #804, `dispatcher` param => option,\n\n## Other improvements\n\n-   **[BREAKING]** MobX-state-tree now requires at least TypeScript 2.8 when using MST with typescript. The type system has been revamped, and should now be a lot more accurate, especially concerning snapshot types.\n-   **[BREAKING]** `map.put` will now return the inserted node, rather than the map itself. This makes it easier to find objects for which the identifier is not known upfront. See [#766](https://github.com/mobxjs/mobx-state-tree/issues/766) by [k-g-a](https://github.com/k-g-a)\n-   **[BREAKING]** The order of firing hooks when instantiating has slighlty changed, as the `afterCreate` hook will now only be fired upon instantiation of the tree node, which now happens lazily (on first read / action). The internal order in which hooks are fired within a single node has remained the same. See [#845](https://github.com/mobxjs/mobx-state-tree/issues/845) for details\n-   Significantly improved the performance of constructing MST trees. Significantly reduced the memory footprint of MST. Big shoutout to the relentless effort by [k-g-a](https://github.com/k-g-a) to optimize all the things! See [#845](https://github.com/mobxjs/mobx-state-tree/issues/845) for details.\n-   Introduced `setLivelynessChecking(\"warn\" | \"ignore\" | \"error\")`, this can be used to customize how MST should act when one tries to read or write to a node that has already been removed from the tree. The default behavior is `warn`.\n-   Improved the overloads of `model.compose`, see [#892](https://github.com/mobxjs/mobx-state-tree/pull/892) by [t49tran](https://github.com/t49tran)\n-   Fixed issue where computed properties based on `getPath` could return stale results, fixes [#917](https://github.com/mobxjs/mobx-state-tree/issues/917)\n-   Fixed issue where onAction middleware threw on dead nodes when attachAfter option was used\n-   Fixed several issues with maps and numeric identifiers, such as [#884](https://github.com/mobxjs/mobx-state-tree/issues/884) and [#826](https://github.com/mobxjs/mobx-state-tree/issues/826)\n\n## TL,DR Migration guide\n\n-   `types.identifier(types.number)` => `types.identifierNumber`\n-   `types.identifier()` and `types.identifier(types.string)` =>`types.identifier`\n-   `types.frozen` => `types.frozen()`\n-   `types.maybe(x)` => `types.maybeNull(x)`\n-   `postProcessSnapshot` should now be declared on the type instead of as action\n\n# 2.2.0\n\n-   Added support for MobX 5. Initiative by [@jeffberry](https://github.com/jeffberry) through [#868](https://github.com/mobxjs/mobx-state-tree/pull/868/files). Please note that there are JavaScript engine restrictions for MobX 5 (no Internet Explorer, or React Native Android). If you need to target those versions please keep using MobX 4 as peer dependency (MST is compatible with both)\n-   Reduced memory footprint with ~10-20%, by [k-g-a](https://github.com/k-g-a) through [#872](https://github.com/mobxjs/mobx-state-tree/pull/872)\n-   Fixed issue where undo manager was not working correctly for non-root stores, by [marcofugaro](https://github.com/marcofugaro) trough [#875](https://github.com/mobxjs/mobx-state-tree/pull/875)\n\n# 2.1.0\n\n-   Fixed issue where default values of `types.frozen` where not applied correctly after apply snapshot. [#842](https://github.com/mobxjs/mobx-state-tree/pull/842) by [SirbyAlive](https://github.com/SirbyAlive). Fixes [#643](https://github.com/mobxjs/mobx-state-tree/issues/634)\n-   Fixed issue where empty patch sets resulted in in unnecessary history items. [#838](https://github.com/mobxjs/mobx-state-tree/pull/838) by [chemitaxis](https://github.com/chemitaxis). Fixes [#837](https://github.com/mobxjs/mobx-state-tree/issues/837)\n-   `flow`s of destroyed nodes can no 'safely' resume. [#798](https://github.com/mobxjs/mobx-state-tree/pull/798/files) by [Bnaya](https://github.com/Bnaya). Fixes [#792](https://github.com/mobxjs/mobx-state-tree/issues/792)\n-   Made sure the type `Snapshot` is exposed. [#821](https://github.com/mobxjs/mobx-state-tree/pull/821) by [dsabanin](https://github.com/dsabanin)\n-   Fix: the function parameter was incorrectly typed as non-optional. [#851](https://github.com/mobxjs/mobx-state-tree/pull/851) by [abruzzihraig](https://github.com/abruzzihraig)\n\n# 2.0.5\n\n-   It is now possible to get the snapshot of a node without triggering the `postProcessSnapshot` hook. See [#745](https://github.com/mobxjs/mobx-state-tree/pull/745) for details. By @robinfehr\n-   Introduced `getParentOfType` and `hasParentOfType`. See [#767](https://github.com/mobxjs/mobx-state-tree/pull/767) by @k-g-a\n-   Fixed issue where running `typeCheck` accidentally logged typecheck errors to the console. Fixes [#781](https://github.com/mobxjs/mobx-state-tree/issues/781)\n\n# 2.0.4\n\n-   Removed accidental dependency on mobx\n\n# 2.0.3\n\n-   Fixed issue where middleware that changed arguments wasn't properly picked up. See [#732](https://github.com/mobxjs/mobx-state-tree/pull/732) by @robinfehr. Fixes [#731](https://github.com/mobxjs/mobx-state-tree/issues/731)\n-   Fixed reassigning to a custom type from a different type in a union silently failing. See [#737](https://github.com/mobxjs/mobx-state-tree/pull/737) by @univerio. Fixes [#736](https://github.com/mobxjs/mobx-state-tree/issues/736)\n-   Fixed typings issue with TypeScript 2.8. See [#740](https://github.com/mobxjs/mobx-state-tree/pull/740) by @bnaya.\n-   Fixed undo manager apply grouped patches in the wrong order. See [#755](https://github.com/mobxjs/mobx-state-tree/pull/755) by @robinfehr. Fixes [#754](https://github.com/mobxjs/mobx-state-tree/issues/754)\n\n# 2.0.2\n\n-   Fixed bidirectional references from nodes to nodes, see [#728](https://github.com/mobxjs/mobx-state-tree/pull/728) by @robinfehr\n-   `joinJsonPath` and `splitJsonPath` are now exposed as utilities, see [#724](https://github.com/mobxjs/mobx-state-tree/pull/724) by @jjrv\n-   Several documentation and example fixes\n\n# 2.0.1\n\n-   Fixed typings for maps of maps [#704](https://github.com/mobxjs/mobx-state-tree/pull/704) by @xaviergonz\n-   Fixed dependency issue in `mst-middlewares` package\n\n# 2.0.0\n\n**Breaking changes**\n\n-   MobX-state-tree now requires MobX 4.0 or higher\n-   Identifiers are now internally always normalized to strings. This also means that adding an object with an number identifier to an observable map, it should still be requested back as string. In general, we recommend to always use string based identifiers to avoid confusion.\n-   Due to the changes in Mobx 4.0, `types.map(subType).keys()` will return `Iterator` instead of `ObservableArrays`. In order to address this issue, wrap the keys with `Array.from()`.\n\n# 1.4.0\n\n**Features**\n\n-   It is now possible to create [custom primitive(like) types](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/API/README.md#custom)! Implements [#673](https://github.com/mobxjs/mobx-state-tree/issues/673) through [#689](https://github.com/mobxjs/mobx-state-tree/pull/689)\n-   [`getIdentifier`](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/API/README.md#getidentifier) is now exposed as function, to get the identifier of a model instance (if any). Fixes [#674](https://github.com/mobxjs/mobx-state-tree/issues/674) through [#678](https://github.com/mobxjs/mobx-state-tree/pull/678) by TimHollies\n-   Writing [middleware](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md) has slightly changed, to make it less error prone and more explicit whether a middleware chain should be aborted. For details, see [#675](https://github.com/mobxjs/mobx-state-tree/pull/675) by Robin Fehr\n-   It is now possible to configure whether [attached middleware](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/API/README.md#addmiddleware) should be triggered for the built-in hooks / operations. [#653](https://github.com/mobxjs/mobx-state-tree/pull/653) by Robin Fehr\n-   We exposed an [api](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/API/README.md#getmembers) to perform reflection on model instances. [#649](https://github.com/mobxjs/mobx-state-tree/pull/649) by Robin Fehr\n\n**Fixes**\n\n-   Fixed a bug where items in maps where not properly reconciled when the `put` operation was used. Fixed [#683](https://github.com/mobxjs/mobx-state-tree/issues/683) and [#672](https://github.com/mobxjs/mobx-state-tree/issues/672) through [#693](https://github.com/mobxjs/mobx-state-tree/pull/693)\n-   Fixed issue where trying to resolve a path would throw exceptions. Fixed [#686](https://github.com/mobxjs/mobx-state-tree/issues/686) through [#692](https://github.com/mobxjs/mobx-state-tree/pull/692)\n-   In non production builds actions and views on models can now be replaced, to simplify mocking. Fixes [#646](https://github.com/mobxjs/mobx-state-tree/issues/646) through [#690](https://github.com/mobxjs/mobx-state-tree/pull/690)\n-   Fixed bug where `tryResolve` could leave a node in a corrupt state. [#668](https://github.com/mobxjs/mobx-state-tree/pull/668) by dnakov\n-   Fixed typings for TypeScript 2.7, through [#667](https://github.com/mobxjs/mobx-state-tree/pull/667) by Javier Gonzalez\n-   Several improvements to error messages\n\n# 1.3.1\n\n-   Fixed bug where `flows` didn't properly batch their next ticks properly in actions, significantly slowing processes down. Fixes [#563](<[#563](https://github.com/mobxjs/mobx-state-tree/issues/563)>)\n\n# 1.3.0\n\n-   Significantly improved the undo/redo manager. The undo manager now supports groups. See [#504](https://github.com/mobxjs/mobx-state-tree/pull/504) by @robinfehr! See the [updated docs](https://github.com/mobxjs/mobx-state-tree/blob/master/packages/mst-middlewares/README.md#undomanager) for more details.\n-   Significantly improved performance, improvements of 20% could be expected, but changes of course per case. See [#553](https://github.com/mobxjs/mobx-state-tree/pull/553)\n-   Implemented `actionLogger` middleware, which logs most events for async actions\n-   Slightly changed the order in which life cycle hooks are fired. `afterAttach` will no fire first on the parent, then on the children. So, unlike `afterCreate`, in `afterAttach` one can assume in `afterAttach that the parent has completely initialized.\n\n# 1.2.1\n\n-   1.2.0 didn't seem to be released correctly...\n\n# 1.2.0\n\n-   Introduced customizable reference types. See the [reference and identifiers](https://github.com/mobxjs/mobx-state-tree#references-and-identifiers) section.\n-   Introduced `model.volatile` to more easily declare and reuse volatile instance state. Volatile state can contain arbitrary data, is shallowly observable and, like props, cannot be modified without actions. See [`model.volatile`](https://github.com/mobxjs/mobx-state-tree#model-volatile) for more details.\n\n# 1.1.1\n\n### Improvements\n\n-   Fixed an issue where nodes where not always created correctly, see #534. Should fix #513 and #531.\n-   All tests are now run in both PROD and non PROD configurations, after running into some bugs that only occurred in production builds.\n-   Some internal optimizations have been applied (and many more will follow). Like having internal leaner node for immutable data. See #474\n-   A lot of minor improvements on the docs\n\n# 1.1.0\n\n### Improvements\n\n-   The concept of process (asynchronous actions) has been renamed to flows. (Mainly to avoid issues with bundlers)\n-   We changed to a lerna setup which allows separately distributing middleware and testing examples with more ease\n-   Every MST middleware is now shipped in a separate package named `mst-middlewares`. They are now written in TypeScript and fully transpiled to ES5 to avoid problems with uglifyjs in create-react-app bundling.\n-   Introduced `createActionTrackingMiddleware`, this significantly simplifies writing middleware for common scenarios. Especially middleware that deals with asynchronous actions (flows)\n-   Renamed `process` to `flow`. Deprecated `process`.\n-   **BREAKING** As a result some middleware event names have also been changed. If you have custom middlewares this change might affect you. Rename middleware event type prefixes starting with `process` to now start with `flow`.\n\n### Fixes\n\n-   Fixed nested maps + environments not working correctly, [#447](https://github.com/mobxjs/mobx-state-tree/pull/447) by @xaviergonz\n-   Improved typescript typings for enumerations, up to 50 values are now supported [#424](https://github.com/mobxjs/mobx-state-tree/pull/447) by @danielduwaer\n\n# 1.0.2\n\n-   Introduced `modelType.extend` which allows creating views and actions with shared state.\n\n# 1.0.1\n\n### Features\n\n-   Added the middlewares `atomic` and types `TimeTraveller`, `UndoManager`. Check out the [docs](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md)!\n-   Introduced `createActionTrackingMiddleware` to simplify the creation of middleware that support complex async processes\n-   exposed `typecheck(type, value)` as public api (will ignore environment flags)\n\n### Improvements\n\n-   `getEnv` will return an empty object instead of throwing when a tree was initialized without environment\n-   Fixed issue where patches generated for nested maps were incorrect (#396)\n-   Fixed the escaping of (back)slashes in JSON paths (#405)\n-   Improved the algorithm that reconcile items in an array (#384)\n-   Assigning a node that has an environment to a parent is now allowed, as long as the environment is strictly the same (#387)\n-   Many minor documentation improvements. Thanks everybody who created a PR!\n\n# 1.0.0\n\nNo changes\n\n# 0.12.0\n\n-   **BREAKING** The redux utilities are no longer part of the core package, but need to be imported from `mobx-state-tree/middleware/redux`.\n\n# 0.11.0\n\n### Breaking changes\n\n-   **BREAKING** `onAction` middleware no longer throws when encountering unserializable arguments. Rather, it serializes a struct like `{ $MST_UNSERIALIZABLE: true, type: \"someType\" }`. MST Nodes are no longer automatically serialized. Rather, one should either pass 1: an id, 2: a (relative) path, 3: a snapshot\n-   **BREAKING** `revertPatch` has been dropped. `IReversableJsonPatch` is no longer exposed, instead use the inverse patches generated by `onPatch`\n-   **BREAKING** some middleware events have been renamed: `process_yield` -> `process_resume`, `process_yield_error` -> `process_resume_error`, to make it less confusing how these events relate to `yield` statements.\n-   **BREAKING** patchRecorder's field `patches` has been renamed to `rawPatches,`cleanPatches`to`patches`, and`inversePatches` was added.\n\n### New features\n\n-   Introduced `decorate(middleware, action)` to easily attach middleware to a specific action\n-   Handlers passed to `onPatch(handler: (patch, inversePatch) => void)` now receive as second argument the inverse patch of the emitted patch\n-   `onAction` lister now supports an `attachAfter` parameter\n-   Middleware events now also contain `parentId` (id of the causing action, `0` if none) and `tree` (the root of context)\n\n### Fixes\n\n-   ReduxDevTools connection is no longer one step behind [#287](https://github.com/mobxjs/mobx-state-tree/issues/287)\n-   Middleware is no longer run as part of the transaction of the targeted action\n-   Fixed representation of `union` types in error messages\n\n# 0.10.3\n\n-   **BREAKISH** Redefining lifecycle hooks will now automatically compose them, implements [#252](https://github.com/mobxjs/mobx-state-tree/issues/252)\n-   Added dev-only checks, typecheck will be performed only in dev-mode and top-level API-calls will be checked.\n-   The internal types `IMiddleWareEvent`, `IMiddlewareEventType`, `ISerializedActionCall` are now exposed (fixes [#315](https://github.com/mobxjs/mobx-state-tree/issues/315))\n\n# 0.10.2\n\n-   Object model instances no longer share a prototype.\n\n# 0.10.1\n\n-   Removed accidental dependency on the codemod\n\n# 0.10.0\n\n-   **BREAKING** the syntax to define model types has been updated. See the [updated docs](https://github.com/mobxjs/mobx-state-tree#creating-models) or the original proposal:[#282](https://github.com/mobxjs/mobx-state-tree/pull/286), but no worries, theres a codemod! :D\n-   **BREAKING** `preProcessSnapshot` hook is no longer a normal hook that can be defined as action. Instead, it should be defined on the type using `types.model(...).preProcessSnapshot(value => value)`\n-   **BREAKING** Asynchronous process should now be defined using `process`. See this [example](https://github.com/mobxjs/mobx-state-tree/blob/adba1943af263898678fe148a80d3d2b9f8dbe63/examples/bookshop/src/stores/BookStore.js#L25) or the [asynchronous action docs](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/async-actions.md).\n\n**How to run the codemod?**\n\nThe codemod is provided as npm package command line tool. It has been written using the TypeScript parser, so it will successfully support either TS or regular JavaScript source files.\n\nTo run the codemod, you need to first install it globally by `npm install -g mst-codemod-to-0.10`.\nAfter that, the `mst-codemod-to-0.10` command will be available in your command line.\n\nTo perform the codemod, you need to call in your command line `mst-codemod-to-0.10` followed by the filename you want to codemod. A `.bak` file with the original source will be created for backup purposes, and the file you provided will be updated to the new syntax! Have fun!\n\nPS: You could also use `npx` instead of installing the codemod globally! :)\n\n# 0.9.5\n\n-   Asynchronous actions are now a first class concept in mobx-state-tree. See the [docs](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/async-actions.md)\n\n# 0.9.4\n\n-   Introduced `types.null` and `types.undefined`\n-   Introduced `types.enumeration(name?, options)`\n\n# 0.9.3\n\n-   Fix `note that a snapshot is compatible` when assigning a type to an optional version of itself\n-   Fix error when deleting a non existing item from a map [#255](https://github.com/mobxjs/mobx-state-tree/issues/255)\n-   Now all required TypeScript interfaces are exported in the main mobx-state-tree package [#256](https://github.com/mobxjs/mobx-state-tree/issues/256)\n\n# 0.9.2\n\nIntroduced the concept of reverse patches, see [#231](https://github.com/mobxjs/mobx-state-tree/pull/231/)\n\n-   Introduced the `revertPatch` operation, that takes a patch or list of patches, and reverse applies it to the target.\n-   `onPatch` now takes a second argument, `includeOldValue`, defaulting to `false`, which, if set to true, includes in the patch any value that is being overwritten as result of the patch. Setting this option to true produces patches that can be used with `revertPatch`\n-   `patchRecorder` now introduces additional fields / methods to be able to reverse apply changes: `patchRecorder.cleanPatches`, `patchRecorder.undo`\n\n# 0.9.1\n\n-   Applying a snapshot or patches will now emit an action as well. The name of the emitted action will be `@APPLY_PATCHES`resp `@APPLY_SNAPSHOT`. See [#107](https://github.com/mobxjs/mobx-state-tree/issues/107)\n-   Fixed issue where same Date instance could'nt be used two times in the same state tree [#229](https://github.com/mobxjs/mobx-state-tree/issues/229)\n-   Fixed issue with reapplying snapshots to Date field resulting in snapshot typecheck error[#233](https://github.com/mobxjs/mobx-state-tree/issues/233)\n-   Declaring `types.maybe(types.frozen)` will now result into an error [#224](https://github.com/mobxjs/mobx-state-tree/issues/224)\n-   Added support for Mobx observable arrays in type checks [#221](https://github.com/mobxjs/mobx-state-tree/issues/221) (from [alessioscalici](https://github.com/alessioscalici))\n\n# 0.9.0\n\n-   **BREAKING** Removed `applyPatches` and `applyActions`. Use `applyPatch` resp. `applyAction`, as both will now also accept an array as argument\n-   **BREAKING** `unprotect` and `protect` can only be applied at root nodes to avoid confusing scenarios Fixed [#180](https://github.com/mobxjs/mobx-state-tree/issues/180)\n-   Fixed [#141](https://github.com/mobxjs/mobx-state-tree/issues/141), actions / views are no longer wrapped in dynamically generated functions for a better debugging experience\n-   Small improvements to typings, fixed compilation issues with TypeScript 2.4.1.\n-   Fixed issues where `compose` couldn't overwrite getters. [#209](https://github.com/mobxjs/mobx-state-tree/issues/209), by @homura\n-   Fixed CDN links in readme\n-   Added TodoMVC to the examples section\n\n# 0.8.2\n\n-   Fixed issue in rollup module bundle\n\n# 0.8.1\n\n-   Fixed issue in release script, rendering 0.8.0 useless\n\n# 0.8.0\n\n-   **BREAKING** Dropped `types.extend` in favor of `types.compose`. See [#192](https://github.com/mobxjs/mobx-state-tree/issues/192)\n-   Introduced the lifecycle hooks `preProcessSnapshot` and `postProcessSnapshot`. See [#203](https://github.com/mobxjs/mobx-state-tree/pull/203) / [#100](https://github.com/mobxjs/mobx-state-tree/issues/100)\n-   Use rollup as bundler [#196](https://github.com/mobxjs/mobx-state-tree/pull/196)\n\n# 0.7.3\n\n-   Introduced the concept of volatile / local state in models. See [#168](https://github.com/mobxjs/mobx-state-tree/issues/168), or [docs](https://github.com/mobxjs/mobx-state-tree/tree/master#volatile-state)\n-   Fixed issue with types.map() with types.identifier(types.number) [#191](https://github.com/mobxjs/mobx-state-tree/issues/191) reported by @boatkorachal\n-   Fixed issue with reconciler that affected types.map when node already existed at that key reported by @boatkorachal [#191](https://github.com/mobxjs/mobx-state-tree/issues/191)\n\n# 0.7.2\n\n-   Fixed `cannot read property resolve of undefined` thanks to @cpunion for reporting, now value of dead nodes will be undefined. [#186](https://github.com/mobxjs/mobx-state-tree/issues/186)\n-   Fixed `[LateType] is not defined` thanks to @amir-arad for reporting, when using late as model property type [#187](https://github.com/mobxjs/mobx-state-tree/issues/187)\n-   Fixed `Object.freeze can only be called on Object` thanks to @ds300 for reporting, when using MST on a ReactNative environment [#189](https://github.com/mobxjs/mobx-state-tree/issues/189)\n-   Now the entire codebase is prettier! :D [#187](https://github.com/mobxjs/mobx-state-tree/issues/187)\n\n# 0.7.1\n\n-   Fixed `array.remove` not working\n\n# 0.7.0\n\nThe type system and internal administration has been refactoring, making the internals both simpler and more flexible.\nThings like references and identifiers are now first class types, making them much better composable. [#152](https://github.com/mobxjs/mobx-state-tree/issues/152)\n\n-   **BREAKING** References with a predefined lookup path are no longer supported. Instead of that, identifiers are now looked up in the entire tree. For that reasons identifiers now have to be unique in the entire tree, per type.\n-   **BREAKING** `resolve` is renamed to `resolvePath`\n-   Introduced `resolveIdentifier(type, tree, identifier)` to find objects by identifier\n-   **BREAKING** `types.reference` is by default non-nullable. For nullable identifiers, use `types.maybe(types.reference(X))`\n-   Many, many improvements. Related open issues will be updated.\n-   **BREAKING** `isMST` is renamed to `isStateTreeNode`\n\n# 0.6.3\n\n-   Fixed issue with array/maps of union types @abruzzihraig [#151](https://github.com/mobxjs/mobx-state-tree/issues/151)\n-   Make types.extend support computed attributes @cpunion [#169](https://github.com/mobxjs/mobx-state-tree/issues/169)\n-   Fixed issue with map of primitive types and applySnapshot @pioh [#155](https://github.com/mobxjs/mobx-state-tree/issues/155)\n-   Better type declarations for union, up to 10 supported types\n\n# 0.6.2\n\n-   Fixed issue where arrays where not properly serialized as action argument\n\n# 0.6.1\n\n-   Improved reporting of Type.is(), now it returns a fine grained report of why the provided value is not applicable.\n\n```\n[mobx-state-tree] Error while converting [{}] to AnonymousModel[]:\nat path \"/name\" snapshot undefined is not assignable to type: string.\nat path \"/quantity\" snapshot undefined is not assignable to type: number.\n```\n\n-   Fixed support for `types.reference` in combination with `types.late`, by @robinfehr\n\n# 0.6.0\n\n-   **BREAKING** `types.withDefault` has been renamed to `types.optional`\n-   **BREAKING** Array and map types can no longer be left out of snapshots by default. Use `optional` to make them optional in the snapshot\n-   **BREAKING** Literals no longer have a default value by default (use optional + literal instead)\n-   **BREAKING** Disabled inlining type.model definitions as introduced in 0.5.1; to many subtle issues\n-   Improved identifier support, they are no properly propagated through utility types like `maybe`, `union` etc\n-   Fixed issue where fields where not referred back to default when a partial snapshot was provided\n-   Fixed #122: `types.identifier` now also accepts a subtype to override the default string type; e.g. `types.identifier(types.number)`\n\n# 0.5.1\n\n-   Introduced support for lazy evaluating values in `withDefault`, useful to generate UUID's, timestamps or non-primitive default values\n-   ~~It is now possible to define something like~~ Removed in 0.6.0\n\n```javascript\nconst Box = types.model({\n    point: {\n        x: 10,\n        y: 10\n    }\n}\n```\n\nWhere the type of `point` property is inferred to `point: types.withDefault(types.model({ x: 10, y: 10}), () => ({ x: 10, y: 10 }))`\n\n# 0.5.0\n\n-   ** BREAKING ** protection is now enabled by default (#101)\n-   ** BREAKING ** it is no longer possible to read values from a dead object. Except through `getSnapshot` or `clone` (#102)\n-   ** BREAKING ** `types.recursive` has been removed in favor of `types.late`\n-   Introduced `unprotect`, to disable protection mode for a certain instance. Useful in `afterCreate` hooks\n-   Introduced `types.late`. Usage: `types.late(() => typeDefinition)`. Can be used for circular / recursive type definitions, even across files. See `test/circular(1|2).ts` for an example (#74)\n\n# 0.4.0\n\n**BREAKING** `types.model` no requires 2 parameters to define a model. The first parameter defines the properties, derived values and view functions. The second argument is used to define the actions. For example:\n\n```javascript\nconst Todo = types.model(\"Todo\", {\n    done: types.boolean,\n    toggle() {\n        this.done = !this.done\n    }\n})\n```\n\nNow should be defined as:\n\n```javascript\nconst Todo = types.model(\n    \"Todo\",\n    {\n        done: types.boolean\n    },\n    {\n        toggle() {\n            this.done = !this.done\n        }\n    }\n)\n```\n\nIt is still possible to define functions on the first object. However, those functions are not considered to be actions, but views. They are not allowed to modify values, but instead should produce a new value themselves.\n\n# 0.3.3\n\n-   Introduced lifecycle hooks `afterCreate`, `afterAttach`, `beforeDetach`, `beforeDestroy`, implements #76\n-   Introduced the convenience method `addDisposer(this, cb)` that can be used to easily destruct reactions etc. which are set up in `afterCreate`. See #76\n\n# 0.3.2\n\n-   Fix: actions where not bound automatically\n-   Improved and simplified the reconciliation mechanism, fixed many edge cases\n-   Improved the reference mechanism, fixed many edge cases\n-   Improved performance\n\n# 0.3.1\n\n-   (re) introduced the concept of environments, which can be passed as second argument to `.create`, and picked up using `getEnv`\n\n# 0.3.0\n\n-   Removed `primitive` type, use a more specific type instead\n-   Improved typescript typings of snapshots\n-   Added `depth` parameter to `getParent` and `hasParent`\n-   Separated the concepts of middleware and serializable actions. It is now possible to intercept, modify actions etc through `addMiddleWare`. `onAction` now uses middleware, if it is used, all parameters of actions should be serializable!\n\n# 0.2.2\n\n-   Introduced the concept of liveliness; if nodes are removed from the the tree because they are replaced by some other value, they will be marked as \"died\". This should help to early signal when people hold on to references that are not part of the tree anymore. To explicitly remove an node from a tree, with the intent to spawn a new state tree from it, use `detach`.\n-   Introduced the convenience method `destroy` to remove a model from it's parent and mark it as dead.\n-   Introduced the concept of protected trees. If a tree is protected using `protect`, it can only be modified through action, and not by mutating it directly anymore.\n\n# 0.2.1\n\n-   Introduced .Type and .SnapshotType to be used with TypeScript to get the type for a model\n\n# 0.2.0\n\n-   Renamed `createFactory` to `types.model` (breaking!)\n-   Renamed `composeFactory` to `types.extend` (breaking!)\n-   Actions should now be declared as `name(params) { body }`, instead of `name: action(function (params) { body})` (breaking!)\n-   Models are no longer constructed by invoking the factory as function, but by calling `factory.create` (breaking!)\n-   Introduced `identifier`\n-   Introduced / improved `reference`\n-   Greatly improved typescript support, type inference etc. However there are still limitations as the full typesystem of MST cannot be expressed in TypeScript. Especially concerning the type of snapshots and the possibility to use snapshots as first class value.\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3\"\n\nservices:\n  docusaurus:\n    build: .\n    ports:\n      - 3000:3000\n      - 35729:35729\n    volumes:\n      - ./docs:/app/docs\n      - ./website/blog:/app/website/blog\n      - ./website/core:/app/website/core\n      - ./website/i18n:/app/website/i18n\n      - ./website/pages:/app/website/pages\n      - ./website/static:/app/website/static\n      - ./website/sidebars.json:/app/website/sidebars.json\n      - ./website/siteConfig.js:/app/website/siteConfig.js\n    working_dir: /app/website\n"
  },
  {
    "path": "docs/.gitattributes",
    "content": "/API/**/* -diff -merge\n/API/**/* linguist-generated\n"
  },
  {
    "path": "docs/API/index.md",
    "content": "---\nid: \"index\"\ntitle: \"mobx-state-tree - v7.0.2\"\nsidebar_label: \"Globals\"\n---\n\n[mobx-state-tree - v7.0.2](index.md)\n\n## Index\n\n### Interfaces\n\n* [CustomTypeOptions](interfaces/customtypeoptions.md)\n* [FunctionWithFlag](interfaces/functionwithflag.md)\n* [IActionContext](interfaces/iactioncontext.md)\n* [IActionRecorder](interfaces/iactionrecorder.md)\n* [IActionTrackingMiddleware2Call](interfaces/iactiontrackingmiddleware2call.md)\n* [IActionTrackingMiddleware2Hooks](interfaces/iactiontrackingmiddleware2hooks.md)\n* [IActionTrackingMiddlewareHooks](interfaces/iactiontrackingmiddlewarehooks.md)\n* [IAnyComplexType](interfaces/ianycomplextype.md)\n* [IAnyModelType](interfaces/ianymodeltype.md)\n* [IAnyType](interfaces/ianytype.md)\n* [IHooks](interfaces/ihooks.md)\n* [IJsonPatch](interfaces/ijsonpatch.md)\n* [IMiddlewareEvent](interfaces/imiddlewareevent.md)\n* [IModelReflectionData](interfaces/imodelreflectiondata.md)\n* [IModelReflectionPropertiesData](interfaces/imodelreflectionpropertiesdata.md)\n* [IModelType](interfaces/imodeltype.md)\n* [IPatchRecorder](interfaces/ipatchrecorder.md)\n* [IReversibleJsonPatch](interfaces/ireversiblejsonpatch.md)\n* [ISerializedActionCall](interfaces/iserializedactioncall.md)\n* [ISimpleType](interfaces/isimpletype.md)\n* [ISnapshotProcessor](interfaces/isnapshotprocessor.md)\n* [ISnapshotProcessors](interfaces/isnapshotprocessors.md)\n* [IType](interfaces/itype.md)\n* [IValidationContextEntry](interfaces/ivalidationcontextentry.md)\n* [IValidationError](interfaces/ivalidationerror.md)\n* [ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)\n* [ReferenceOptionsOnInvalidated](interfaces/referenceoptionsoninvalidated.md)\n* [UnionOptions](interfaces/unionoptions.md)\n\n### Type aliases\n\n* [IDisposer](index.md#idisposer)\n* [IHooksGetter](index.md#ihooksgetter)\n* [IMiddlewareEventType](index.md#imiddlewareeventtype)\n* [IMiddlewareHandler](index.md#imiddlewarehandler)\n* [ITypeDispatcher](index.md#itypedispatcher)\n* [IUnionType](index.md#iuniontype)\n* [IValidationContext](index.md#ivalidationcontext)\n* [IValidationResult](index.md#ivalidationresult)\n* [Instance](index.md#instance)\n* [LivelinessMode](index.md#livelinessmode)\n* [OnReferenceInvalidated](index.md#onreferenceinvalidated)\n* [OnReferenceInvalidatedEvent](index.md#onreferenceinvalidatedevent)\n* [ReferenceIdentifier](index.md#referenceidentifier)\n* [ReferenceOptions](index.md#referenceoptions)\n* [SnapshotIn](index.md#snapshotin)\n* [SnapshotOrInstance](index.md#snapshotorinstance)\n* [SnapshotOut](index.md#snapshotout)\n\n### Variables\n\n* [DatePrimitive](index.md#const-dateprimitive)\n* [boolean](index.md#const-boolean)\n* [finite](index.md#const-finite)\n* [float](index.md#const-float)\n* [identifier](index.md#const-identifier)\n* [identifierNumber](index.md#const-identifiernumber)\n* [integer](index.md#const-integer)\n* [nullType](index.md#const-nulltype)\n* [number](index.md#const-number)\n* [string](index.md#const-string)\n* [undefinedType](index.md#const-undefinedtype)\n\n### Functions\n\n* [addDisposer](index.md#adddisposer)\n* [addMiddleware](index.md#addmiddleware)\n* [applyAction](index.md#applyaction)\n* [applyPatch](index.md#applypatch)\n* [applySnapshot](index.md#applysnapshot)\n* [array](index.md#array)\n* [cast](index.md#cast)\n* [castFlowReturn](index.md#castflowreturn)\n* [castToReferenceSnapshot](index.md#casttoreferencesnapshot)\n* [castToSnapshot](index.md#casttosnapshot)\n* [clone](index.md#clone)\n* [compose](index.md#compose)\n* [createActionTrackingMiddleware](index.md#createactiontrackingmiddleware)\n* [createActionTrackingMiddleware2](index.md#createactiontrackingmiddleware2)\n* [custom](index.md#custom)\n* [decorate](index.md#decorate)\n* [destroy](index.md#destroy)\n* [detach](index.md#detach)\n* [enumeration](index.md#enumeration)\n* [escapeJsonPath](index.md#escapejsonpath)\n* [flow](index.md#flow)\n* [frozen](index.md#frozen)\n* [getChildType](index.md#getchildtype)\n* [getEnv](index.md#getenv)\n* [getIdentifier](index.md#getidentifier)\n* [getLivelinessChecking](index.md#getlivelinesschecking)\n* [getMembers](index.md#getmembers)\n* [getNodeId](index.md#getnodeid)\n* [getParent](index.md#getparent)\n* [getParentOfType](index.md#getparentoftype)\n* [getPath](index.md#getpath)\n* [getPathParts](index.md#getpathparts)\n* [getPropertyMembers](index.md#getpropertymembers)\n* [getRelativePath](index.md#getrelativepath)\n* [getRoot](index.md#getroot)\n* [getRunningActionContext](index.md#getrunningactioncontext)\n* [getSnapshot](index.md#getsnapshot)\n* [getType](index.md#gettype)\n* [hasEnv](index.md#hasenv)\n* [hasParent](index.md#hasparent)\n* [hasParentOfType](index.md#hasparentoftype)\n* [isActionContextChildOf](index.md#isactioncontextchildof)\n* [isActionContextThisOrChildOf](index.md#isactioncontextthisorchildof)\n* [isAlive](index.md#isalive)\n* [isArrayType](index.md#isarraytype)\n* [isFrozenType](index.md#isfrozentype)\n* [isIdentifierType](index.md#isidentifiertype)\n* [isLateType](index.md#islatetype)\n* [isLiteralType](index.md#isliteraltype)\n* [isMapType](index.md#ismaptype)\n* [isModelType](index.md#ismodeltype)\n* [isOptionalType](index.md#isoptionaltype)\n* [isPrimitiveType](index.md#isprimitivetype)\n* [isProtected](index.md#isprotected)\n* [isReferenceType](index.md#isreferencetype)\n* [isRefinementType](index.md#isrefinementtype)\n* [isRoot](index.md#isroot)\n* [isStateTreeNode](index.md#isstatetreenode)\n* [isType](index.md#istype)\n* [isUnionType](index.md#isuniontype)\n* [isValidReference](index.md#isvalidreference)\n* [joinJsonPath](index.md#joinjsonpath)\n* [late](index.md#late)\n* [lazy](index.md#lazy)\n* [literal](index.md#literal)\n* [map](index.md#map)\n* [maybe](index.md#maybe)\n* [maybeNull](index.md#maybenull)\n* [model](index.md#model)\n* [onAction](index.md#onaction)\n* [onPatch](index.md#onpatch)\n* [onSnapshot](index.md#onsnapshot)\n* [optional](index.md#optional)\n* [protect](index.md#protect)\n* [recordActions](index.md#recordactions)\n* [recordPatches](index.md#recordpatches)\n* [reference](index.md#reference)\n* [refinement](index.md#refinement)\n* [resolveIdentifier](index.md#resolveidentifier)\n* [resolvePath](index.md#resolvepath)\n* [safeReference](index.md#safereference)\n* [setLivelinessChecking](index.md#setlivelinesschecking)\n* [snapshotProcessor](index.md#snapshotprocessor)\n* [splitJsonPath](index.md#splitjsonpath)\n* [toGenerator](index.md#togenerator)\n* [toGeneratorFunction](index.md#togeneratorfunction)\n* [tryReference](index.md#tryreference)\n* [tryResolve](index.md#tryresolve)\n* [typecheck](index.md#typecheck)\n* [unescapeJsonPath](index.md#unescapejsonpath)\n* [union](index.md#union)\n* [unprotect](index.md#unprotect)\n* [walk](index.md#walk)\n\n### Object literals\n\n* [types](index.md#const-types)\n\n## Type aliases\n\n###  IDisposer\n\nƬ **IDisposer**: *function*\n\n*Defined in [src/utils.ts:35](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/utils.ts#L35)*\n\nA generic disposer.\n\n#### Type declaration:\n\n▸ (): *void*\n\n___\n\n###  IHooksGetter\n\nƬ **IHooksGetter**: *function*\n\n*Defined in [src/core/node/Hook.ts:19](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/Hook.ts#L19)*\n\n#### Type declaration:\n\n▸ (`self`: T): *[IHooks](interfaces/ihooks.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | T |\n\n___\n\n###  IMiddlewareEventType\n\nƬ **IMiddlewareEventType**: *\"action\" | \"flow_spawn\" | \"flow_resume\" | \"flow_resume_error\" | \"flow_return\" | \"flow_throw\"*\n\n*Defined in [src/core/action.ts:16](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L16)*\n\n___\n\n###  IMiddlewareHandler\n\nƬ **IMiddlewareHandler**: *function*\n\n*Defined in [src/core/action.ts:54](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L54)*\n\n#### Type declaration:\n\n▸ (`actionCall`: [IMiddlewareEvent](interfaces/imiddlewareevent.md), `next`: function, `abort`: function): *any*\n\n**Parameters:**\n\n▪ **actionCall**: *[IMiddlewareEvent](interfaces/imiddlewareevent.md)*\n\n▪ **next**: *function*\n\n▸ (`actionCall`: [IMiddlewareEvent](interfaces/imiddlewareevent.md), `callback?`: undefined | function): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`actionCall` | [IMiddlewareEvent](interfaces/imiddlewareevent.md) |\n`callback?` | undefined &#124; function |\n\n▪ **abort**: *function*\n\n▸ (`value`: any): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`value` | any |\n\n___\n\n###  ITypeDispatcher\n\nƬ **ITypeDispatcher**: *function*\n\n*Defined in [src/types/utility-types/union.ts:22](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L22)*\n\n#### Type declaration:\n\n▸ (`snapshot`: Types[number][\"SnapshotType\"]): *Types[number]*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | Types[number][\"SnapshotType\"] |\n\n___\n\n###  IUnionType\n\nƬ **IUnionType**: *ITypeUnion‹Types[number][\"CreationType\"], Types[number][\"SnapshotType\"], Types[number][\"TypeWithoutSTN\"]›*\n\n*Defined in [src/types/utility-types/union.ts:169](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L169)*\n\n___\n\n###  IValidationContext\n\nƬ **IValidationContext**: *[IValidationContextEntry](interfaces/ivalidationcontextentry.md)[]*\n\n*Defined in [src/core/type/type-checker.ts:23](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L23)*\n\nArray of validation context entries\n\n___\n\n###  IValidationResult\n\nƬ **IValidationResult**: *[IValidationError](interfaces/ivalidationerror.md)[]*\n\n*Defined in [src/core/type/type-checker.ts:36](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L36)*\n\nType validation result, which is an array of type validation errors\n\n___\n\n###  Instance\n\nƬ **Instance**: *T extends object ? T[\"Type\"] : T*\n\n*Defined in [src/core/type/type.ts:233](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L233)*\n\nThe instance representation of a given type.\n\n___\n\n###  LivelinessMode\n\nƬ **LivelinessMode**: *\"warn\" | \"error\" | \"ignore\"*\n\n*Defined in [src/core/node/livelinessChecking.ts:7](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/livelinessChecking.ts#L7)*\n\nDefines what MST should do when running into reads / writes to objects that have died.\n- `\"warn\"`: Print a warning (default).\n- `\"error\"`: Throw an exception.\n- \"`ignore`\": Do nothing.\n\n___\n\n###  OnReferenceInvalidated\n\nƬ **OnReferenceInvalidated**: *function*\n\n*Defined in [src/types/utility-types/reference.ts:45](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L45)*\n\n#### Type declaration:\n\n▸ (`event`: [OnReferenceInvalidatedEvent](index.md#onreferenceinvalidatedevent)‹STN›): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`event` | [OnReferenceInvalidatedEvent](index.md#onreferenceinvalidatedevent)‹STN› |\n\n___\n\n###  OnReferenceInvalidatedEvent\n\nƬ **OnReferenceInvalidatedEvent**: *object*\n\n*Defined in [src/types/utility-types/reference.ts:36](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L36)*\n\n#### Type declaration:\n\n___\n\n###  ReferenceIdentifier\n\nƬ **ReferenceIdentifier**: *string | number*\n\n*Defined in [src/types/utility-types/identifier.ts:147](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/identifier.ts#L147)*\n\nValid types for identifiers.\n\n___\n\n###  ReferenceOptions\n\nƬ **ReferenceOptions**: *[ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)‹IT› | [ReferenceOptionsOnInvalidated](interfaces/referenceoptionsoninvalidated.md)‹IT› | [ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)‹IT› & [ReferenceOptionsOnInvalidated](interfaces/referenceoptionsoninvalidated.md)‹IT›*\n\n*Defined in [src/types/utility-types/reference.ts:481](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L481)*\n\n___\n\n###  SnapshotIn\n\nƬ **SnapshotIn**: *T extends object ? T[\"CreationType\"] : T extends IStateTreeNode<infer IT> ? IT[\"CreationType\"] : T*\n\n*Defined in [src/core/type/type.ts:238](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L238)*\n\nThe input (creation) snapshot representation of a given type.\n\n___\n\n###  SnapshotOrInstance\n\nƬ **SnapshotOrInstance**: *[SnapshotIn](index.md#snapshotin)‹T› | [Instance](index.md#instance)‹T›*\n\n*Defined in [src/core/type/type.ts:279](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L279)*\n\nA type which is equivalent to the union of SnapshotIn and Instance types of a given typeof TYPE or typeof VARIABLE.\nFor primitives it defaults to the primitive itself.\n\nFor example:\n- `SnapshotOrInstance<typeof ModelA> = SnapshotIn<typeof ModelA> | Instance<typeof ModelA>`\n- `SnapshotOrInstance<typeof self.a (where self.a is a ModelA)> = SnapshotIn<typeof ModelA> | Instance<typeof ModelA>`\n\nUsually you might want to use this when your model has a setter action that sets a property.\n\nExample:\n```ts\nconst ModelA = types.model({\n  n: types.number\n})\n\nconst ModelB = types.model({\n  innerModel: ModelA\n}).actions(self => ({\n  // this will accept as property both the snapshot and the instance, whichever is preferred\n  setInnerModel(m: SnapshotOrInstance<typeof self.innerModel>) {\n    self.innerModel = cast(m)\n  }\n}))\n```\n\n___\n\n###  SnapshotOut\n\nƬ **SnapshotOut**: *T extends object ? T[\"SnapshotType\"] : T extends IStateTreeNode<infer IT> ? IT[\"SnapshotType\"] : T*\n\n*Defined in [src/core/type/type.ts:247](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L247)*\n\nThe output snapshot representation of a given type.\n\n## Variables\n\n### `Const` DatePrimitive\n\n• **DatePrimitive**: *[IType](interfaces/itype.md)‹number | Date, number, Date›* =  _DatePrimitive\n\n*Defined in [src/types/primitives.ts:215](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L215)*\n\n`types.Date` - Creates a type that can only contain a javascript Date value.\n\nExample:\n```ts\nconst LogLine = types.model({\n  timestamp: types.Date,\n})\n\nLogLine.create({ timestamp: new Date() })\n```\n\n___\n\n### `Const` bigint\n\n• **bigint**: *[IType](interfaces/itype.md)‹bigint | string | number, string, bigint›* =  _BigIntPrimitive\n\n*Defined in [src/types/primitives.ts:192](https://github.com/mobxjs/mobx-state-tree/blob/8c6f719b/src/types/primitives.ts#L192)*\n\n`types.bigint` - Creates a type that can only contain a bigint value.\nSnapshots serialize to string (JSON-safe) and deserialize from string, number or bigint.\n\nExample:\n```ts\nconst BigId = types.model({\n  id: types.identifier,\n  value: types.bigint\n})\ngetSnapshot(store).value // \"0\" (string, JSON-safe)\n```\n___\n\n### `Const` boolean\n\n• **boolean**: *[ISimpleType](interfaces/isimpletype.md)‹boolean›* =  new CoreType<boolean, boolean, boolean>(\n    \"boolean\",\n    TypeFlags.Boolean,\n    v => typeof v === \"boolean\"\n)\n\n*Defined in [src/types/primitives.ts:169](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L169)*\n\n`types.boolean` - Creates a type that can only contain a boolean value.\nThis type is used for boolean values by default\n\nExample:\n```ts\nconst Thing = types.model({\n  isCool: types.boolean,\n  isAwesome: false\n})\n```\n\n___\n\n### `Const` finite\n\n• **finite**: *[ISimpleType](interfaces/isimpletype.md)‹number›* =  new CoreType<number, number, number>(\n    \"finite\",\n    TypeFlags.Finite,\n    v => isFinite(v)\n)\n\n*Defined in [src/types/primitives.ts:150](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L150)*\n\n`types.finite` - Creates a type that can only contain an finite value.\n\nExample:\n```ts\nconst Size = types.model({\n  width: types.finite,\n  height: 10\n})\n```\n\n___\n\n### `Const` float\n\n• **float**: *[ISimpleType](interfaces/isimpletype.md)‹number›* =  new CoreType<number, number, number>(\n    \"float\",\n    TypeFlags.Float,\n    v => isFloat(v)\n)\n\n*Defined in [src/types/primitives.ts:132](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L132)*\n\n`types.float` - Creates a type that can only contain an float value.\n\nExample:\n```ts\nconst Size = types.model({\n  width: types.float,\n  height: 10\n})\n```\n\n___\n\n### `Const` identifier\n\n• **identifier**: *[ISimpleType](interfaces/isimpletype.md)‹string›* =  new IdentifierType()\n\n*Defined in [src/types/utility-types/identifier.ts:115](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/identifier.ts#L115)*\n\n`types.identifier` - Identifiers are used to make references, lifecycle events and reconciling works.\nInside a state tree, for each type can exist only one instance for each given identifier.\nFor example there couldn't be 2 instances of user with id 1. If you need more, consider using references.\nIdentifier can be used only as type property of a model.\nThis type accepts as parameter the value type of the identifier field that can be either string or number.\n\nExample:\n```ts\n const Todo = types.model(\"Todo\", {\n     id: types.identifier,\n     title: types.string\n })\n```\n\n**`returns`** \n\n___\n\n### `Const` identifierNumber\n\n• **identifierNumber**: *[ISimpleType](interfaces/isimpletype.md)‹number›* =  new IdentifierNumberType()\n\n*Defined in [src/types/utility-types/identifier.ts:130](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/identifier.ts#L130)*\n\n`types.identifierNumber` - Similar to `types.identifier`. This one will serialize from / to a number when applying snapshots\n\nExample:\n```ts\n const Todo = types.model(\"Todo\", {\n     id: types.identifierNumber,\n     title: types.string\n })\n```\n\n**`returns`** \n\n___\n\n### `Const` integer\n\n• **integer**: *[ISimpleType](interfaces/isimpletype.md)‹number›* =  new CoreType<number, number, number>(\n    \"integer\",\n    TypeFlags.Integer,\n    v => isInteger(v)\n)\n\n*Defined in [src/types/primitives.ts:114](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L114)*\n\n`types.integer` - Creates a type that can only contain an integer value.\n\nExample:\n```ts\nconst Size = types.model({\n  width: types.integer,\n  height: 10\n})\n```\n\n___\n\n### `Const` nullType\n\n• **nullType**: *[ISimpleType](interfaces/isimpletype.md)‹null›* =  new CoreType<null, null, null>(\n    \"null\",\n    TypeFlags.Null,\n    v => v === null\n)\n\n*Defined in [src/types/primitives.ts:178](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L178)*\n\n`types.null` - The type of the value `null`\n\n___\n\n### `Const` number\n\n• **number**: *[ISimpleType](interfaces/isimpletype.md)‹number›* =  new CoreType<number, number, number>(\n    \"number\",\n    TypeFlags.Number,\n    v => typeof v === \"number\"\n)\n\n*Defined in [src/types/primitives.ts:96](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L96)*\n\n`types.number` - Creates a type that can only contain a numeric value.\nThis type is used for numeric values by default\n\nExample:\n```ts\nconst Vector = types.model({\n  x: types.number,\n  y: 1.5\n})\n```\n\n___\n\n### `Const` string\n\n• **string**: *[ISimpleType](interfaces/isimpletype.md)‹string›* =  new CoreType<string, string, string>(\n    \"string\",\n    TypeFlags.String,\n    v => typeof v === \"string\"\n)\n\n*Defined in [src/types/primitives.ts:77](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L77)*\n\n`types.string` - Creates a type that can only contain a string value.\nThis type is used for string values by default\n\nExample:\n```ts\nconst Person = types.model({\n  firstName: types.string,\n  lastName: \"Doe\"\n})\n```\n\n___\n\n### `Const` undefinedType\n\n• **undefinedType**: *[ISimpleType](interfaces/isimpletype.md)‹undefined›* =  new CoreType<undefined, undefined, undefined>(\n    \"undefined\",\n    TypeFlags.Undefined,\n    v => v === undefined\n)\n\n*Defined in [src/types/primitives.ts:187](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L187)*\n\n`types.undefined` - The type of the value `undefined`\n\n## Functions\n\n###  addDisposer\n\n▸ **addDisposer**(`target`: IAnyStateTreeNode, `disposer`: [IDisposer](index.md#idisposer)): *[IDisposer](index.md#idisposer)*\n\n*Defined in [src/core/mst-operations.ts:751](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L751)*\n\nUse this utility to register a function that should be called whenever the\ntargeted state tree node is destroyed. This is a useful alternative to managing\ncleanup methods yourself using the `beforeDestroy` hook.\n\nThis methods returns the same disposer that was passed as argument.\n\nExample:\n```ts\nconst Todo = types.model({\n  title: types.string\n}).actions(self => ({\n  afterCreate() {\n    const autoSaveDisposer = reaction(\n      () => getSnapshot(self),\n      snapshot => sendSnapshotToServerSomehow(snapshot)\n    )\n    // stop sending updates to server if this\n    // instance is destroyed\n    addDisposer(self, autoSaveDisposer)\n  }\n}))\n```\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n`disposer` | [IDisposer](index.md#idisposer) |\n\n**Returns:** *[IDisposer](index.md#idisposer)*\n\nThe same disposer that was passed as argument\n\n___\n\n###  addMiddleware\n\n▸ **addMiddleware**(`target`: IAnyStateTreeNode, `handler`: [IMiddlewareHandler](index.md#imiddlewarehandler), `includeHooks`: boolean): *[IDisposer](index.md#idisposer)*\n\n*Defined in [src/core/action.ts:174](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L174)*\n\nMiddleware can be used to intercept any action is invoked on the subtree where it is attached.\nIf a tree is protected (by default), this means that any mutation of the tree will pass through your middleware.\n\nFor more details, see the [middleware docs](concepts/middleware.md)\n\n**Parameters:**\n\nName | Type | Default | Description |\n------ | ------ | ------ | ------ |\n`target` | IAnyStateTreeNode | - | Node to apply the middleware to. |\n`handler` | [IMiddlewareHandler](index.md#imiddlewarehandler) | - | - |\n`includeHooks` | boolean | true | - |\n\n**Returns:** *[IDisposer](index.md#idisposer)*\n\nA callable function to dispose the middleware.\n\n___\n\n###  applyAction\n\n▸ **applyAction**(`target`: IAnyStateTreeNode, `actions`: [ISerializedActionCall](interfaces/iserializedactioncall.md) | [ISerializedActionCall](interfaces/iserializedactioncall.md)[]): *void*\n\n*Defined in [src/middlewares/on-action.ts:89](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L89)*\n\nApplies an action or a series of actions in a single MobX transaction.\nDoes not return any value\nTakes an action description as produced by the `onAction` middleware.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`target` | IAnyStateTreeNode | - |\n`actions` | [ISerializedActionCall](interfaces/iserializedactioncall.md) &#124; [ISerializedActionCall](interfaces/iserializedactioncall.md)[] |   |\n\n**Returns:** *void*\n\n___\n\n###  applyPatch\n\n▸ **applyPatch**(`target`: IAnyStateTreeNode, `patch`: [IJsonPatch](interfaces/ijsonpatch.md) | ReadonlyArray‹[IJsonPatch](interfaces/ijsonpatch.md)›): *void*\n\n*Defined in [src/core/mst-operations.ts:124](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L124)*\n\nApplies a JSON-patch to the given model instance or bails out if the patch couldn't be applied\nSee [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details.\n\nCan apply a single past, or an array of patches.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n`patch` | [IJsonPatch](interfaces/ijsonpatch.md) &#124; ReadonlyArray‹[IJsonPatch](interfaces/ijsonpatch.md)› |\n\n**Returns:** *void*\n\n___\n\n###  applySnapshot\n\n▸ **applySnapshot**<**C**>(`target`: IStateTreeNode‹[IType](interfaces/itype.md)‹C, any, any››, `snapshot`: C): *void*\n\n*Defined in [src/core/mst-operations.ts:321](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L321)*\n\nApplies a snapshot to a given model instances. Patch and snapshot listeners will be invoked as usual.\n\n**Type parameters:**\n\n▪ **C**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IStateTreeNode‹[IType](interfaces/itype.md)‹C, any, any›› |\n`snapshot` | C |\n\n**Returns:** *void*\n\n___\n\n###  array\n\n▸ **array**<**IT**>(`subtype`: IT): *IArrayType‹IT›*\n\n*Defined in [src/types/complex-types/array.ts:348](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/array.ts#L348)*\n\n`types.array` - Creates an index based collection type who's children are all of a uniform declared type.\n\nThis type will always produce [observable arrays](https://mobx.js.org/api.html#observablearray)\n\nExample:\n```ts\nconst Todo = types.model({\n  task: types.string\n})\n\nconst TodoStore = types.model({\n  todos: types.array(Todo)\n})\n\nconst s = TodoStore.create({ todos: [] })\nunprotect(s) // needed to allow modifying outside of an action\ns.todos.push({ task: \"Grab coffee\" })\nconsole.log(s.todos[0]) // prints: \"Grab coffee\"\n```\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subtype` | IT |\n\n**Returns:** *IArrayType‹IT›*\n\n___\n\n###  cast\n\n▸ **cast**<**O**>(`snapshotOrInstance`: O): *O*\n\n*Defined in [src/core/mst-operations.ts:908](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L908)*\n\nCasts a node snapshot or instance type to an instance type so it can be assigned to a type instance.\nNote that this is just a cast for the type system, this is, it won't actually convert a snapshot to an instance,\nbut just fool typescript into thinking so.\nEither way, casting when outside an assignation operation won't compile.\n\nExample:\n```ts\nconst ModelA = types.model({\n  n: types.number\n}).actions(self => ({\n  setN(aNumber: number) {\n    self.n = aNumber\n  }\n}))\n\nconst ModelB = types.model({\n  innerModel: ModelA\n}).actions(self => ({\n  someAction() {\n    // this will allow the compiler to assign a snapshot to the property\n    self.innerModel = cast({ a: 5 })\n  }\n}))\n```\n\n**Type parameters:**\n\n▪ **O**: *string | number | boolean | null | undefined*\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`snapshotOrInstance` | O | Snapshot or instance |\n\n**Returns:** *O*\n\nThe same object cast as an instance\n\n▸ **cast**<**O**>(`snapshotOrInstance`: TypeOfValue<O>[\"CreationType\"] | TypeOfValue<O>[\"SnapshotType\"] | TypeOfValue<O>[\"Type\"]): *O*\n\n*Defined in [src/core/mst-operations.ts:911](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L911)*\n\nCasts a node snapshot or instance type to an instance type so it can be assigned to a type instance.\nNote that this is just a cast for the type system, this is, it won't actually convert a snapshot to an instance,\nbut just fool typescript into thinking so.\nEither way, casting when outside an assignation operation won't compile.\n\nExample:\n```ts\nconst ModelA = types.model({\n  n: types.number\n}).actions(self => ({\n  setN(aNumber: number) {\n    self.n = aNumber\n  }\n}))\n\nconst ModelB = types.model({\n  innerModel: ModelA\n}).actions(self => ({\n  someAction() {\n    // this will allow the compiler to assign a snapshot to the property\n    self.innerModel = cast({ a: 5 })\n  }\n}))\n```\n\n**Type parameters:**\n\n▪ **O**\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`snapshotOrInstance` | TypeOfValue<O>[\"CreationType\"] &#124; TypeOfValue<O>[\"SnapshotType\"] &#124; TypeOfValue<O>[\"Type\"] | Snapshot or instance |\n\n**Returns:** *O*\n\nThe same object cast as an instance\n\n___\n\n###  castFlowReturn\n\n▸ **castFlowReturn**<**T**>(`val`: T): *T*\n\n*Defined in [src/core/flow.ts:34](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/flow.ts#L34)*\n\n**`deprecated`** Not needed since TS3.6.\nUsed for TypeScript to make flows that return a promise return the actual promise result.\n\n**Type parameters:**\n\n▪ **T**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`val` | T |\n\n**Returns:** *T*\n\n___\n\n###  castToReferenceSnapshot\n\n▸ **castToReferenceSnapshot**<**I**>(`instance`: I): *Extract<I, IAnyStateTreeNode> extends never ? I : ReferenceIdentifier*\n\n*Defined in [src/core/mst-operations.ts:1011](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L1011)*\n\nCasts a node instance type to a reference snapshot type so it can be assigned to a reference snapshot (e.g. to be used inside a create call).\nNote that this is just a cast for the type system, this is, it won't actually convert an instance to a reference snapshot,\nbut just fool typescript into thinking so.\n\nExample:\n```ts\nconst ModelA = types.model({\n  id: types.identifier,\n  n: types.number\n}).actions(self => ({\n  setN(aNumber: number) {\n    self.n = aNumber\n  }\n}))\n\nconst ModelB = types.model({\n  refA: types.reference(ModelA)\n})\n\nconst a = ModelA.create({ id: 'someId', n: 5 });\n// this will allow the compiler to use a model as if it were a reference snapshot\nconst b = ModelB.create({ refA: castToReferenceSnapshot(a)})\n```\n\n**Type parameters:**\n\n▪ **I**\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`instance` | I | Instance |\n\n**Returns:** *Extract<I, IAnyStateTreeNode> extends never ? I : ReferenceIdentifier*\n\nThe same object cast as a reference snapshot (string or number)\n\n___\n\n###  castToSnapshot\n\n▸ **castToSnapshot**<**I**>(`snapshotOrInstance`: I): *Extract<I, IAnyStateTreeNode> extends never ? I : TypeOfValue<I>[\"CreationType\"]*\n\n*Defined in [src/core/mst-operations.ts:977](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L977)*\n\nCasts a node instance type to a snapshot type so it can be assigned to a type snapshot (e.g. to be used inside a create call).\nNote that this is just a cast for the type system, this is, it won't actually convert an instance to a snapshot,\nbut just fool typescript into thinking so.\n\nExample:\n```ts\nconst ModelA = types.model({\n  n: types.number\n}).actions(self => ({\n  setN(aNumber: number) {\n    self.n = aNumber\n  }\n}))\n\nconst ModelB = types.model({\n  innerModel: ModelA\n})\n\nconst a = ModelA.create({ n: 5 });\n// this will allow the compiler to use a model as if it were a snapshot\nconst b = ModelB.create({ innerModel: castToSnapshot(a)})\n```\n\n**Type parameters:**\n\n▪ **I**\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`snapshotOrInstance` | I | Snapshot or instance |\n\n**Returns:** *Extract<I, IAnyStateTreeNode> extends never ? I : TypeOfValue<I>[\"CreationType\"]*\n\nThe same object cast as an input (creation) snapshot\n\n___\n\n###  clone\n\n▸ **clone**<**T**>(`source`: T, `keepEnvironment`: boolean | any): *T*\n\n*Defined in [src/core/mst-operations.ts:666](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L666)*\n\nReturns a deep copy of the given state tree node as new tree.\nShorthand for `snapshot(x) = getType(x).create(getSnapshot(x))`\n\n_Tip: clone will create a literal copy, including the same identifiers. To modify identifiers etc. during cloning, don't use clone but take a snapshot of the tree, modify it, and create new instance_\n\n**Type parameters:**\n\n▪ **T**: *IAnyStateTreeNode*\n\n**Parameters:**\n\nName | Type | Default | Description |\n------ | ------ | ------ | ------ |\n`source` | T | - | - |\n`keepEnvironment` | boolean &#124; any | true | indicates whether the clone should inherit the same environment (`true`, the default), or not have an environment (`false`). If an object is passed in as second argument, that will act as the environment for the cloned tree. |\n\n**Returns:** *T*\n\n___\n\n###  compose\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›): *[IModelType](interfaces/imodeltype.md)‹PA & PB, OA & OB, _CustomJoin‹FCA, FCB›, _CustomJoin‹FSA, FSB››*\n\n*Defined in [src/types/complex-types/model.ts:812](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L812)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB, OA & OB, _CustomJoin‹FCA, FCB›, _CustomJoin‹FSA, FSB››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›): *[IModelType](interfaces/imodeltype.md)‹PA & PB, OA & OB, _CustomJoin‹FCA, FCB›, _CustomJoin‹FSA, FSB››*\n\n*Defined in [src/types/complex-types/model.ts:814](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L814)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB, OA & OB, _CustomJoin‹FCA, FCB›, _CustomJoin‹FSA, FSB››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC, OA & OB & OC, _CustomJoin‹FCA, _CustomJoin‹FCB, FCC››, _CustomJoin‹FSA, _CustomJoin‹FSB, FSC›››*\n\n*Defined in [src/types/complex-types/model.ts:816](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L816)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC, OA & OB & OC, _CustomJoin‹FCA, _CustomJoin‹FCB, FCC››, _CustomJoin‹FSA, _CustomJoin‹FSB, FSC›››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC, OA & OB & OC, _CustomJoin‹FCA, _CustomJoin‹FCB, FCC››, _CustomJoin‹FSA, _CustomJoin‹FSB, FSC›››*\n\n*Defined in [src/types/complex-types/model.ts:818](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L818)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC, OA & OB & OC, _CustomJoin‹FCA, _CustomJoin‹FCB, FCC››, _CustomJoin‹FSA, _CustomJoin‹FSB, FSC›››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD, OA & OB & OC & OD, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, FCD›››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, FSD››››*\n\n*Defined in [src/types/complex-types/model.ts:820](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L820)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD, OA & OB & OC & OD, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, FCD›››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, FSD››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD, OA & OB & OC & OD, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, FCD›››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, FSD››››*\n\n*Defined in [src/types/complex-types/model.ts:822](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L822)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD, OA & OB & OC & OD, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, FCD›››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, FSD››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE, OA & OB & OC & OD & OE, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, FCE››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, FSE›››››*\n\n*Defined in [src/types/complex-types/model.ts:824](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L824)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE, OA & OB & OC & OD & OE, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, FCE››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, FSE›››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE, OA & OB & OC & OD & OE, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, FCE››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, FSE›››››*\n\n*Defined in [src/types/complex-types/model.ts:826](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L826)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE, OA & OB & OC & OD & OE, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, FCE››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, FSE›››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF, OA & OB & OC & OD & OE & OF, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, FCF›››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, FSF››››››*\n\n*Defined in [src/types/complex-types/model.ts:830](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L830)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF, OA & OB & OC & OD & OE & OF, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, FCF›››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, FSF››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF, OA & OB & OC & OD & OE & OF, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, FCF›››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, FSF››››››*\n\n*Defined in [src/types/complex-types/model.ts:833](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L833)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF, OA & OB & OC & OD & OE & OF, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, FCF›››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, FSF››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**, **PG**, **OG**, **FCG**, **FSG**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›, `G`: [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG, OA & OB & OC & OD & OE & OF & OG, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, FCG››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, FSG›››››››*\n\n*Defined in [src/types/complex-types/model.ts:836](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L836)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n▪ **PG**: *ModelProperties*\n\n▪ **OG**\n\n▪ **FCG**\n\n▪ **FSG**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n`G` | [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG, OA & OB & OC & OD & OE & OF & OG, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, FCG››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, FSG›››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**, **PG**, **OG**, **FCG**, **FSG**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›, `G`: [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG, OA & OB & OC & OD & OE & OF & OG, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, FCG››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, FSG›››››››*\n\n*Defined in [src/types/complex-types/model.ts:839](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L839)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n▪ **PG**: *ModelProperties*\n\n▪ **OG**\n\n▪ **FCG**\n\n▪ **FSG**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n`G` | [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG, OA & OB & OC & OD & OE & OF & OG, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, FCG››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, FSG›››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**, **PG**, **OG**, **FCG**, **FSG**, **PH**, **OH**, **FCH**, **FSH**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›, `G`: [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG›, `H`: [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH, OA & OB & OC & OD & OE & OF & OG & OH, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, FCH›››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, FSH››››››››*\n\n*Defined in [src/types/complex-types/model.ts:842](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L842)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n▪ **PG**: *ModelProperties*\n\n▪ **OG**\n\n▪ **FCG**\n\n▪ **FSG**\n\n▪ **PH**: *ModelProperties*\n\n▪ **OH**\n\n▪ **FCH**\n\n▪ **FSH**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n`G` | [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG› |\n`H` | [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH, OA & OB & OC & OD & OE & OF & OG & OH, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, FCH›››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, FSH››››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**, **PG**, **OG**, **FCG**, **FSG**, **PH**, **OH**, **FCH**, **FSH**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›, `G`: [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG›, `H`: [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH, OA & OB & OC & OD & OE & OF & OG & OH, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, FCH›››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, FSH››››››››*\n\n*Defined in [src/types/complex-types/model.ts:845](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L845)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n▪ **PG**: *ModelProperties*\n\n▪ **OG**\n\n▪ **FCG**\n\n▪ **FSG**\n\n▪ **PH**: *ModelProperties*\n\n▪ **OH**\n\n▪ **FCH**\n\n▪ **FSH**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n`G` | [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG› |\n`H` | [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH, OA & OB & OC & OD & OE & OF & OG & OH, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, FCH›››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, FSH››››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**, **PG**, **OG**, **FCG**, **FSG**, **PH**, **OH**, **FCH**, **FSH**, **PI**, **OI**, **FCI**, **FSI**>(`name`: string, `A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›, `G`: [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG›, `H`: [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH›, `I`: [IModelType](interfaces/imodeltype.md)‹PI, OI, FCI, FSI›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH & PI, OA & OB & OC & OD & OE & OF & OG & OH & OI, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, _CustomJoin‹FCH, FCI››››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, _CustomJoin‹FSH, FSI›››››››››*\n\n*Defined in [src/types/complex-types/model.ts:848](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L848)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n▪ **PG**: *ModelProperties*\n\n▪ **OG**\n\n▪ **FCG**\n\n▪ **FSG**\n\n▪ **PH**: *ModelProperties*\n\n▪ **OH**\n\n▪ **FCH**\n\n▪ **FSH**\n\n▪ **PI**: *ModelProperties*\n\n▪ **OI**\n\n▪ **FCI**\n\n▪ **FSI**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n`G` | [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG› |\n`H` | [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH› |\n`I` | [IModelType](interfaces/imodeltype.md)‹PI, OI, FCI, FSI› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH & PI, OA & OB & OC & OD & OE & OF & OG & OH & OI, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, _CustomJoin‹FCH, FCI››››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, _CustomJoin‹FSH, FSI›››››››››*\n\n▸ **compose**<**PA**, **OA**, **FCA**, **FSA**, **PB**, **OB**, **FCB**, **FSB**, **PC**, **OC**, **FCC**, **FSC**, **PD**, **OD**, **FCD**, **FSD**, **PE**, **OE**, **FCE**, **FSE**, **PF**, **OF**, **FCF**, **FSF**, **PG**, **OG**, **FCG**, **FSG**, **PH**, **OH**, **FCH**, **FSH**, **PI**, **OI**, **FCI**, **FSI**>(`A`: [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA›, `B`: [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB›, `C`: [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC›, `D`: [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD›, `E`: [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE›, `F`: [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF›, `G`: [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG›, `H`: [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH›, `I`: [IModelType](interfaces/imodeltype.md)‹PI, OI, FCI, FSI›): *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH & PI, OA & OB & OC & OD & OE & OF & OG & OH & OI, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, _CustomJoin‹FCH, FCI››››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, _CustomJoin‹FSH, FSI›››››››››*\n\n*Defined in [src/types/complex-types/model.ts:851](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L851)*\n\n`types.compose` - Composes a new model from one or more existing model types.\nThis method can be invoked in two forms:\nGiven 2 or more model types, the types are composed into a new Type.\nGiven first parameter as a string and 2 or more model types,\nthe types are composed into a new Type with the given name\n\n**Type parameters:**\n\n▪ **PA**: *ModelProperties*\n\n▪ **OA**\n\n▪ **FCA**\n\n▪ **FSA**\n\n▪ **PB**: *ModelProperties*\n\n▪ **OB**\n\n▪ **FCB**\n\n▪ **FSB**\n\n▪ **PC**: *ModelProperties*\n\n▪ **OC**\n\n▪ **FCC**\n\n▪ **FSC**\n\n▪ **PD**: *ModelProperties*\n\n▪ **OD**\n\n▪ **FCD**\n\n▪ **FSD**\n\n▪ **PE**: *ModelProperties*\n\n▪ **OE**\n\n▪ **FCE**\n\n▪ **FSE**\n\n▪ **PF**: *ModelProperties*\n\n▪ **OF**\n\n▪ **FCF**\n\n▪ **FSF**\n\n▪ **PG**: *ModelProperties*\n\n▪ **OG**\n\n▪ **FCG**\n\n▪ **FSG**\n\n▪ **PH**: *ModelProperties*\n\n▪ **OH**\n\n▪ **FCH**\n\n▪ **FSH**\n\n▪ **PI**: *ModelProperties*\n\n▪ **OI**\n\n▪ **FCI**\n\n▪ **FSI**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`A` | [IModelType](interfaces/imodeltype.md)‹PA, OA, FCA, FSA› |\n`B` | [IModelType](interfaces/imodeltype.md)‹PB, OB, FCB, FSB› |\n`C` | [IModelType](interfaces/imodeltype.md)‹PC, OC, FCC, FSC› |\n`D` | [IModelType](interfaces/imodeltype.md)‹PD, OD, FCD, FSD› |\n`E` | [IModelType](interfaces/imodeltype.md)‹PE, OE, FCE, FSE› |\n`F` | [IModelType](interfaces/imodeltype.md)‹PF, OF, FCF, FSF› |\n`G` | [IModelType](interfaces/imodeltype.md)‹PG, OG, FCG, FSG› |\n`H` | [IModelType](interfaces/imodeltype.md)‹PH, OH, FCH, FSH› |\n`I` | [IModelType](interfaces/imodeltype.md)‹PI, OI, FCI, FSI› |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹PA & PB & PC & PD & PE & PF & PG & PH & PI, OA & OB & OC & OD & OE & OF & OG & OH & OI, _CustomJoin‹FCA, _CustomJoin‹FCB, _CustomJoin‹FCC, _CustomJoin‹FCD, _CustomJoin‹FCE, _CustomJoin‹FCF, _CustomJoin‹FCG, _CustomJoin‹FCH, FCI››››››››, _CustomJoin‹FSA, _CustomJoin‹FSB, _CustomJoin‹FSC, _CustomJoin‹FSD, _CustomJoin‹FSE, _CustomJoin‹FSF, _CustomJoin‹FSG, _CustomJoin‹FSH, FSI›››››››››*\n\n___\n\n###  createActionTrackingMiddleware\n\n▸ **createActionTrackingMiddleware**<**T**>(`hooks`: [IActionTrackingMiddlewareHooks](interfaces/iactiontrackingmiddlewarehooks.md)‹T›): *[IMiddlewareHandler](index.md#imiddlewarehandler)*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:28](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L28)*\n\nNote: Consider migrating to `createActionTrackingMiddleware2`, it is easier to use.\n\nConvenience utility to create action based middleware that supports async processes more easily.\nAll hooks are called for both synchronous and asynchronous actions. Except that either `onSuccess` or `onFail` is called\n\nThe create middleware tracks the process of an action (assuming it passes the `filter`).\n`onResume` can return any value, which will be passed as second argument to any other hook. This makes it possible to keep state during a process.\n\nSee the `atomic` middleware for an example\n\n**Type parameters:**\n\n▪ **T**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`hooks` | [IActionTrackingMiddlewareHooks](interfaces/iactiontrackingmiddlewarehooks.md)‹T› |\n\n**Returns:** *[IMiddlewareHandler](index.md#imiddlewarehandler)*\n\n___\n\n###  createActionTrackingMiddleware2\n\n▸ **createActionTrackingMiddleware2**<**TEnv**>(`middlewareHooks`: [IActionTrackingMiddleware2Hooks](interfaces/iactiontrackingmiddleware2hooks.md)‹TEnv›): *[IMiddlewareHandler](index.md#imiddlewarehandler)*\n\n*Defined in [src/middlewares/createActionTrackingMiddleware2.ts:72](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/createActionTrackingMiddleware2.ts#L72)*\n\nConvenience utility to create action based middleware that supports async processes more easily.\nThe flow is like this:\n- for each action: if filter passes -> `onStart` -> (inner actions recursively) -> `onFinish`\n\nExample: if we had an action `a` that called inside an action `b1`, then `b2` the flow would be:\n- `filter(a)`\n- `onStart(a)`\n  - `filter(b1)`\n  - `onStart(b1)`\n  - `onFinish(b1)`\n  - `filter(b2)`\n  - `onStart(b2)`\n  - `onFinish(b2)`\n- `onFinish(a)`\n\nThe flow is the same no matter if the actions are sync or async.\n\nSee the `atomic` middleware for an example\n\n**Type parameters:**\n\n▪ **TEnv**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`middlewareHooks` | [IActionTrackingMiddleware2Hooks](interfaces/iactiontrackingmiddleware2hooks.md)‹TEnv› |\n\n**Returns:** *[IMiddlewareHandler](index.md#imiddlewarehandler)*\n\n___\n\n###  custom\n\n▸ **custom**<**S**, **T**>(`options`: [CustomTypeOptions](interfaces/customtypeoptions.md)‹S, T›): *[IType](interfaces/itype.md)‹S | T, S, T›*\n\n*Defined in [src/types/utility-types/custom.ts:74](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/custom.ts#L74)*\n\n`types.custom` - Creates a custom type. Custom types can be used for arbitrary immutable values, that have a serializable representation. For example, to create your own Date representation, Decimal type etc.\n\nThe signature of the options is:\n```ts\nexport interface CustomTypeOptions<S, T> {\n    // Friendly name\n    name: string\n    // given a serialized value and environment, how to turn it into the target type\n    fromSnapshot(snapshot: S, env: any): T\n    // return the serialization of the current value\n    toSnapshot(value: T): S\n    // if true, this is a converted value, if false, it's a snapshot\n    isTargetType(value: T | S): value is T\n    // a non empty string is assumed to be a validation error\n    getValidationMessage?(snapshot: S): string\n}\n```\n\nExample:\n```ts\nconst DecimalPrimitive = types.custom<string, Decimal>({\n    name: \"Decimal\",\n    fromSnapshot(value: string) {\n        return new Decimal(value)\n    },\n    toSnapshot(value: Decimal) {\n        return value.toString()\n    },\n    isTargetType(value: string | Decimal): boolean {\n        return value instanceof Decimal\n    },\n    getValidationMessage(value: string): string {\n        if (/^-?\\d+\\.\\d+$/.test(value)) return \"\" // OK\n        return `'${value}' doesn't look like a valid decimal number`\n    }\n})\n\nconst Wallet = types.model({\n    balance: DecimalPrimitive\n})\n```\n\n**Type parameters:**\n\n▪ **S**\n\n▪ **T**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`options` | [CustomTypeOptions](interfaces/customtypeoptions.md)‹S, T› |\n\n**Returns:** *[IType](interfaces/itype.md)‹S | T, S, T›*\n\n___\n\n###  decorate\n\n▸ **decorate**<**T**>(`handler`: [IMiddlewareHandler](index.md#imiddlewarehandler), `fn`: T, `includeHooks`: boolean): *T*\n\n*Defined in [src/core/action.ts:213](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L213)*\n\nBinds middleware to a specific action.\n\nExample:\n```ts\ntype.actions(self => {\n  function takeA____() {\n      self.toilet.donate()\n      self.wipe()\n      self.wipe()\n      self.toilet.flush()\n  }\n  return {\n    takeA____: decorate(atomic, takeA____)\n  }\n})\n```\n\n**Type parameters:**\n\n▪ **T**: *Function*\n\n**Parameters:**\n\nName | Type | Default |\n------ | ------ | ------ |\n`handler` | [IMiddlewareHandler](index.md#imiddlewarehandler) | - |\n`fn` | T | - |\n`includeHooks` | boolean | true |\n\n**Returns:** *T*\n\nThe original function\n\n___\n\n###  destroy\n\n▸ **destroy**(`target`: IAnyStateTreeNode): *void*\n\n*Defined in [src/core/mst-operations.ts:698](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L698)*\n\nRemoves a model element from the state tree, and mark it as end-of-life; the element should not be used anymore\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *void*\n\n___\n\n###  detach\n\n▸ **detach**<**T**>(`target`: T): *T*\n\n*Defined in [src/core/mst-operations.ts:687](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L687)*\n\nRemoves a model element from the state tree, and let it live on as a new state tree\n\n**Type parameters:**\n\n▪ **T**: *IAnyStateTreeNode*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | T |\n\n**Returns:** *T*\n\n___\n\n###  enumeration\n\n▸ **enumeration**<**T**>(`options`: keyof T[]): *[ISimpleType](interfaces/isimpletype.md)‹UnionStringArray‹T[]››*\n\n*Defined in [src/types/utility-types/enumeration.ts:11](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/enumeration.ts#L11)*\n\n`types.enumeration` - Can be used to create an string based enumeration.\n(note: this methods is just sugar for a union of string literals)\n\nExample:\n```ts\nconst TrafficLight = types.model({\n  color: types.enumeration(\"Color\", [\"Red\", \"Orange\", \"Green\"])\n})\n```\n\n**Type parameters:**\n\n▪ **T**: *string*\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`options` | keyof T[] | possible values this enumeration can have |\n\n**Returns:** *[ISimpleType](interfaces/isimpletype.md)‹UnionStringArray‹T[]››*\n\n▸ **enumeration**<**T**>(`name`: string, `options`: keyof T[]): *[ISimpleType](interfaces/isimpletype.md)‹UnionStringArray‹T[]››*\n\n*Defined in [src/types/utility-types/enumeration.ts:14](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/enumeration.ts#L14)*\n\n`types.enumeration` - Can be used to create an string based enumeration.\n(note: this methods is just sugar for a union of string literals)\n\nExample:\n```ts\nconst TrafficLight = types.model({\n  color: types.enumeration(\"Color\", [\"Red\", \"Orange\", \"Green\"])\n})\n```\n\n**Type parameters:**\n\n▪ **T**: *string*\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`name` | string | descriptive name of the enumeration (optional) |\n`options` | keyof T[] | possible values this enumeration can have |\n\n**Returns:** *[ISimpleType](interfaces/isimpletype.md)‹UnionStringArray‹T[]››*\n\n___\n\n###  escapeJsonPath\n\n▸ **escapeJsonPath**(`path`: string): *string*\n\n*Defined in [src/core/json-patch.ts:78](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L78)*\n\nEscape slashes and backslashes.\n\nhttp://tools.ietf.org/html/rfc6901\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`path` | string |\n\n**Returns:** *string*\n\n___\n\n###  flow\n\n▸ **flow**<**R**, **Args**>(`generator`: function): *function*\n\n*Defined in [src/core/flow.ts:21](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/flow.ts#L21)*\n\nSee [asynchronous actions](concepts/async-actions.md).\n\n**Type parameters:**\n\n▪ **R**\n\n▪ **Args**: *any[]*\n\n**Parameters:**\n\n▪ **generator**: *function*\n\n▸ (...`args`: Args): *Generator‹PromiseLike‹any›, R, any›*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`...args` | Args |\n\n**Returns:** *function*\n\nThe flow as a promise.\n\n▸ (...`args`: Args): *Promise‹FlowReturn‹R››*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`...args` | Args |\n\n___\n\n###  frozen\n\n▸ **frozen**<**C**>(`subType`: [IType](interfaces/itype.md)‹C, any, any›): *[IType](interfaces/itype.md)‹C, C, C›*\n\n*Defined in [src/types/utility-types/frozen.ts:59](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/frozen.ts#L59)*\n\n`types.frozen` - Frozen can be used to store any value that is serializable in itself (that is valid JSON).\nFrozen values need to be immutable or treated as if immutable. They need be serializable as well.\nValues stored in frozen will snapshotted as-is by MST, and internal changes will not be tracked.\n\nThis is useful to store complex, but immutable values like vectors etc. It can form a powerful bridge to parts of your application that should be immutable, or that assume data to be immutable.\n\nNote: if you want to store free-form state that is mutable, or not serializeable, consider using volatile state instead.\n\nFrozen properties can be defined in three different ways\n1. `types.frozen(SubType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type\n2. `types.frozen({ someDefaultValue: true})` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field\n3. `types.frozen<TypeScriptType>()` - provide a typescript type, to help in strongly typing the field (design time only)\n\nExample:\n```ts\nconst GameCharacter = types.model({\n  name: string,\n  location: types.frozen({ x: 0, y: 0})\n})\n\nconst hero = GameCharacter.create({\n  name: \"Mario\",\n  location: { x: 7, y: 4 }\n})\n\nhero.location = { x: 10, y: 2 } // OK\nhero.location.x = 7 // Not ok!\n```\n\n```ts\ntype Point = { x: number, y: number }\n   const Mouse = types.model({\n        loc: types.frozen<Point>()\n   })\n```\n\n**Type parameters:**\n\n▪ **C**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subType` | [IType](interfaces/itype.md)‹C, any, any› |\n\n**Returns:** *[IType](interfaces/itype.md)‹C, C, C›*\n\n▸ **frozen**<**T**>(`defaultValue`: T): *[IType](interfaces/itype.md)‹T | undefined | null, T, T›*\n\n*Defined in [src/types/utility-types/frozen.ts:60](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/frozen.ts#L60)*\n\n`types.frozen` - Frozen can be used to store any value that is serializable in itself (that is valid JSON).\nFrozen values need to be immutable or treated as if immutable. They need be serializable as well.\nValues stored in frozen will snapshotted as-is by MST, and internal changes will not be tracked.\n\nThis is useful to store complex, but immutable values like vectors etc. It can form a powerful bridge to parts of your application that should be immutable, or that assume data to be immutable.\n\nNote: if you want to store free-form state that is mutable, or not serializeable, consider using volatile state instead.\n\nFrozen properties can be defined in three different ways\n1. `types.frozen(SubType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type\n2. `types.frozen({ someDefaultValue: true})` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field\n3. `types.frozen<TypeScriptType>()` - provide a typescript type, to help in strongly typing the field (design time only)\n\nExample:\n```ts\nconst GameCharacter = types.model({\n  name: string,\n  location: types.frozen({ x: 0, y: 0})\n})\n\nconst hero = GameCharacter.create({\n  name: \"Mario\",\n  location: { x: 7, y: 4 }\n})\n\nhero.location = { x: 10, y: 2 } // OK\nhero.location.x = 7 // Not ok!\n```\n\n```ts\ntype Point = { x: number, y: number }\n   const Mouse = types.model({\n        loc: types.frozen<Point>()\n   })\n```\n\n**Type parameters:**\n\n▪ **T**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`defaultValue` | T |\n\n**Returns:** *[IType](interfaces/itype.md)‹T | undefined | null, T, T›*\n\n▸ **frozen**<**T**>(): *[IType](interfaces/itype.md)‹T, T, T›*\n\n*Defined in [src/types/utility-types/frozen.ts:61](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/frozen.ts#L61)*\n\n`types.frozen` - Frozen can be used to store any value that is serializable in itself (that is valid JSON).\nFrozen values need to be immutable or treated as if immutable. They need be serializable as well.\nValues stored in frozen will snapshotted as-is by MST, and internal changes will not be tracked.\n\nThis is useful to store complex, but immutable values like vectors etc. It can form a powerful bridge to parts of your application that should be immutable, or that assume data to be immutable.\n\nNote: if you want to store free-form state that is mutable, or not serializeable, consider using volatile state instead.\n\nFrozen properties can be defined in three different ways\n1. `types.frozen(SubType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type\n2. `types.frozen({ someDefaultValue: true})` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field\n3. `types.frozen<TypeScriptType>()` - provide a typescript type, to help in strongly typing the field (design time only)\n\nExample:\n```ts\nconst GameCharacter = types.model({\n  name: string,\n  location: types.frozen({ x: 0, y: 0})\n})\n\nconst hero = GameCharacter.create({\n  name: \"Mario\",\n  location: { x: 7, y: 4 }\n})\n\nhero.location = { x: 10, y: 2 } // OK\nhero.location.x = 7 // Not ok!\n```\n\n```ts\ntype Point = { x: number, y: number }\n   const Mouse = types.model({\n        loc: types.frozen<Point>()\n   })\n```\n\n**Type parameters:**\n\n▪ **T**\n\n**Returns:** *[IType](interfaces/itype.md)‹T, T, T›*\n\n___\n\n###  getChildType\n\n▸ **getChildType**(`object`: IAnyStateTreeNode, `propertyName?`: undefined | string): *[IAnyType](interfaces/ianytype.md)*\n\n*Defined in [src/core/mst-operations.ts:68](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L68)*\n\nReturns the _declared_ type of the given sub property of an object, array or map.\nIn the case of arrays and maps the property name is optional and will be ignored.\n\nExample:\n```ts\nconst Box = types.model({ x: 0, y: 0 })\nconst box = Box.create()\n\nconsole.log(getChildType(box, \"x\").name) // 'number'\n```\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`object` | IAnyStateTreeNode |\n`propertyName?` | undefined &#124; string |\n\n**Returns:** *[IAnyType](interfaces/ianytype.md)*\n\n___\n\n###  getEnv\n\n▸ **getEnv**<**T**>(`target`: IAnyStateTreeNode): *T*\n\n*Defined in [src/core/mst-operations.ts:773](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L773)*\n\nReturns the environment of the current state tree, or throws. For more info on environments,\nsee [Dependency injection](/concepts/dependency-injection)\n\nPlease note that in child nodes access to the root is only possible\nonce the `afterAttach` hook has fired\n\nReturns an empty environment if the tree wasn't initialized with an environment\n\n**Type parameters:**\n\n▪ **T**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *T*\n\n___\n\n###  getIdentifier\n\n▸ **getIdentifier**(`target`: IAnyStateTreeNode): *string | null*\n\n*Defined in [src/core/mst-operations.ts:549](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L549)*\n\nReturns the identifier of the target node.\nThis is the *string normalized* identifier, which might not match the type of the identifier attribute\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *string | null*\n\n___\n\n###  getLivelinessChecking\n\n▸ **getLivelinessChecking**(): *[LivelinessMode](index.md#livelinessmode)*\n\n*Defined in [src/core/node/livelinessChecking.ts:27](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/livelinessChecking.ts#L27)*\n\nReturns the current liveliness checking mode.\n\n**Returns:** *[LivelinessMode](index.md#livelinessmode)*\n\n`\"warn\"`, `\"error\"` or `\"ignore\"`\n\n___\n\n###  getMembers\n\n▸ **getMembers**(`target`: IAnyStateTreeNode): *[IModelReflectionData](interfaces/imodelreflectiondata.md)*\n\n*Defined in [src/core/mst-operations.ts:874](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L874)*\n\nReturns a reflection of the model node, including name, properties, views, volatile state,\nand actions. `flowActions` is also provided as a separate array of names for any action that\ncame from a flow generator as well.\n\nIn the case where a model has two actions: `doSomething` and `doSomethingWithFlow`, where\n`doSomethingWithFlow` is a flow generator, the `actions` array will contain both actions,\ni.e. [\"doSomething\", \"doSomethingWithFlow\"], and the `flowActions` array will contain only\nthe flow action, i.e. [\"doSomethingWithFlow\"].\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *[IModelReflectionData](interfaces/imodelreflectiondata.md)*\n\n___\n\n###  getNodeId\n\n▸ **getNodeId**(`target`: IAnyStateTreeNode): *number*\n\n*Defined in [src/core/mst-operations.ts:1026](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L1026)*\n\nReturns the unique node id (not to be confused with the instance identifier) for a\ngiven instance.\nThis id is a number that is unique for each instance.\n\n**`export`** \n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *number*\n\n___\n\n###  getParent\n\n▸ **getParent**<**IT**>(`target`: IAnyStateTreeNode, `depth`: number): *TypeOrStateTreeNodeToStateTreeNode‹IT›*\n\n*Defined in [src/core/mst-operations.ts:382](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L382)*\n\nReturns the immediate parent of this object, or throws.\n\nNote that the immediate parent can be either an object, map or array, and\ndoesn't necessarily refer to the parent model.\n\nPlease note that in child nodes access to the root is only possible\nonce the `afterAttach` hook has fired.\n\n**Type parameters:**\n\n▪ **IT**: *IAnyStateTreeNode | [IAnyComplexType](interfaces/ianycomplextype.md)*\n\n**Parameters:**\n\nName | Type | Default | Description |\n------ | ------ | ------ | ------ |\n`target` | IAnyStateTreeNode | - | - |\n`depth` | number | 1 | How far should we look upward? 1 by default. |\n\n**Returns:** *TypeOrStateTreeNodeToStateTreeNode‹IT›*\n\n___\n\n###  getParentOfType\n\n▸ **getParentOfType**<**IT**>(`target`: IAnyStateTreeNode, `type`: IT): *IT[\"Type\"]*\n\n*Defined in [src/core/mst-operations.ts:426](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L426)*\n\nReturns the target's parent of a given type, or throws.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n`type` | IT |\n\n**Returns:** *IT[\"Type\"]*\n\n___\n\n###  getPath\n\n▸ **getPath**(`target`: IAnyStateTreeNode): *string*\n\n*Defined in [src/core/mst-operations.ts:466](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L466)*\n\nReturns the path of the given object in the model tree\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *string*\n\n___\n\n###  getPathParts\n\n▸ **getPathParts**(`target`: IAnyStateTreeNode): *string[]*\n\n*Defined in [src/core/mst-operations.ts:479](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L479)*\n\nReturns the path of the given object as unescaped string array.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *string[]*\n\n___\n\n###  getPropertyMembers\n\n▸ **getPropertyMembers**(`typeOrNode`: [IAnyModelType](interfaces/ianymodeltype.md) | IAnyStateTreeNode): *[IModelReflectionPropertiesData](interfaces/imodelreflectionpropertiesdata.md)*\n\n*Defined in [src/core/mst-operations.ts:835](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L835)*\n\nReturns a reflection of the model type properties and name for either a model type or model node.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`typeOrNode` | [IAnyModelType](interfaces/ianymodeltype.md) &#124; IAnyStateTreeNode |\n\n**Returns:** *[IModelReflectionPropertiesData](interfaces/imodelreflectionpropertiesdata.md)*\n\n___\n\n###  getRelativePath\n\n▸ **getRelativePath**(`base`: IAnyStateTreeNode, `target`: IAnyStateTreeNode): *string*\n\n*Defined in [src/core/mst-operations.ts:648](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L648)*\n\nGiven two state tree nodes that are part of the same tree,\nreturns the shortest jsonpath needed to navigate from the one to the other\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`base` | IAnyStateTreeNode |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *string*\n\n___\n\n###  getRoot\n\n▸ **getRoot**<**IT**>(`target`: IAnyStateTreeNode): *TypeOrStateTreeNodeToStateTreeNode‹IT›*\n\n*Defined in [src/core/mst-operations.ts:451](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L451)*\n\nGiven an object in a model tree, returns the root object of that tree.\n\nPlease note that in child nodes access to the root is only possible\nonce the `afterAttach` hook has fired.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyComplexType](interfaces/ianycomplextype.md) | IAnyStateTreeNode*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *TypeOrStateTreeNodeToStateTreeNode‹IT›*\n\n___\n\n###  getRunningActionContext\n\n▸ **getRunningActionContext**(): *[IActionContext](interfaces/iactioncontext.md) | undefined*\n\n*Defined in [src/core/actionContext.ts:26](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L26)*\n\nReturns the currently executing MST action context, or undefined if none.\n\n**Returns:** *[IActionContext](interfaces/iactioncontext.md) | undefined*\n\n___\n\n###  getSnapshot\n\n▸ **getSnapshot**<**S**>(`target`: IStateTreeNode‹[IType](interfaces/itype.md)‹any, S, any››, `applyPostProcess`: boolean): *S*\n\n*Defined in [src/core/mst-operations.ts:336](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L336)*\n\nCalculates a snapshot from the given model instance. The snapshot will always reflect the latest state but use\nstructural sharing where possible. Doesn't require MobX transactions to be completed.\n\n**Type parameters:**\n\n▪ **S**\n\n**Parameters:**\n\nName | Type | Default | Description |\n------ | ------ | ------ | ------ |\n`target` | IStateTreeNode‹[IType](interfaces/itype.md)‹any, S, any›› | - | - |\n`applyPostProcess` | boolean | true | If true (the default) then postProcessSnapshot gets applied. |\n\n**Returns:** *S*\n\n___\n\n###  getType\n\n▸ **getType**(`object`: IAnyStateTreeNode): *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n*Defined in [src/core/mst-operations.ts:46](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L46)*\n\nReturns the _actual_ type of the given tree node. (Or throws)\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`object` | IAnyStateTreeNode |\n\n**Returns:** *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n___\n\n###  hasEnv\n\n▸ **hasEnv**(`target`: IAnyStateTreeNode): *boolean*\n\n*Defined in [src/core/mst-operations.ts:790](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L790)*\n\nReturns whether the current state tree has environment or not.\n\n**`export`** \n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *boolean*\n\n___\n\n###  hasParent\n\n▸ **hasParent**(`target`: IAnyStateTreeNode, `depth`: number): *boolean*\n\n*Defined in [src/core/mst-operations.ts:356](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L356)*\n\nGiven a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array.\n\n**Parameters:**\n\nName | Type | Default | Description |\n------ | ------ | ------ | ------ |\n`target` | IAnyStateTreeNode | - | - |\n`depth` | number | 1 | How far should we look upward? 1 by default. |\n\n**Returns:** *boolean*\n\n___\n\n###  hasParentOfType\n\n▸ **hasParentOfType**(`target`: IAnyStateTreeNode, `type`: [IAnyComplexType](interfaces/ianycomplextype.md)): *boolean*\n\n*Defined in [src/core/mst-operations.ts:406](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L406)*\n\nGiven a model instance, returns `true` if the object has a parent of given type, that is, is part of another object, map or array\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n`type` | [IAnyComplexType](interfaces/ianycomplextype.md) |\n\n**Returns:** *boolean*\n\n___\n\n###  isActionContextChildOf\n\n▸ **isActionContextChildOf**(`actionContext`: [IActionContext](interfaces/iactioncontext.md), `parent`: number | [IActionContext](interfaces/iactioncontext.md) | [IMiddlewareEvent](interfaces/imiddlewareevent.md)): *boolean*\n\n*Defined in [src/core/actionContext.ts:56](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L56)*\n\nReturns if the given action context is a parent of this action context.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`actionContext` | [IActionContext](interfaces/iactioncontext.md) |\n`parent` | number &#124; [IActionContext](interfaces/iactioncontext.md) &#124; [IMiddlewareEvent](interfaces/imiddlewareevent.md) |\n\n**Returns:** *boolean*\n\n___\n\n###  isActionContextThisOrChildOf\n\n▸ **isActionContextThisOrChildOf**(`actionContext`: [IActionContext](interfaces/iactioncontext.md), `parentOrThis`: number | [IActionContext](interfaces/iactioncontext.md) | [IMiddlewareEvent](interfaces/imiddlewareevent.md)): *boolean*\n\n*Defined in [src/core/actionContext.ts:66](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L66)*\n\nReturns if the given action context is this or a parent of this action context.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`actionContext` | [IActionContext](interfaces/iactioncontext.md) |\n`parentOrThis` | number &#124; [IActionContext](interfaces/iactioncontext.md) &#124; [IMiddlewareEvent](interfaces/imiddlewareevent.md) |\n\n**Returns:** *boolean*\n\n___\n\n###  isAlive\n\n▸ **isAlive**(`target`: IAnyStateTreeNode): *boolean*\n\n*Defined in [src/core/mst-operations.ts:716](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L716)*\n\nReturns true if the given state tree node is not killed yet.\nThis means that the node is still a part of a tree, and that `destroy`\nhas not been called. If a node is not alive anymore, the only thing one can do with it\nis requesting it's last path and snapshot\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *boolean*\n\n___\n\n###  isArrayType\n\n▸ **isArrayType**(`type`: unknown): *type is IArrayType<IAnyType>*\n\n*Defined in [src/types/complex-types/array.ts:512](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/array.ts#L512)*\n\nReturns if a given value represents an array type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IArrayType<IAnyType>*\n\n`true` if the type is an array type.\n\n___\n\n###  isFrozenType\n\n▸ **isFrozenType**(`type`: unknown): *type is ISimpleType<any>*\n\n*Defined in [src/types/utility-types/frozen.ts:114](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/frozen.ts#L114)*\n\nReturns if a given value represents a frozen type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is ISimpleType<any>*\n\n___\n\n###  isIdentifierType\n\n▸ **isIdentifierType**(`type`: unknown): *type is ISimpleType | ISimpleType*\n\n*Defined in [src/types/utility-types/identifier.ts:138](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/identifier.ts#L138)*\n\nReturns if a given value represents an identifier type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is ISimpleType | ISimpleType*\n\n___\n\n###  isLateType\n\n▸ **isLateType**(`type`: unknown): *type is IAnyType*\n\n*Defined in [src/types/utility-types/late.ts:144](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/late.ts#L144)*\n\nReturns if a given value represents a late type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IAnyType*\n\n___\n\n###  isLiteralType\n\n▸ **isLiteralType**(`type`: unknown): *type is ISimpleType<any>*\n\n*Defined in [src/types/utility-types/literal.ts:85](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/literal.ts#L85)*\n\nReturns if a given value represents a literal type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is ISimpleType<any>*\n\n___\n\n###  isMapType\n\n▸ **isMapType**(`type`: unknown): *type is IMapType<IAnyType>*\n\n*Defined in [src/types/complex-types/map.ts:521](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/map.ts#L521)*\n\nReturns if a given value represents a map type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IMapType<IAnyType>*\n\n`true` if it is a map type.\n\n___\n\n###  isModelType\n\n▸ **isModelType**(`type`: unknown): *type is IAnyModelType*\n\n*Defined in [src/types/complex-types/model.ts:897](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L897)*\n\nReturns if a given value represents a model type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IAnyModelType*\n\n___\n\n###  isOptionalType\n\n▸ **isOptionalType**(`type`: unknown): *type is IOptionalIType<IAnyType, []>*\n\n*Defined in [src/types/utility-types/optional.ts:234](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/optional.ts#L234)*\n\nReturns if a value represents an optional type.\n\n**`template`** IT\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IOptionalIType<IAnyType, []>*\n\n___\n\n###  isPrimitiveType\n\n▸ **isPrimitiveType**(`type`: unknown): *type is ISimpleType<string> | ISimpleType<number> | ISimpleType<boolean> | IType*\n\n*Defined in [src/types/primitives.ts:241](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/primitives.ts#L241)*\n\nReturns if a given value represents a primitive type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is ISimpleType<string> | ISimpleType<number> | ISimpleType<boolean> | IType*\n\n___\n\n###  isProtected\n\n▸ **isProtected**(`target`: IAnyStateTreeNode): *boolean*\n\n*Defined in [src/core/mst-operations.ts:310](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L310)*\n\nReturns true if the object is in protected mode, @see protect\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *boolean*\n\n___\n\n###  isReferenceType\n\n▸ **isReferenceType**(`type`: unknown): *type is IReferenceType<IAnyComplexType>*\n\n*Defined in [src/types/utility-types/reference.ts:541](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L541)*\n\nReturns if a given value represents a reference type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IReferenceType<IAnyComplexType>*\n\n___\n\n###  isRefinementType\n\n▸ **isRefinementType**(`type`: unknown): *type is IAnyType*\n\n*Defined in [src/types/utility-types/refinement.ts:125](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/refinement.ts#L125)*\n\nReturns if a given value is a refinement type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IAnyType*\n\n___\n\n###  isRoot\n\n▸ **isRoot**(`target`: IAnyStateTreeNode): *boolean*\n\n*Defined in [src/core/mst-operations.ts:492](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L492)*\n\nReturns true if the given object is the root of a model tree.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *boolean*\n\n___\n\n###  isStateTreeNode\n\n▸ **isStateTreeNode**<**IT**>(`value`: any): *value is STNValue<Instance<IT>, IT>*\n\n*Defined in [src/core/node/node-utils.ts:67](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/node-utils.ts#L67)*\n\nReturns true if the given value is a node in a state tree.\nMore precisely, that is, if the value is an instance of a\n`types.model`, `types.array` or `types.map`.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`value` | any |\n\n**Returns:** *value is STNValue<Instance<IT>, IT>*\n\ntrue if the value is a state tree node.\n\n___\n\n###  isType\n\n▸ **isType**(`value`: any): *value is IAnyType*\n\n*Defined in [src/core/type/type.ts:541](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L541)*\n\nReturns if a given value represents a type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`value` | any | Value to check. |\n\n**Returns:** *value is IAnyType*\n\n`true` if the value is a type.\n\n___\n\n###  isUnionType\n\n▸ **isUnionType**(`type`: unknown): *type is IUnionType<IAnyType[]>*\n\n*Defined in [src/types/utility-types/union.ts:218](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L218)*\n\nReturns if a given value represents a union type.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | unknown |\n\n**Returns:** *type is IUnionType<IAnyType[]>*\n\n___\n\n###  isValidReference\n\n▸ **isValidReference**<**N**>(`getter`: function, `checkIfAlive`: boolean): *boolean*\n\n*Defined in [src/core/mst-operations.ts:596](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L596)*\n\nTests if a reference is valid (pointing to an existing node and optionally if alive) and returns if the check passes or not.\n\n**Type parameters:**\n\n▪ **N**: *IAnyStateTreeNode*\n\n**Parameters:**\n\n▪ **getter**: *function*\n\nFunction to access the reference.\n\n▸ (): *N | null | undefined*\n\n▪`Default value`  **checkIfAlive**: *boolean*= true\n\ntrue to also make sure the referenced node is alive (default), false to skip this check.\n\n**Returns:** *boolean*\n\n___\n\n###  joinJsonPath\n\n▸ **joinJsonPath**(`path`: string[]): *string*\n\n*Defined in [src/core/json-patch.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L99)*\n\nGenerates a json-path compliant json path from path parts.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`path` | string[] |\n\n**Returns:** *string*\n\n___\n\n###  late\n\n▸ **late**<**T**>(`type`: function): *T*\n\n*Defined in [src/types/utility-types/late.ts:106](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/late.ts#L106)*\n\n`types.late` - Defines a type that gets implemented later. This is useful when you have to deal with circular dependencies.\nPlease notice that when defining circular dependencies TypeScript isn't smart enough to inference them.\n\nExample:\n```ts\n  // TypeScript isn't smart enough to infer self referencing types.\n const Node = types.model({\n      children: types.array(types.late((): IAnyModelType => Node)) // then typecast each array element to Instance<typeof Node>\n })\n```\n\n**Type parameters:**\n\n▪ **T**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\n▪ **type**: *function*\n\nA function that returns the type that will be defined.\n\n▸ (): *T*\n\n**Returns:** *T*\n\n▸ **late**<**T**>(`name`: string, `type`: function): *T*\n\n*Defined in [src/types/utility-types/late.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/late.ts#L107)*\n\n`types.late` - Defines a type that gets implemented later. This is useful when you have to deal with circular dependencies.\nPlease notice that when defining circular dependencies TypeScript isn't smart enough to inference them.\n\nExample:\n```ts\n  // TypeScript isn't smart enough to infer self referencing types.\n const Node = types.model({\n      children: types.array(types.late((): IAnyModelType => Node)) // then typecast each array element to Instance<typeof Node>\n })\n```\n\n**Type parameters:**\n\n▪ **T**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\n▪ **name**: *string*\n\nThe name to use for the type that will be returned.\n\n▪ **type**: *function*\n\nA function that returns the type that will be defined.\n\n▸ (): *T*\n\n**Returns:** *T*\n\n___\n\n###  lazy\n\n▸ **lazy**<**T**, **U**>(`name`: string, `options`: LazyOptions‹T, U›): *T*\n\n*Defined in [src/types/utility-types/lazy.ts:22](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/lazy.ts#L22)*\n\n**Type parameters:**\n\n▪ **T**: *[IType](interfaces/itype.md)‹any, any, any›*\n\n▪ **U**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`options` | LazyOptions‹T, U› |\n\n**Returns:** *T*\n\n___\n\n###  literal\n\n▸ **literal**<**S**>(`value`: S): *[ISimpleType](interfaces/isimpletype.md)‹S›*\n\n*Defined in [src/types/utility-types/literal.ts:72](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/literal.ts#L72)*\n\n`types.literal` - The literal type will return a type that will match only the exact given type.\nThe given value must be a primitive, in order to be serialized to a snapshot correctly.\nYou can use literal to match exact strings for example the exact male or female string.\n\nExample:\n```ts\nconst Person = types.model({\n    name: types.string,\n    gender: types.union(types.literal('male'), types.literal('female'))\n})\n```\n\n**Type parameters:**\n\n▪ **S**: *Primitives*\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`value` | S | The value to use in the strict equal check |\n\n**Returns:** *[ISimpleType](interfaces/isimpletype.md)‹S›*\n\n___\n\n###  map\n\n▸ **map**<**IT**>(`subtype`: IT): *IMapType‹IT›*\n\n*Defined in [src/types/complex-types/map.ts:511](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/map.ts#L511)*\n\n`types.map` - Creates a key based collection type who's children are all of a uniform declared type.\nIf the type stored in a map has an identifier, it is mandatory to store the child under that identifier in the map.\n\nThis type will always produce [observable maps](https://mobx.js.org/api.html#observablemap)\n\nExample:\n```ts\nconst Todo = types.model({\n  id: types.identifier,\n  task: types.string\n})\n\nconst TodoStore = types.model({\n  todos: types.map(Todo)\n})\n\nconst s = TodoStore.create({ todos: {} })\nunprotect(s)\ns.todos.set(17, { task: \"Grab coffee\", id: 17 })\ns.todos.put({ task: \"Grab cookie\", id: 18 }) // put will infer key from the identifier\nconsole.log(s.todos.get(17).task) // prints: \"Grab coffee\"\n```\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subtype` | IT |\n\n**Returns:** *IMapType‹IT›*\n\n___\n\n###  maybe\n\n▸ **maybe**<**IT**>(`type`: IT): *IMaybe‹IT›*\n\n*Defined in [src/types/utility-types/maybe.ts:31](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/maybe.ts#L31)*\n\n`types.maybe` - Maybe will make a type nullable, and also optional.\nThe value `undefined` will be used to represent nullability.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | IT |\n\n**Returns:** *IMaybe‹IT›*\n\n___\n\n###  maybeNull\n\n▸ **maybeNull**<**IT**>(`type`: IT): *IMaybeNull‹IT›*\n\n*Defined in [src/types/utility-types/maybe.ts:44](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/maybe.ts#L44)*\n\n`types.maybeNull` - Maybe will make a type nullable, and also optional.\nThe value `null` will be used to represent no value.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | IT |\n\n**Returns:** *IMaybeNull‹IT›*\n\n___\n\n###  model\n\n▸ **model**<**P**>(`name`: string, `properties?`: [P](undefined)): *[IModelType](interfaces/imodeltype.md)‹ModelPropertiesDeclarationToProperties‹P›, __type›*\n\n*Defined in [src/types/complex-types/model.ts:781](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L781)*\n\n`types.model` - Creates a new model type by providing a name, properties, volatile state and actions.\n\nSee the [model type](/concepts/trees#creating-models) description or the [getting started](intro/getting-started.md#getting-started-1) tutorial.\n\n**Type parameters:**\n\n▪ **P**: *ModelPropertiesDeclaration*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`name` | string |\n`properties?` | [P](undefined) |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹ModelPropertiesDeclarationToProperties‹P›, __type›*\n\n▸ **model**<**P**>(`properties?`: [P](undefined)): *[IModelType](interfaces/imodeltype.md)‹ModelPropertiesDeclarationToProperties‹P›, __type›*\n\n*Defined in [src/types/complex-types/model.ts:785](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L785)*\n\n`types.model` - Creates a new model type by providing a name, properties, volatile state and actions.\n\nSee the [model type](/concepts/trees#creating-models) description or the [getting started](intro/getting-started.md#getting-started-1) tutorial.\n\n**Type parameters:**\n\n▪ **P**: *ModelPropertiesDeclaration*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`properties?` | [P](undefined) |\n\n**Returns:** *[IModelType](interfaces/imodeltype.md)‹ModelPropertiesDeclarationToProperties‹P›, __type›*\n\n___\n\n###  onAction\n\n▸ **onAction**(`target`: IAnyStateTreeNode, `listener`: function, `attachAfter`: boolean): *[IDisposer](index.md#idisposer)*\n\n*Defined in [src/middlewares/on-action.ts:226](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L226)*\n\nRegisters a function that will be invoked for each action that is called on the provided model instance, or to any of its children.\nSee [actions](https://github.com/mobxjs/mobx-state-tree#actions) for more details. onAction events are emitted only for the outermost called action in the stack.\nAction can also be intercepted by middleware using addMiddleware to change the function call before it will be run.\n\nNot all action arguments might be serializable. For unserializable arguments, a struct like `{ $MST_UNSERIALIZABLE: true, type: \"someType\" }` will be generated.\nMST Nodes are considered non-serializable as well (they could be serialized as there snapshot, but it is uncertain whether an replaying party will be able to handle such a non-instantiated snapshot).\nRather, when using `onAction` middleware, one should consider in passing arguments which are 1: an id, 2: a (relative) path, or 3: a snapshot. Instead of a real MST node.\n\nExample:\n```ts\nconst Todo = types.model({\n  task: types.string\n})\n\nconst TodoStore = types.model({\n  todos: types.array(Todo)\n}).actions(self => ({\n  add(todo) {\n    self.todos.push(todo);\n  }\n}))\n\nconst s = TodoStore.create({ todos: [] })\n\nlet disposer = onAction(s, (call) => {\n  console.log(call);\n})\n\ns.add({ task: \"Grab a coffee\" })\n// Logs: { name: \"add\", path: \"\", args: [{ task: \"Grab a coffee\" }] }\n```\n\n**Parameters:**\n\n▪ **target**: *IAnyStateTreeNode*\n\n▪ **listener**: *function*\n\n▸ (`call`: [ISerializedActionCall](interfaces/iserializedactioncall.md)): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [ISerializedActionCall](interfaces/iserializedactioncall.md) |\n\n▪`Default value`  **attachAfter**: *boolean*= false\n\n(default false) fires the listener *after* the action has executed instead of before.\n\n**Returns:** *[IDisposer](index.md#idisposer)*\n\n___\n\n###  onPatch\n\n▸ **onPatch**(`target`: IAnyStateTreeNode, `callback`: function): *[IDisposer](index.md#idisposer)*\n\n*Defined in [src/core/mst-operations.ts:83](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L83)*\n\nRegisters a function that will be invoked for each mutation that is applied to the provided model instance, or to any of its children.\nSee [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details. onPatch events are emitted immediately and will not await the end of a transaction.\nPatches can be used to deeply observe a model tree.\n\n**Parameters:**\n\n▪ **target**: *IAnyStateTreeNode*\n\nthe model instance from which to receive patches\n\n▪ **callback**: *function*\n\nthe callback that is invoked for each patch. The reversePatch is a patch that would actually undo the emitted patch\n\n▸ (`patch`: [IJsonPatch](interfaces/ijsonpatch.md), `reversePatch`: [IJsonPatch](interfaces/ijsonpatch.md)): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`patch` | [IJsonPatch](interfaces/ijsonpatch.md) |\n`reversePatch` | [IJsonPatch](interfaces/ijsonpatch.md) |\n\n**Returns:** *[IDisposer](index.md#idisposer)*\n\nfunction to remove the listener\n\n___\n\n###  onSnapshot\n\n▸ **onSnapshot**<**S**>(`target`: IStateTreeNode‹[IType](interfaces/itype.md)‹any, S, any››, `callback`: function): *[IDisposer](index.md#idisposer)*\n\n*Defined in [src/core/mst-operations.ts:103](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L103)*\n\nRegisters a function that is invoked whenever a new snapshot for the given model instance is available.\nThe listener will only be fire at the end of the current MobX (trans)action.\nSee [snapshots](https://github.com/mobxjs/mobx-state-tree#snapshots) for more details.\n\n**Type parameters:**\n\n▪ **S**\n\n**Parameters:**\n\n▪ **target**: *IStateTreeNode‹[IType](interfaces/itype.md)‹any, S, any››*\n\n▪ **callback**: *function*\n\n▸ (`snapshot`: S): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | S |\n\n**Returns:** *[IDisposer](index.md#idisposer)*\n\n___\n\n###  optional\n\n▸ **optional**<**IT**>(`type`: IT, `defaultValueOrFunction`: OptionalDefaultValueOrFunction‹IT›): *IOptionalIType‹IT, []›*\n\n*Defined in [src/types/utility-types/optional.ts:160](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/optional.ts#L160)*\n\n`types.optional` - Can be used to create a property with a default value.\n\nDepending on the third argument (`optionalValues`) there are two ways of operation:\n- If the argument is not provided, then if a value is not provided in the snapshot (`undefined` or missing),\n  it will default to the provided `defaultValue`\n- If the argument is provided, then if the value in the snapshot matches one of the optional values inside the array then it will\n  default to the provided `defaultValue`. Additionally, if one of the optional values inside the array is `undefined` then a missing\n  property is also valid.\n\n  Note that it is also possible to include values of the same type as the intended subtype as optional values,\n  in this case the optional value will be transformed into the `defaultValue` (e.g. `types.optional(types.string, \"unnamed\", [undefined, \"\"])`\n  will transform the snapshot values `undefined` (and therefore missing) and empty strings into the string `\"unnamed\"` when it gets\n  instantiated).\n\nIf `defaultValue` is a function, the function will be invoked for every new instance.\nApplying a snapshot in which the optional value is one of the optional values (or `undefined`/_not_ present if none are provided) causes the\nvalue to be reset.\n\nExample:\n```ts\nconst Todo = types.model({\n  title: types.string,\n  subtitle1: types.optional(types.string, \"\", [null]),\n  subtitle2: types.optional(types.string, \"\", [null, undefined]),\n  done: types.optional(types.boolean, false),\n  created: types.optional(types.Date, () => new Date()),\n})\n\n// if done is missing / undefined it will become false\n// if created is missing / undefined it will get a freshly generated timestamp\n// if subtitle1 is null it will default to \"\", but it cannot be missing or undefined\n// if subtitle2 is null or undefined it will default to \"\"; since it can be undefined it can also be missing\nconst todo = Todo.create({ title: \"Get coffee\", subtitle1: null })\n```\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | IT |\n`defaultValueOrFunction` | OptionalDefaultValueOrFunction‹IT› |\n\n**Returns:** *IOptionalIType‹IT, []›*\n\n▸ **optional**<**IT**, **OptionalVals**>(`type`: IT, `defaultValueOrFunction`: OptionalDefaultValueOrFunction‹IT›, `optionalValues`: OptionalVals): *IOptionalIType‹IT, OptionalVals›*\n\n*Defined in [src/types/utility-types/optional.ts:164](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/optional.ts#L164)*\n\n`types.optional` - Can be used to create a property with a default value.\n\nDepending on the third argument (`optionalValues`) there are two ways of operation:\n- If the argument is not provided, then if a value is not provided in the snapshot (`undefined` or missing),\n  it will default to the provided `defaultValue`\n- If the argument is provided, then if the value in the snapshot matches one of the optional values inside the array then it will\n  default to the provided `defaultValue`. Additionally, if one of the optional values inside the array is `undefined` then a missing\n  property is also valid.\n\n  Note that it is also possible to include values of the same type as the intended subtype as optional values,\n  in this case the optional value will be transformed into the `defaultValue` (e.g. `types.optional(types.string, \"unnamed\", [undefined, \"\"])`\n  will transform the snapshot values `undefined` (and therefore missing) and empty strings into the string `\"unnamed\"` when it gets\n  instantiated).\n\nIf `defaultValue` is a function, the function will be invoked for every new instance.\nApplying a snapshot in which the optional value is one of the optional values (or `undefined`/_not_ present if none are provided) causes the\nvalue to be reset.\n\nExample:\n```ts\nconst Todo = types.model({\n  title: types.string,\n  subtitle1: types.optional(types.string, \"\", [null]),\n  subtitle2: types.optional(types.string, \"\", [null, undefined]),\n  done: types.optional(types.boolean, false),\n  created: types.optional(types.Date, () => new Date()),\n})\n\n// if done is missing / undefined it will become false\n// if created is missing / undefined it will get a freshly generated timestamp\n// if subtitle1 is null it will default to \"\", but it cannot be missing or undefined\n// if subtitle2 is null or undefined it will default to \"\"; since it can be undefined it can also be missing\nconst todo = Todo.create({ title: \"Get coffee\", subtitle1: null })\n```\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n▪ **OptionalVals**: *ValidOptionalValues*\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`type` | IT | - |\n`defaultValueOrFunction` | OptionalDefaultValueOrFunction‹IT› | - |\n`optionalValues` | OptionalVals | an optional array with zero or more primitive values (string, number, boolean, null or undefined)                       that will be converted into the default. `[ undefined ]` is assumed when none is provided |\n\n**Returns:** *IOptionalIType‹IT, OptionalVals›*\n\n___\n\n###  protect\n\n▸ **protect**(`target`: IAnyStateTreeNode): *void*\n\n*Defined in [src/core/mst-operations.ts:265](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L265)*\n\nThe inverse of `unprotect`.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`target` | IAnyStateTreeNode |   |\n\n**Returns:** *void*\n\n___\n\n###  recordActions\n\n▸ **recordActions**(`subject`: IAnyStateTreeNode, `filter?`: undefined | function): *[IActionRecorder](interfaces/iactionrecorder.md)*\n\n*Defined in [src/middlewares/on-action.ts:148](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L148)*\n\nSmall abstraction around `onAction` and `applyAction`, attaches an action listener to a tree and records all the actions emitted.\nReturns an recorder object with the following signature:\n\nExample:\n```ts\nexport interface IActionRecorder {\n     // the recorded actions\n     actions: ISerializedActionCall[]\n     // true if currently recording\n     recording: boolean\n     // stop recording actions\n     stop(): void\n     // resume recording actions\n     resume(): void\n     // apply all the recorded actions on the given object\n     replay(target: IAnyStateTreeNode): void\n}\n```\n\nThe optional filter function allows to skip recording certain actions.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subject` | IAnyStateTreeNode |\n`filter?` | undefined &#124; function |\n\n**Returns:** *[IActionRecorder](interfaces/iactionrecorder.md)*\n\n___\n\n###  recordPatches\n\n▸ **recordPatches**(`subject`: IAnyStateTreeNode, `filter?`: undefined | function): *[IPatchRecorder](interfaces/ipatchrecorder.md)*\n\n*Defined in [src/core/mst-operations.ts:177](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L177)*\n\nSmall abstraction around `onPatch` and `applyPatch`, attaches a patch listener to a tree and records all the patches.\nReturns a recorder object with the following signature:\n\nExample:\n```ts\nexport interface IPatchRecorder {\n     // the recorded patches\n     patches: IJsonPatch[]\n     // the inverse of the recorded patches\n     inversePatches: IJsonPatch[]\n     // true if currently recording\n     recording: boolean\n     // stop recording patches\n     stop(): void\n     // resume recording patches\n     resume(): void\n     // apply all the recorded patches on the given target (the original subject if omitted)\n     replay(target?: IAnyStateTreeNode): void\n     // reverse apply the recorded patches on the given target  (the original subject if omitted)\n     // stops the recorder if not already stopped\n     undo(): void\n}\n```\n\nThe optional filter function allows to skip recording certain patches.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subject` | IAnyStateTreeNode |\n`filter?` | undefined &#124; function |\n\n**Returns:** *[IPatchRecorder](interfaces/ipatchrecorder.md)*\n\n___\n\n###  reference\n\n▸ **reference**<**IT**>(`subType`: IT, `options?`: [ReferenceOptions](index.md#referenceoptions)‹IT›): *IReferenceType‹IT›*\n\n*Defined in [src/types/utility-types/reference.ts:494](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L494)*\n\n`types.reference` - Creates a reference to another type, which should have defined an identifier.\nSee also the [reference and identifiers](https://github.com/mobxjs/mobx-state-tree#references-and-identifiers) section.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subType` | IT |\n`options?` | [ReferenceOptions](index.md#referenceoptions)‹IT› |\n\n**Returns:** *IReferenceType‹IT›*\n\n___\n\n###  refinement\n\n▸ **refinement**<**IT**>(`name`: string, `type`: IT, `predicate`: function, `message?`: string | function): *IT*\n\n*Defined in [src/types/utility-types/refinement.ts:83](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/refinement.ts#L83)*\n\n`types.refinement` - Creates a type that is more specific than the base type, e.g. `types.refinement(types.string, value => value.length > 5)` to create a type of strings that can only be longer then 5.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\n▪ **name**: *string*\n\n▪ **type**: *IT*\n\n▪ **predicate**: *function*\n\n▸ (`snapshot`: IT[\"CreationType\"]): *boolean*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | IT[\"CreationType\"] |\n\n▪`Optional`  **message**: *string | function*\n\n**Returns:** *IT*\n\n▸ **refinement**<**IT**>(`type`: IT, `predicate`: function, `message?`: string | function): *IT*\n\n*Defined in [src/types/utility-types/refinement.ts:89](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/refinement.ts#L89)*\n\n`types.refinement` - Creates a type that is more specific than the base type, e.g. `types.refinement(types.string, value => value.length > 5)` to create a type of strings that can only be longer then 5.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\n▪ **type**: *IT*\n\n▪ **predicate**: *function*\n\n▸ (`snapshot`: IT[\"CreationType\"]): *boolean*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | IT[\"CreationType\"] |\n\n▪`Optional`  **message**: *string | function*\n\n**Returns:** *IT*\n\n___\n\n###  resolveIdentifier\n\n▸ **resolveIdentifier**<**IT**>(`type`: IT, `target`: IAnyStateTreeNode, `identifier`: [ReferenceIdentifier](index.md#referenceidentifier)): *IT[\"Type\"] | undefined*\n\n*Defined in [src/core/mst-operations.ts:525](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L525)*\n\nResolves a model instance given a root target, the type and the identifier you are searching for.\nReturns undefined if no value can be found.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyModelType](interfaces/ianymodeltype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`type` | IT |\n`target` | IAnyStateTreeNode |\n`identifier` | [ReferenceIdentifier](index.md#referenceidentifier) |\n\n**Returns:** *IT[\"Type\"] | undefined*\n\n___\n\n###  resolvePath\n\n▸ **resolvePath**(`target`: IAnyStateTreeNode, `path`: string): *any*\n\n*Defined in [src/core/mst-operations.ts:507](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L507)*\n\nResolves a path relatively to a given object.\nReturns undefined if no value can be found.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`target` | IAnyStateTreeNode | - |\n`path` | string | escaped json path |\n\n**Returns:** *any*\n\n___\n\n###  safeReference\n\n▸ **safeReference**<**IT**>(`subType`: IT, `options`: __type | [ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)‹IT› & object): *IReferenceType‹IT›*\n\n*Defined in [src/types/utility-types/reference.ts:545](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L545)*\n\n`types.safeReference` - A safe reference is like a standard reference, except that it accepts the undefined value by default\nand automatically sets itself to undefined (when the parent is a model) / removes itself from arrays and maps\nwhen the reference it is pointing to gets detached/destroyed.\n\nThe optional options parameter object accepts a parameter named `acceptsUndefined`, which is set to true by default, so it is suitable\nfor model properties.\nWhen used inside collections (arrays/maps), it is recommended to set this option to false so it can't take undefined as value,\nwhich is usually the desired in those cases.\nAdditionally, the optional options parameter object accepts a parameter named `onInvalidated`, which will be called when the reference target node that the reference is pointing to is about to be detached/destroyed\n\nStrictly speaking it is a `types.maybe(types.reference(X))` (when `acceptsUndefined` is set to true, the default) and\n`types.reference(X)` (when `acceptsUndefined` is set to false), both of them with a customized `onInvalidated` option.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subType` | IT |\n`options` | __type &#124; [ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)‹IT› & object |\n\n**Returns:** *IReferenceType‹IT›*\n\n▸ **safeReference**<**IT**>(`subType`: IT, `options?`: __type | [ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)‹IT› & object): *IMaybe‹IReferenceType‹IT››*\n\n*Defined in [src/types/utility-types/reference.ts:552](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L552)*\n\n`types.safeReference` - A safe reference is like a standard reference, except that it accepts the undefined value by default\nand automatically sets itself to undefined (when the parent is a model) / removes itself from arrays and maps\nwhen the reference it is pointing to gets detached/destroyed.\n\nThe optional options parameter object accepts a parameter named `acceptsUndefined`, which is set to true by default, so it is suitable\nfor model properties.\nWhen used inside collections (arrays/maps), it is recommended to set this option to false so it can't take undefined as value,\nwhich is usually the desired in those cases.\nAdditionally, the optional options parameter object accepts a parameter named `onInvalidated`, which will be called when the reference target node that the reference is pointing to is about to be detached/destroyed\n\nStrictly speaking it is a `types.maybe(types.reference(X))` (when `acceptsUndefined` is set to true, the default) and\n`types.reference(X)` (when `acceptsUndefined` is set to false), both of them with a customized `onInvalidated` option.\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyComplexType](interfaces/ianycomplextype.md)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`subType` | IT |\n`options?` | __type &#124; [ReferenceOptionsGetSet](interfaces/referenceoptionsgetset.md)‹IT› & object |\n\n**Returns:** *IMaybe‹IReferenceType‹IT››*\n\n___\n\n###  setLivelinessChecking\n\n▸ **setLivelinessChecking**(`mode`: [LivelinessMode](index.md#livelinessmode)): *void*\n\n*Defined in [src/core/node/livelinessChecking.ts:18](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/livelinessChecking.ts#L18)*\n\nDefines what MST should do when running into reads / writes to objects that have died.\nBy default it will print a warning.\nUse the `\"error\"` option to easy debugging to see where the error was thrown and when the offending read / write took place\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`mode` | [LivelinessMode](index.md#livelinessmode) | `\"warn\"`, `\"error\"` or `\"ignore\"`  |\n\n**Returns:** *void*\n\n___\n\n###  snapshotProcessor\n\n▸ **snapshotProcessor**<**IT**, **CustomC**, **CustomS**>(`type`: IT, `processors`: [ISnapshotProcessors](interfaces/isnapshotprocessors.md)‹IT, CustomC, CustomS›, `name?`: undefined | string): *[ISnapshotProcessor](interfaces/isnapshotprocessor.md)‹IT, CustomC, CustomS›*\n\n*Defined in [src/types/utility-types/snapshotProcessor.ts:271](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/snapshotProcessor.ts#L271)*\n\n`types.snapshotProcessor` - Runs a pre/post snapshot processor before/after serializing a given type.\n\n[See known issue with `applySnapshot` and `preProcessSnapshot`](https://github.com/mobxjs/mobx-state-tree/issues/1317)\n\nExample:\n```ts\nconst Todo1 = types.model({ text: types.string })\n// in the backend the text type must be null when empty\ninterface BackendTodo {\n    text: string | null\n}\n\nconst Todo2 = types.snapshotProcessor(Todo1, {\n    // from snapshot to instance\n    preProcessor(snapshot: BackendTodo) {\n        return {\n            text: sn.text || \"\";\n        }\n    },\n\n    // from instance to snapshot\n    postProcessor(snapshot, node): BackendTodo {\n        return {\n            text: !sn.text ? null : sn.text\n        }\n    }\n})\n```\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n▪ **CustomC**\n\n▪ **CustomS**\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`type` | IT | Type to run the processors over. |\n`processors` | [ISnapshotProcessors](interfaces/isnapshotprocessors.md)‹IT, CustomC, CustomS› | Processors to run. |\n`name?` | undefined &#124; string | Type name, or undefined to inherit the inner type one. |\n\n**Returns:** *[ISnapshotProcessor](interfaces/isnapshotprocessor.md)‹IT, CustomC, CustomS›*\n\n___\n\n###  splitJsonPath\n\n▸ **splitJsonPath**(`path`: string): *string[]*\n\n*Defined in [src/core/json-patch.ts:119](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L119)*\n\nSplits and decodes a json path into several parts.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`path` | string |\n\n**Returns:** *string[]*\n\n___\n\n###  toGenerator\n\n▸ **toGenerator**<**R**>(`p`: Promise‹R›): *Generator‹Promise‹R›, R, R›*\n\n*Defined in [src/core/flow.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/flow.ts#L87)*\n\n**`experimental`** \nexperimental api - might change on minor/patch releases\n\nConvert a promise to a generator yielding that promise\nThis is intended to allow for usage of `yield*` in async actions to\nretain the promise return type.\n\nExample:\n```ts\nfunction getDataAsync(input: string): Promise<number> { ... }\n\nconst someModel.actions(self => ({\n  someAction: flow(function*() {\n    // value is typed as number\n    const value = yield* toGenerator(getDataAsync(\"input value\"));\n    ...\n  })\n}))\n```\n\n**Type parameters:**\n\n▪ **R**\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`p` | Promise‹R› |\n\n**Returns:** *Generator‹Promise‹R›, R, R›*\n\n___\n\n###  toGeneratorFunction\n\n▸ **toGeneratorFunction**<**R**, **Args**>(`p`: function): *(Anonymous function)*\n\n*Defined in [src/core/flow.ts:60](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/flow.ts#L60)*\n\n**`experimental`** \nexperimental api - might change on minor/patch releases\n\nConvert a promise-returning function to a generator-returning one.\nThis is intended to allow for usage of `yield*` in async actions to\nretain the promise return type.\n\nExample:\n```ts\nfunction getDataAsync(input: string): Promise<number> { ... }\nconst getDataGen = toGeneratorFunction(getDataAsync);\n\nconst someModel.actions(self => ({\n  someAction: flow(function*() {\n    // value is typed as number\n    const value = yield* getDataGen(\"input value\");\n    ...\n  })\n}))\n```\n\n**Type parameters:**\n\n▪ **R**\n\n▪ **Args**: *any[]*\n\n**Parameters:**\n\n▪ **p**: *function*\n\n▸ (...`args`: Args): *Promise‹R›*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`...args` | Args |\n\n**Returns:** *(Anonymous function)*\n\n___\n\n###  tryReference\n\n▸ **tryReference**<**N**>(`getter`: function, `checkIfAlive`: boolean): *N | undefined*\n\n*Defined in [src/core/mst-operations.ts:564](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L564)*\n\nTests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if the check passes,\nelse it returns undefined.\n\n**Type parameters:**\n\n▪ **N**: *IAnyStateTreeNode*\n\n**Parameters:**\n\n▪ **getter**: *function*\n\nFunction to access the reference.\n\n▸ (): *N | null | undefined*\n\n▪`Default value`  **checkIfAlive**: *boolean*= true\n\ntrue to also make sure the referenced node is alive (default), false to skip this check.\n\n**Returns:** *N | undefined*\n\n___\n\n###  tryResolve\n\n▸ **tryResolve**(`target`: IAnyStateTreeNode, `path`: string): *any*\n\n*Defined in [src/core/mst-operations.ts:624](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L624)*\n\nTry to resolve a given path relative to a given node.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n`path` | string |\n\n**Returns:** *any*\n\n___\n\n###  typecheck\n\n▸ **typecheck**<**IT**>(`type`: IT, `value`: ExtractCSTWithSTN‹IT›): *void*\n\n*Defined in [src/core/type/type-checker.ts:164](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L164)*\n\nRun's the typechecker for the given type on the given value, which can be a snapshot or an instance.\nThrows if the given value is not according the provided type specification.\nUse this if you need typechecks even in a production build (by default all automatic runtime type checks will be skipped in production builds)\n\n**Type parameters:**\n\n▪ **IT**: *[IAnyType](interfaces/ianytype.md)*\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`type` | IT | Type to check against. |\n`value` | ExtractCSTWithSTN‹IT› | Value to be checked, either a snapshot or an instance.  |\n\n**Returns:** *void*\n\n___\n\n###  unescapeJsonPath\n\n▸ **unescapeJsonPath**(`path`: string): *string*\n\n*Defined in [src/core/json-patch.ts:89](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L89)*\n\nUnescape slashes and backslashes.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`path` | string |\n\n**Returns:** *string*\n\n___\n\n###  union\n\n▸ **union**<**Types**>(...`types`: Types): *[IUnionType](index.md#iuniontype)‹Types›*\n\n*Defined in [src/types/utility-types/union.ts:175](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L175)*\n\n`types.union` - Create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function of the form `(snapshot) => Type`.\n\n**Type parameters:**\n\n▪ **Types**: *[IAnyType](interfaces/ianytype.md)[]*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`...types` | Types |\n\n**Returns:** *[IUnionType](index.md#iuniontype)‹Types›*\n\n▸ **union**<**Types**>(`options`: [UnionOptions](interfaces/unionoptions.md)‹Types›, ...`types`: Types): *[IUnionType](index.md#iuniontype)‹Types›*\n\n*Defined in [src/types/utility-types/union.ts:176](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L176)*\n\n`types.union` - Create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function of the form `(snapshot) => Type`.\n\n**Type parameters:**\n\n▪ **Types**: *[IAnyType](interfaces/ianytype.md)[]*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`options` | [UnionOptions](interfaces/unionoptions.md)‹Types› |\n`...types` | Types |\n\n**Returns:** *[IUnionType](index.md#iuniontype)‹Types›*\n\n___\n\n###  unprotect\n\n▸ **unprotect**(`target`: IAnyStateTreeNode): *void*\n\n*Defined in [src/core/mst-operations.ts:298](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L298)*\n\nBy default it is not allowed to directly modify a model. Models can only be modified through actions.\nHowever, in some cases you don't care about the advantages (like replayability, traceability, etc) this yields.\nFor example because you are building a PoC or don't have any middleware attached to your tree.\n\nIn that case you can disable this protection by calling `unprotect` on the root of your tree.\n\nExample:\n```ts\nconst Todo = types.model({\n    done: false\n}).actions(self => ({\n    toggle() {\n        self.done = !self.done\n    }\n}))\n\nconst todo = Todo.create()\ntodo.done = true // throws!\ntodo.toggle() // OK\nunprotect(todo)\ntodo.done = false // OK\n```\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *void*\n\n___\n\n###  walk\n\n▸ **walk**(`target`: IAnyStateTreeNode, `processor`: function): *void*\n\n*Defined in [src/core/mst-operations.ts:808](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L808)*\n\nPerforms a depth first walk through a tree.\n\n**Parameters:**\n\n▪ **target**: *IAnyStateTreeNode*\n\n▪ **processor**: *function*\n\n▸ (`item`: IAnyStateTreeNode): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`item` | IAnyStateTreeNode |\n\n**Returns:** *void*\n\n## Object literals\n\n### `Const` types\n\n### ▪ **types**: *object*\n\n*Defined in [src/types/index.ts:34](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L34)*\n\n###  Date\n\n• **Date**: *[IType](interfaces/itype.md)‹number | Date, number, Date›* =  DatePrimitive\n\n*Defined in [src/types/index.ts:53](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L53)*\n\n###  array\n\n• **array**: *[array](index.md#array)*\n\n*Defined in [src/types/index.ts:55](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L55)*\n\n###  boolean\n\n• **boolean**: *[ISimpleType](interfaces/isimpletype.md)‹boolean›*\n\n*Defined in [src/types/index.ts:48](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L48)*\n\n###  compose\n\n• **compose**: *[compose](index.md#compose)*\n\n*Defined in [src/types/index.ts:37](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L37)*\n\n###  custom\n\n• **custom**: *[custom](index.md#custom)*\n\n*Defined in [src/types/index.ts:38](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L38)*\n\n###  enumeration\n\n• **enumeration**: *[enumeration](index.md#enumeration)*\n\n*Defined in [src/types/index.ts:35](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L35)*\n\n###  finite\n\n• **finite**: *[ISimpleType](interfaces/isimpletype.md)‹number›*\n\n*Defined in [src/types/index.ts:52](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L52)*\n\n###  float\n\n• **float**: *[ISimpleType](interfaces/isimpletype.md)‹number›*\n\n*Defined in [src/types/index.ts:51](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L51)*\n\n###  frozen\n\n• **frozen**: *[frozen](index.md#frozen)*\n\n*Defined in [src/types/index.ts:56](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L56)*\n\n###  identifier\n\n• **identifier**: *[ISimpleType](interfaces/isimpletype.md)‹string›*\n\n*Defined in [src/types/index.ts:57](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L57)*\n\n###  identifierNumber\n\n• **identifierNumber**: *[ISimpleType](interfaces/isimpletype.md)‹number›*\n\n*Defined in [src/types/index.ts:58](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L58)*\n\n###  integer\n\n• **integer**: *[ISimpleType](interfaces/isimpletype.md)‹number›*\n\n*Defined in [src/types/index.ts:50](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L50)*\n\n###  late\n\n• **late**: *[late](index.md#late)*\n\n*Defined in [src/types/index.ts:59](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L59)*\n\n###  lazy\n\n• **lazy**: *[lazy](index.md#lazy)*\n\n*Defined in [src/types/index.ts:60](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L60)*\n\n###  literal\n\n• **literal**: *[literal](index.md#literal)*\n\n*Defined in [src/types/index.ts:43](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L43)*\n\n###  map\n\n• **map**: *[map](index.md#map)*\n\n*Defined in [src/types/index.ts:54](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L54)*\n\n###  maybe\n\n• **maybe**: *[maybe](index.md#maybe)*\n\n*Defined in [src/types/index.ts:44](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L44)*\n\n###  maybeNull\n\n• **maybeNull**: *[maybeNull](index.md#maybenull)*\n\n*Defined in [src/types/index.ts:45](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L45)*\n\n###  model\n\n• **model**: *[model](index.md#model)*\n\n*Defined in [src/types/index.ts:36](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L36)*\n\n###  null\n\n• **null**: *[ISimpleType](interfaces/isimpletype.md)‹null›* =  nullType\n\n*Defined in [src/types/index.ts:62](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L62)*\n\n###  number\n\n• **number**: *[ISimpleType](interfaces/isimpletype.md)‹number›*\n\n*Defined in [src/types/index.ts:49](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L49)*\n\n###  optional\n\n• **optional**: *[optional](index.md#optional)*\n\n*Defined in [src/types/index.ts:42](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L42)*\n\n###  reference\n\n• **reference**: *[reference](index.md#reference)*\n\n*Defined in [src/types/index.ts:39](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L39)*\n\n###  refinement\n\n• **refinement**: *[refinement](index.md#refinement)*\n\n*Defined in [src/types/index.ts:46](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L46)*\n\n###  safeReference\n\n• **safeReference**: *[safeReference](index.md#safereference)*\n\n*Defined in [src/types/index.ts:40](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L40)*\n\n###  snapshotProcessor\n\n• **snapshotProcessor**: *[snapshotProcessor](index.md#snapshotprocessor)*\n\n*Defined in [src/types/index.ts:63](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L63)*\n\n###  string\n\n• **string**: *[ISimpleType](interfaces/isimpletype.md)‹string›*\n\n*Defined in [src/types/index.ts:47](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L47)*\n\n###  undefined\n\n• **undefined**: *[ISimpleType](interfaces/isimpletype.md)‹undefined›* =  undefinedType\n\n*Defined in [src/types/index.ts:61](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L61)*\n\n###  union\n\n• **union**: *[union](index.md#union)*\n\n*Defined in [src/types/index.ts:41](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/index.ts#L41)*\n"
  },
  {
    "path": "docs/API/interfaces/customtypeoptions.md",
    "content": "---\nid: \"customtypeoptions\"\ntitle: \"CustomTypeOptions\"\nsidebar_label: \"CustomTypeOptions\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [CustomTypeOptions](customtypeoptions.md)\n\n## Type parameters\n\n▪ **S**\n\n▪ **T**\n\n## Hierarchy\n\n* **CustomTypeOptions**\n\n## Index\n\n### Properties\n\n* [name](customtypeoptions.md#name)\n\n### Methods\n\n* [fromSnapshot](customtypeoptions.md#fromsnapshot)\n* [getValidationMessage](customtypeoptions.md#getvalidationmessage)\n* [isTargetType](customtypeoptions.md#istargettype)\n* [toSnapshot](customtypeoptions.md#tosnapshot)\n\n## Properties\n\n###  name\n\n• **name**: *string*\n\n*Defined in [src/types/utility-types/custom.ts:15](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/custom.ts#L15)*\n\nFriendly name\n\n## Methods\n\n###  fromSnapshot\n\n▸ **fromSnapshot**(`snapshot`: S, `env?`: any): *T*\n\n*Defined in [src/types/utility-types/custom.ts:17](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/custom.ts#L17)*\n\ngiven a serialized value and environment, how to turn it into the target type\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | S |\n`env?` | any |\n\n**Returns:** *T*\n\n___\n\n###  getValidationMessage\n\n▸ **getValidationMessage**(`snapshot`: S): *string*\n\n*Defined in [src/types/utility-types/custom.ts:23](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/custom.ts#L23)*\n\na non empty string is assumed to be a validation error\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | S |\n\n**Returns:** *string*\n\n___\n\n###  isTargetType\n\n▸ **isTargetType**(`value`: T | S): *boolean*\n\n*Defined in [src/types/utility-types/custom.ts:21](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/custom.ts#L21)*\n\nif true, this is a converted value, if false, it's a snapshot\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`value` | T &#124; S |\n\n**Returns:** *boolean*\n\n___\n\n###  toSnapshot\n\n▸ **toSnapshot**(`value`: T): *S*\n\n*Defined in [src/types/utility-types/custom.ts:19](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/custom.ts#L19)*\n\nreturn the serialization of the current value\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`value` | T |\n\n**Returns:** *S*\n"
  },
  {
    "path": "docs/API/interfaces/functionwithflag.md",
    "content": "---\nid: \"functionwithflag\"\ntitle: \"FunctionWithFlag\"\nsidebar_label: \"FunctionWithFlag\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [FunctionWithFlag](functionwithflag.md)\n\n## Hierarchy\n\n* Function\n\n  ↳ **FunctionWithFlag**\n\n## Index\n\n### Properties\n\n* [Function](functionwithflag.md#function)\n* [[Symbol.metadata]](functionwithflag.md#[symbol.metadata])\n* [_isFlowAction](functionwithflag.md#optional-_isflowaction)\n* [_isMSTAction](functionwithflag.md#optional-_ismstaction)\n* [arguments](functionwithflag.md#arguments)\n* [caller](functionwithflag.md#caller)\n* [length](functionwithflag.md#length)\n* [name](functionwithflag.md#name)\n* [prototype](functionwithflag.md#prototype)\n\n### Methods\n\n* [[Symbol.hasInstance]](functionwithflag.md#[symbol.hasinstance])\n* [apply](functionwithflag.md#apply)\n* [bind](functionwithflag.md#bind)\n* [call](functionwithflag.md#call)\n* [toString](functionwithflag.md#tostring)\n\n## Properties\n\n###  Function\n\n• **Function**: *FunctionConstructor*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:319\n\n___\n\n###  [Symbol.metadata]\n\n• **[Symbol.metadata]**: *DecoratorMetadata | null*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.esnext.decorators.d.ts:27\n\n___\n\n### `Optional` _isFlowAction\n\n• **_isFlowAction**? : *undefined | false | true*\n\n*Defined in [src/core/action.ts:42](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L42)*\n\n___\n\n### `Optional` _isMSTAction\n\n• **_isMSTAction**? : *undefined | false | true*\n\n*Defined in [src/core/action.ts:41](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L41)*\n\n___\n\n###  arguments\n\n• **arguments**: *any*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:305\n\n___\n\n###  caller\n\n• **caller**: *Function*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:306\n\n___\n\n###  length\n\n• **length**: *number*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:302\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es2015.core.d.ts:97\n\nReturns the name of the function. Function names are read-only and can not be changed.\n\n___\n\n###  prototype\n\n• **prototype**: *any*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:301\n\n## Methods\n\n###  [Symbol.hasInstance]\n\n▸ **[Symbol.hasInstance]**(`value`: any): *boolean*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts:164\n\nDetermines whether the given value inherits from this function if this function was used\nas a constructor function.\n\nA constructor function can control which objects are recognized as its instances by\n'instanceof' by overriding this method.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`value` | any |\n\n**Returns:** *boolean*\n\n___\n\n###  apply\n\n▸ **apply**(`this`: Function, `thisArg`: any, `argArray?`: any): *any*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:281\n\nCalls the function, substituting the specified object for the this value of the function, and the specified array for the arguments of the function.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`this` | Function | - |\n`thisArg` | any | The object to be used as the this object. |\n`argArray?` | any | A set of arguments to be passed to the function.  |\n\n**Returns:** *any*\n\n___\n\n###  bind\n\n▸ **bind**(`this`: Function, `thisArg`: any, ...`argArray`: any[]): *any*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:296\n\nFor a given function, creates a bound function that has the same body as the original function.\nThe this object of the bound function is associated with the specified object, and has the specified initial parameters.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`this` | Function | - |\n`thisArg` | any | An object to which the this keyword can refer inside the new function. |\n`...argArray` | any[] | A list of arguments to be passed to the new function.  |\n\n**Returns:** *any*\n\n___\n\n###  call\n\n▸ **call**(`this`: Function, `thisArg`: any, ...`argArray`: any[]): *any*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:288\n\nCalls a method of an object, substituting another object for the current object.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`this` | Function | - |\n`thisArg` | any | The object to be used as the current object. |\n`...argArray` | any[] | A list of arguments to be passed to the method.  |\n\n**Returns:** *any*\n\n___\n\n###  toString\n\n▸ **toString**(): *string*\n\n*Inherited from void*\n\nDefined in node_modules/typescript/lib/lib.es5.d.ts:299\n\nReturns a string representation of a function.\n\n**Returns:** *string*\n"
  },
  {
    "path": "docs/API/interfaces/iactioncontext.md",
    "content": "---\nid: \"iactioncontext\"\ntitle: \"IActionContext\"\nsidebar_label: \"IActionContext\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IActionContext](iactioncontext.md)\n\n## Hierarchy\n\n* **IActionContext**\n\n  ↳ [IMiddlewareEvent](imiddlewareevent.md)\n\n## Index\n\n### Properties\n\n* [args](iactioncontext.md#args)\n* [context](iactioncontext.md#context)\n* [id](iactioncontext.md#id)\n* [name](iactioncontext.md#name)\n* [parentActionEvent](iactioncontext.md#parentactionevent)\n* [tree](iactioncontext.md#tree)\n\n## Properties\n\n###  args\n\n• **args**: *any[]*\n\n*Defined in [src/core/actionContext.ts:20](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L20)*\n\nEvent arguments in an array (action arguments for actions)\n\n___\n\n###  context\n\n• **context**: *IAnyStateTreeNode*\n\n*Defined in [src/core/actionContext.ts:15](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L15)*\n\nEvent context (node where the action was invoked)\n\n___\n\n###  id\n\n• **id**: *number*\n\n*Defined in [src/core/actionContext.ts:9](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L9)*\n\nEvent unique id\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Defined in [src/core/actionContext.ts:6](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L6)*\n\nEvent name (action name for actions)\n\n___\n\n###  parentActionEvent\n\n• **parentActionEvent**: *[IMiddlewareEvent](imiddlewareevent.md) | undefined*\n\n*Defined in [src/core/actionContext.ts:12](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L12)*\n\nParent action event object\n\n___\n\n###  tree\n\n• **tree**: *IAnyStateTreeNode*\n\n*Defined in [src/core/actionContext.ts:17](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L17)*\n\nEvent tree (root node of the node where the action was invoked)\n"
  },
  {
    "path": "docs/API/interfaces/iactionrecorder.md",
    "content": "---\nid: \"iactionrecorder\"\ntitle: \"IActionRecorder\"\nsidebar_label: \"IActionRecorder\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IActionRecorder](iactionrecorder.md)\n\n## Hierarchy\n\n* **IActionRecorder**\n\n## Index\n\n### Properties\n\n* [actions](iactionrecorder.md#actions)\n* [recording](iactionrecorder.md#recording)\n\n### Methods\n\n* [replay](iactionrecorder.md#replay)\n* [resume](iactionrecorder.md#resume)\n* [stop](iactionrecorder.md#stop)\n\n## Properties\n\n###  actions\n\n• **actions**: *ReadonlyArray‹[ISerializedActionCall](iserializedactioncall.md)›*\n\n*Defined in [src/middlewares/on-action.ts:37](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L37)*\n\n___\n\n###  recording\n\n• **recording**: *boolean*\n\n*Defined in [src/middlewares/on-action.ts:38](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L38)*\n\n## Methods\n\n###  replay\n\n▸ **replay**(`target`: IAnyStateTreeNode): *void*\n\n*Defined in [src/middlewares/on-action.ts:41](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L41)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target` | IAnyStateTreeNode |\n\n**Returns:** *void*\n\n___\n\n###  resume\n\n▸ **resume**(): *void*\n\n*Defined in [src/middlewares/on-action.ts:40](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L40)*\n\n**Returns:** *void*\n\n___\n\n###  stop\n\n▸ **stop**(): *void*\n\n*Defined in [src/middlewares/on-action.ts:39](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L39)*\n\n**Returns:** *void*\n"
  },
  {
    "path": "docs/API/interfaces/iactiontrackingmiddleware2call.md",
    "content": "---\nid: \"iactiontrackingmiddleware2call\"\ntitle: \"IActionTrackingMiddleware2Call\"\nsidebar_label: \"IActionTrackingMiddleware2Call\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IActionTrackingMiddleware2Call](iactiontrackingmiddleware2call.md)\n\n## Type parameters\n\n▪ **TEnv**\n\n## Hierarchy\n\n* object\n\n  ↳ **IActionTrackingMiddleware2Call**\n\n## Index\n\n### Properties\n\n* [env](iactiontrackingmiddleware2call.md#env)\n* [parentCall](iactiontrackingmiddleware2call.md#optional-parentcall)\n\n## Properties\n\n###  env\n\n• **env**: *TEnv | undefined*\n\n*Defined in [src/middlewares/createActionTrackingMiddleware2.ts:4](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/createActionTrackingMiddleware2.ts#L4)*\n\n___\n\n### `Optional` parentCall\n\n• **parentCall**? : *[IActionTrackingMiddleware2Call](iactiontrackingmiddleware2call.md)‹TEnv›*\n\n*Defined in [src/middlewares/createActionTrackingMiddleware2.ts:5](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/createActionTrackingMiddleware2.ts#L5)*\n"
  },
  {
    "path": "docs/API/interfaces/iactiontrackingmiddleware2hooks.md",
    "content": "---\nid: \"iactiontrackingmiddleware2hooks\"\ntitle: \"IActionTrackingMiddleware2Hooks\"\nsidebar_label: \"IActionTrackingMiddleware2Hooks\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IActionTrackingMiddleware2Hooks](iactiontrackingmiddleware2hooks.md)\n\n## Type parameters\n\n▪ **TEnv**\n\n## Hierarchy\n\n* **IActionTrackingMiddleware2Hooks**\n\n## Index\n\n### Properties\n\n* [filter](iactiontrackingmiddleware2hooks.md#optional-filter)\n* [onFinish](iactiontrackingmiddleware2hooks.md#onfinish)\n* [onStart](iactiontrackingmiddleware2hooks.md#onstart)\n\n## Properties\n\n### `Optional` filter\n\n• **filter**? : *undefined | function*\n\n*Defined in [src/middlewares/createActionTrackingMiddleware2.ts:9](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/createActionTrackingMiddleware2.ts#L9)*\n\n___\n\n###  onFinish\n\n• **onFinish**: *function*\n\n*Defined in [src/middlewares/createActionTrackingMiddleware2.ts:11](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/createActionTrackingMiddleware2.ts#L11)*\n\n#### Type declaration:\n\n▸ (`call`: [IActionTrackingMiddleware2Call](iactiontrackingmiddleware2call.md)‹TEnv›, `error?`: any): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IActionTrackingMiddleware2Call](iactiontrackingmiddleware2call.md)‹TEnv› |\n`error?` | any |\n\n___\n\n###  onStart\n\n• **onStart**: *function*\n\n*Defined in [src/middlewares/createActionTrackingMiddleware2.ts:10](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/createActionTrackingMiddleware2.ts#L10)*\n\n#### Type declaration:\n\n▸ (`call`: [IActionTrackingMiddleware2Call](iactiontrackingmiddleware2call.md)‹TEnv›): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IActionTrackingMiddleware2Call](iactiontrackingmiddleware2call.md)‹TEnv› |\n"
  },
  {
    "path": "docs/API/interfaces/iactiontrackingmiddlewarehooks.md",
    "content": "---\nid: \"iactiontrackingmiddlewarehooks\"\ntitle: \"IActionTrackingMiddlewareHooks\"\nsidebar_label: \"IActionTrackingMiddlewareHooks\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IActionTrackingMiddlewareHooks](iactiontrackingmiddlewarehooks.md)\n\n## Type parameters\n\n▪ **T**\n\n## Hierarchy\n\n* **IActionTrackingMiddlewareHooks**\n\n## Index\n\n### Properties\n\n* [filter](iactiontrackingmiddlewarehooks.md#optional-filter)\n* [onFail](iactiontrackingmiddlewarehooks.md#onfail)\n* [onResume](iactiontrackingmiddlewarehooks.md#onresume)\n* [onStart](iactiontrackingmiddlewarehooks.md#onstart)\n* [onSuccess](iactiontrackingmiddlewarehooks.md#onsuccess)\n* [onSuspend](iactiontrackingmiddlewarehooks.md#onsuspend)\n\n## Properties\n\n### `Optional` filter\n\n• **filter**? : *undefined | function*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:6](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L6)*\n\n___\n\n###  onFail\n\n• **onFail**: *function*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:11](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L11)*\n\n#### Type declaration:\n\n▸ (`call`: [IMiddlewareEvent](imiddlewareevent.md), `context`: T, `error`: any): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IMiddlewareEvent](imiddlewareevent.md) |\n`context` | T |\n`error` | any |\n\n___\n\n###  onResume\n\n• **onResume**: *function*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:8](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L8)*\n\n#### Type declaration:\n\n▸ (`call`: [IMiddlewareEvent](imiddlewareevent.md), `context`: T): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IMiddlewareEvent](imiddlewareevent.md) |\n`context` | T |\n\n___\n\n###  onStart\n\n• **onStart**: *function*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:7](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L7)*\n\n#### Type declaration:\n\n▸ (`call`: [IMiddlewareEvent](imiddlewareevent.md)): *T*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IMiddlewareEvent](imiddlewareevent.md) |\n\n___\n\n###  onSuccess\n\n• **onSuccess**: *function*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:10](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L10)*\n\n#### Type declaration:\n\n▸ (`call`: [IMiddlewareEvent](imiddlewareevent.md), `context`: T, `result`: any): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IMiddlewareEvent](imiddlewareevent.md) |\n`context` | T |\n`result` | any |\n\n___\n\n###  onSuspend\n\n• **onSuspend**: *function*\n\n*Defined in [src/middlewares/create-action-tracking-middleware.ts:9](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/create-action-tracking-middleware.ts#L9)*\n\n#### Type declaration:\n\n▸ (`call`: [IMiddlewareEvent](imiddlewareevent.md), `context`: T): *void*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`call` | [IMiddlewareEvent](imiddlewareevent.md) |\n`context` | T |\n"
  },
  {
    "path": "docs/API/interfaces/ianycomplextype.md",
    "content": "---\nid: \"ianycomplextype\"\ntitle: \"IAnyComplexType\"\nsidebar_label: \"IAnyComplexType\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IAnyComplexType](ianycomplextype.md)\n\nAny kind of complex type.\n\n## Hierarchy\n\n* [IType](itype.md)‹any, any, object›\n\n  ↳ **IAnyComplexType**\n\n## Index\n\n### Properties\n\n* [identifierAttribute](ianycomplextype.md#optional-identifierattribute)\n* [name](ianycomplextype.md#name)\n\n### Methods\n\n* [create](ianycomplextype.md#create)\n* [describe](ianycomplextype.md#describe)\n* [is](ianycomplextype.md#is)\n* [validate](ianycomplextype.md#validate)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Inherited from [IType](itype.md).[identifierAttribute](itype.md#optional-identifierattribute)*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IType](itype.md).[name](itype.md#name)*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n## Methods\n\n###  create\n\n▸ **create**(`snapshot?`: any | ExcludeReadonly‹object›, `env?`: any): *this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[create](itype.md#create)*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | any &#124; ExcludeReadonly‹object› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Inherited from [IType](itype.md).[describe](itype.md#describe)*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is any | this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[is](itype.md#is)*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is any | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: any | object, `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Inherited from [IType](itype.md).[validate](itype.md#validate)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any &#124; object | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n"
  },
  {
    "path": "docs/API/interfaces/ianymodeltype.md",
    "content": "---\nid: \"ianymodeltype\"\ntitle: \"IAnyModelType\"\nsidebar_label: \"IAnyModelType\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IAnyModelType](ianymodeltype.md)\n\nAny model type.\n\n## Hierarchy\n\n  ↳ [IModelType](imodeltype.md)‹any, any, any, any›\n\n  ↳ **IAnyModelType**\n\n## Index\n\n### Properties\n\n* [identifierAttribute](ianymodeltype.md#optional-identifierattribute)\n* [name](ianymodeltype.md#name)\n* [properties](ianymodeltype.md#properties)\n\n### Methods\n\n* [actions](ianymodeltype.md#actions)\n* [create](ianymodeltype.md#create)\n* [describe](ianymodeltype.md#describe)\n* [extend](ianymodeltype.md#extend)\n* [is](ianymodeltype.md#is)\n* [named](ianymodeltype.md#named)\n* [postProcessSnapshot](ianymodeltype.md#postprocesssnapshot)\n* [preProcessSnapshot](ianymodeltype.md#preprocesssnapshot)\n* [props](ianymodeltype.md#props)\n* [validate](ianymodeltype.md#validate)\n* [views](ianymodeltype.md#views)\n* [volatile](ianymodeltype.md#volatile)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Inherited from [IType](itype.md).[identifierAttribute](itype.md#optional-identifierattribute)*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IType](itype.md).[name](itype.md#name)*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n___\n\n###  properties\n\n• **properties**: *any*\n\n*Inherited from [IModelType](imodeltype.md).[properties](imodeltype.md#properties)*\n\n*Defined in [src/types/complex-types/model.ts:195](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L195)*\n\n## Methods\n\n###  actions\n\n▸ **actions**<**A**>(`fn`: function): *[IModelType](imodeltype.md)‹any, any & A, any, any›*\n\n*Inherited from [IModelType](imodeltype.md).[actions](imodeltype.md#actions)*\n\n*Defined in [src/types/complex-types/model.ts:209](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L209)*\n\n**Type parameters:**\n\n▪ **A**: *ModelActions*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *A*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any & A, any, any›*\n\n___\n\n###  create\n\n▸ **create**(`snapshot?`: ModelCreationType2‹any, any› | ExcludeReadonly‹ModelInstanceType‹any, any››, `env?`: any): *this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[create](itype.md#create)*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | ModelCreationType2‹any, any› &#124; ExcludeReadonly‹ModelInstanceType‹any, any›› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Inherited from [IType](itype.md).[describe](itype.md#describe)*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  extend\n\n▸ **extend**<**A**, **V**, **VS**>(`fn`: function): *[IModelType](imodeltype.md)‹any, any & A & V & VS, any, any›*\n\n*Inherited from [IModelType](imodeltype.md).[extend](imodeltype.md#extend)*\n\n*Defined in [src/types/complex-types/model.ts:217](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L217)*\n\n**Type parameters:**\n\n▪ **A**: *ModelActions*\n\n▪ **V**: *Object*\n\n▪ **VS**: *Object*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *object*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any & A & V & VS, any, any›*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is ModelCreationType2<any, any> | this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[is](itype.md#is)*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is ModelCreationType2<any, any> | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  named\n\n▸ **named**(`newName`: string): *[IModelType](imodeltype.md)‹any, any, any, any›*\n\n*Inherited from [IModelType](imodeltype.md).[named](imodeltype.md#named)*\n\n*Defined in [src/types/complex-types/model.ts:197](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L197)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`newName` | string |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any, any, any›*\n\n___\n\n###  postProcessSnapshot\n\n▸ **postProcessSnapshot**<**NewS**>(`fn`: function): *[IModelType](imodeltype.md)‹any, any, any, NewS›*\n\n*Inherited from [IModelType](imodeltype.md).[postProcessSnapshot](imodeltype.md#postprocesssnapshot)*\n\n*Defined in [src/types/complex-types/model.ts:225](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L225)*\n\n**Type parameters:**\n\n▪ **NewS**\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`snapshot`: ModelSnapshotType2‹any, any›): *NewS*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | ModelSnapshotType2‹any, any› |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any, any, NewS›*\n\n___\n\n###  preProcessSnapshot\n\n▸ **preProcessSnapshot**<**NewC**>(`fn`: function): *[IModelType](imodeltype.md)‹any, any, NewC, any›*\n\n*Inherited from [IModelType](imodeltype.md).[preProcessSnapshot](imodeltype.md#preprocesssnapshot)*\n\n*Defined in [src/types/complex-types/model.ts:221](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L221)*\n\n**Type parameters:**\n\n▪ **NewC**\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`snapshot`: NewC): *WithAdditionalProperties‹ModelCreationType2‹any, any››*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | NewC |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any, NewC, any›*\n\n___\n\n###  props\n\n▸ **props**<**PROPS2**>(`props`: PROPS2): *[IModelType](imodeltype.md)‹any & ModelPropertiesDeclarationToProperties‹PROPS2›, any, any, any›*\n\n*Inherited from [IModelType](imodeltype.md).[props](imodeltype.md#props)*\n\n*Defined in [src/types/complex-types/model.ts:201](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L201)*\n\n**Type parameters:**\n\n▪ **PROPS2**: *ModelPropertiesDeclaration*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`props` | PROPS2 |\n\n**Returns:** *[IModelType](imodeltype.md)‹any & ModelPropertiesDeclarationToProperties‹PROPS2›, any, any, any›*\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: ModelCreationType2‹any, any› | ModelInstanceType‹any, any›, `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Inherited from [IType](itype.md).[validate](itype.md#validate)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | ModelCreationType2‹any, any› &#124; ModelInstanceType‹any, any› | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n\n___\n\n###  views\n\n▸ **views**<**V**>(`fn`: function): *[IModelType](imodeltype.md)‹any, any & V, any, any›*\n\n*Inherited from [IModelType](imodeltype.md).[views](imodeltype.md#views)*\n\n*Defined in [src/types/complex-types/model.ts:205](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L205)*\n\n**Type parameters:**\n\n▪ **V**: *Object*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *V*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any & V, any, any›*\n\n___\n\n###  volatile\n\n▸ **volatile**<**TP**>(`fn`: function): *[IModelType](imodeltype.md)‹any, any & TP, any, any›*\n\n*Inherited from [IModelType](imodeltype.md).[volatile](imodeltype.md#volatile)*\n\n*Defined in [src/types/complex-types/model.ts:213](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L213)*\n\n**Type parameters:**\n\n▪ **TP**: *object*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *TP*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹any, any & TP, any, any›*\n"
  },
  {
    "path": "docs/API/interfaces/ianytype.md",
    "content": "---\nid: \"ianytype\"\ntitle: \"IAnyType\"\nsidebar_label: \"IAnyType\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IAnyType](ianytype.md)\n\nAny kind of type.\n\n## Hierarchy\n\n* [IType](itype.md)‹any, any, any›\n\n  ↳ **IAnyType**\n\n## Index\n\n### Properties\n\n* [identifierAttribute](ianytype.md#optional-identifierattribute)\n* [name](ianytype.md#name)\n\n### Methods\n\n* [create](ianytype.md#create)\n* [describe](ianytype.md#describe)\n* [is](ianytype.md#is)\n* [validate](ianytype.md#validate)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Inherited from [IType](itype.md).[identifierAttribute](itype.md#optional-identifierattribute)*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IType](itype.md).[name](itype.md#name)*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n## Methods\n\n###  create\n\n▸ **create**(`snapshot?`: any | ExcludeReadonly‹any›, `env?`: any): *this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[create](itype.md#create)*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | any &#124; ExcludeReadonly‹any› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Inherited from [IType](itype.md).[describe](itype.md#describe)*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is any | this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[is](itype.md#is)*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is any | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: any | any, `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Inherited from [IType](itype.md).[validate](itype.md#validate)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any &#124; any | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n"
  },
  {
    "path": "docs/API/interfaces/ihooks.md",
    "content": "---\nid: \"ihooks\"\ntitle: \"IHooks\"\nsidebar_label: \"IHooks\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IHooks](ihooks.md)\n\n## Hierarchy\n\n* **IHooks**\n\n## Index\n\n### Properties\n\n* [[Hook.afterAttach]](ihooks.md#optional-[hook.afterattach])\n* [[Hook.afterCreate]](ihooks.md#optional-[hook.aftercreate])\n* [[Hook.beforeDestroy]](ihooks.md#optional-[hook.beforedestroy])\n* [[Hook.beforeDetach]](ihooks.md#optional-[hook.beforedetach])\n\n## Properties\n\n### `Optional` [Hook.afterAttach]\n\n• **[Hook.afterAttach]**? : *undefined | function*\n\n*Defined in [src/core/node/Hook.ts:14](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/Hook.ts#L14)*\n\n___\n\n### `Optional` [Hook.afterCreate]\n\n• **[Hook.afterCreate]**? : *undefined | function*\n\n*Defined in [src/core/node/Hook.ts:13](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/Hook.ts#L13)*\n\n___\n\n### `Optional` [Hook.beforeDestroy]\n\n• **[Hook.beforeDestroy]**? : *undefined | function*\n\n*Defined in [src/core/node/Hook.ts:16](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/Hook.ts#L16)*\n\n___\n\n### `Optional` [Hook.beforeDetach]\n\n• **[Hook.beforeDetach]**? : *undefined | function*\n\n*Defined in [src/core/node/Hook.ts:15](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/node/Hook.ts#L15)*\n"
  },
  {
    "path": "docs/API/interfaces/ijsonpatch.md",
    "content": "---\nid: \"ijsonpatch\"\ntitle: \"IJsonPatch\"\nsidebar_label: \"IJsonPatch\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IJsonPatch](ijsonpatch.md)\n\nhttps://tools.ietf.org/html/rfc6902\nhttp://jsonpatch.com/\n\n## Hierarchy\n\n* **IJsonPatch**\n\n  ↳ [IReversibleJsonPatch](ireversiblejsonpatch.md)\n\n## Index\n\n### Properties\n\n* [op](ijsonpatch.md#op)\n* [path](ijsonpatch.md#path)\n* [value](ijsonpatch.md#optional-value)\n\n## Properties\n\n###  op\n\n• **op**: *\"replace\" | \"add\" | \"remove\"*\n\n*Defined in [src/core/json-patch.ts:8](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L8)*\n\n___\n\n###  path\n\n• **path**: *string*\n\n*Defined in [src/core/json-patch.ts:9](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L9)*\n\n___\n\n### `Optional` value\n\n• **value**? : *any*\n\n*Defined in [src/core/json-patch.ts:10](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L10)*\n"
  },
  {
    "path": "docs/API/interfaces/imiddlewareevent.md",
    "content": "---\nid: \"imiddlewareevent\"\ntitle: \"IMiddlewareEvent\"\nsidebar_label: \"IMiddlewareEvent\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IMiddlewareEvent](imiddlewareevent.md)\n\n## Hierarchy\n\n* [IActionContext](iactioncontext.md)\n\n  ↳ **IMiddlewareEvent**\n\n## Index\n\n### Properties\n\n* [allParentIds](imiddlewareevent.md#allparentids)\n* [args](imiddlewareevent.md#args)\n* [context](imiddlewareevent.md#context)\n* [id](imiddlewareevent.md#id)\n* [name](imiddlewareevent.md#name)\n* [parentActionEvent](imiddlewareevent.md#parentactionevent)\n* [parentEvent](imiddlewareevent.md#parentevent)\n* [parentId](imiddlewareevent.md#parentid)\n* [rootId](imiddlewareevent.md#rootid)\n* [tree](imiddlewareevent.md#tree)\n* [type](imiddlewareevent.md#type)\n\n## Properties\n\n###  allParentIds\n\n• **allParentIds**: *number[]*\n\n*Defined in [src/core/action.ts:37](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L37)*\n\nId of all events, from root until current (excluding current)\n\n___\n\n###  args\n\n• **args**: *any[]*\n\n*Inherited from [IActionContext](iactioncontext.md).[args](iactioncontext.md#args)*\n\n*Defined in [src/core/actionContext.ts:20](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L20)*\n\nEvent arguments in an array (action arguments for actions)\n\n___\n\n###  context\n\n• **context**: *IAnyStateTreeNode*\n\n*Inherited from [IActionContext](iactioncontext.md).[context](iactioncontext.md#context)*\n\n*Defined in [src/core/actionContext.ts:15](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L15)*\n\nEvent context (node where the action was invoked)\n\n___\n\n###  id\n\n• **id**: *number*\n\n*Inherited from [IActionContext](iactioncontext.md).[id](iactioncontext.md#id)*\n\n*Defined in [src/core/actionContext.ts:9](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L9)*\n\nEvent unique id\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IActionContext](iactioncontext.md).[name](iactioncontext.md#name)*\n\n*Defined in [src/core/actionContext.ts:6](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L6)*\n\nEvent name (action name for actions)\n\n___\n\n###  parentActionEvent\n\n• **parentActionEvent**: *[IMiddlewareEvent](imiddlewareevent.md) | undefined*\n\n*Inherited from [IActionContext](iactioncontext.md).[parentActionEvent](iactioncontext.md#parentactionevent)*\n\n*Defined in [src/core/actionContext.ts:12](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L12)*\n\nParent action event object\n\n___\n\n###  parentEvent\n\n• **parentEvent**: *[IMiddlewareEvent](imiddlewareevent.md) | undefined*\n\n*Defined in [src/core/action.ts:32](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L32)*\n\nParent event object\n\n___\n\n###  parentId\n\n• **parentId**: *number*\n\n*Defined in [src/core/action.ts:30](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L30)*\n\nParent event unique id\n\n___\n\n###  rootId\n\n• **rootId**: *number*\n\n*Defined in [src/core/action.ts:35](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L35)*\n\nRoot event unique id\n\n___\n\n###  tree\n\n• **tree**: *IAnyStateTreeNode*\n\n*Inherited from [IActionContext](iactioncontext.md).[tree](iactioncontext.md#tree)*\n\n*Defined in [src/core/actionContext.ts:17](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/actionContext.ts#L17)*\n\nEvent tree (root node of the node where the action was invoked)\n\n___\n\n###  type\n\n• **type**: *[IMiddlewareEventType](../index.md#imiddlewareeventtype)*\n\n*Defined in [src/core/action.ts:27](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/action.ts#L27)*\n\nEvent type\n"
  },
  {
    "path": "docs/API/interfaces/imodelreflectiondata.md",
    "content": "---\nid: \"imodelreflectiondata\"\ntitle: \"IModelReflectionData\"\nsidebar_label: \"IModelReflectionData\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IModelReflectionData](imodelreflectiondata.md)\n\n## Hierarchy\n\n* [IModelReflectionPropertiesData](imodelreflectionpropertiesdata.md)\n\n  ↳ **IModelReflectionData**\n\n## Index\n\n### Properties\n\n* [actions](imodelreflectiondata.md#actions)\n* [flowActions](imodelreflectiondata.md#flowactions)\n* [name](imodelreflectiondata.md#name)\n* [properties](imodelreflectiondata.md#properties)\n* [views](imodelreflectiondata.md#views)\n* [volatile](imodelreflectiondata.md#volatile)\n\n## Properties\n\n###  actions\n\n• **actions**: *string[]*\n\n*Defined in [src/core/mst-operations.ts:855](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L855)*\n\n___\n\n###  flowActions\n\n• **flowActions**: *string[]*\n\n*Defined in [src/core/mst-operations.ts:858](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L858)*\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IModelReflectionPropertiesData](imodelreflectionpropertiesdata.md).[name](imodelreflectionpropertiesdata.md#name)*\n\n*Defined in [src/core/mst-operations.ts:825](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L825)*\n\n___\n\n###  properties\n\n• **properties**: *object*\n\n*Inherited from [IModelReflectionPropertiesData](imodelreflectionpropertiesdata.md).[properties](imodelreflectionpropertiesdata.md#properties)*\n\n*Defined in [src/core/mst-operations.ts:826](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L826)*\n\n#### Type declaration:\n\n* \\[ **K**: *string*\\]: [IAnyType](ianytype.md)\n\n___\n\n###  views\n\n• **views**: *string[]*\n\n*Defined in [src/core/mst-operations.ts:856](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L856)*\n\n___\n\n###  volatile\n\n• **volatile**: *string[]*\n\n*Defined in [src/core/mst-operations.ts:857](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L857)*\n"
  },
  {
    "path": "docs/API/interfaces/imodelreflectionpropertiesdata.md",
    "content": "---\nid: \"imodelreflectionpropertiesdata\"\ntitle: \"IModelReflectionPropertiesData\"\nsidebar_label: \"IModelReflectionPropertiesData\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IModelReflectionPropertiesData](imodelreflectionpropertiesdata.md)\n\n## Hierarchy\n\n* **IModelReflectionPropertiesData**\n\n  ↳ [IModelReflectionData](imodelreflectiondata.md)\n\n## Index\n\n### Properties\n\n* [name](imodelreflectionpropertiesdata.md#name)\n* [properties](imodelreflectionpropertiesdata.md#properties)\n\n## Properties\n\n###  name\n\n• **name**: *string*\n\n*Defined in [src/core/mst-operations.ts:825](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L825)*\n\n___\n\n###  properties\n\n• **properties**: *object*\n\n*Defined in [src/core/mst-operations.ts:826](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L826)*\n\n#### Type declaration:\n\n* \\[ **K**: *string*\\]: [IAnyType](ianytype.md)\n"
  },
  {
    "path": "docs/API/interfaces/imodeltype.md",
    "content": "---\nid: \"imodeltype\"\ntitle: \"IModelType\"\nsidebar_label: \"IModelType\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IModelType](imodeltype.md)\n\n## Type parameters\n\n▪ **PROPS**: *ModelProperties*\n\n▪ **OTHERS**\n\n▪ **CustomC**\n\n▪ **CustomS**\n\n## Hierarchy\n\n* [IType](itype.md)‹ModelCreationType2‹PROPS, CustomC›, ModelSnapshotType2‹PROPS, CustomS›, ModelInstanceType‹PROPS, OTHERS››\n\n  ↳ **IModelType**\n\n  ↳ [IAnyModelType](ianymodeltype.md)\n\n## Index\n\n### Properties\n\n* [identifierAttribute](imodeltype.md#optional-identifierattribute)\n* [name](imodeltype.md#name)\n* [properties](imodeltype.md#properties)\n\n### Methods\n\n* [actions](imodeltype.md#actions)\n* [create](imodeltype.md#create)\n* [describe](imodeltype.md#describe)\n* [extend](imodeltype.md#extend)\n* [is](imodeltype.md#is)\n* [named](imodeltype.md#named)\n* [postProcessSnapshot](imodeltype.md#postprocesssnapshot)\n* [preProcessSnapshot](imodeltype.md#preprocesssnapshot)\n* [props](imodeltype.md#props)\n* [validate](imodeltype.md#validate)\n* [views](imodeltype.md#views)\n* [volatile](imodeltype.md#volatile)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Inherited from [IType](itype.md).[identifierAttribute](itype.md#optional-identifierattribute)*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IType](itype.md).[name](itype.md#name)*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n___\n\n###  properties\n\n• **properties**: *PROPS*\n\n*Defined in [src/types/complex-types/model.ts:195](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L195)*\n\n## Methods\n\n###  actions\n\n▸ **actions**<**A**>(`fn`: function): *[IModelType](imodeltype.md)‹PROPS, OTHERS & A, CustomC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:209](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L209)*\n\n**Type parameters:**\n\n▪ **A**: *ModelActions*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *A*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS & A, CustomC, CustomS›*\n\n___\n\n###  create\n\n▸ **create**(`snapshot?`: ModelCreationType2‹PROPS, CustomC› | ExcludeReadonly‹ModelInstanceType‹PROPS, OTHERS››, `env?`: any): *this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[create](itype.md#create)*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | ModelCreationType2‹PROPS, CustomC› &#124; ExcludeReadonly‹ModelInstanceType‹PROPS, OTHERS›› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Inherited from [IType](itype.md).[describe](itype.md#describe)*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  extend\n\n▸ **extend**<**A**, **V**, **VS**>(`fn`: function): *[IModelType](imodeltype.md)‹PROPS, OTHERS & A & V & VS, CustomC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:217](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L217)*\n\n**Type parameters:**\n\n▪ **A**: *ModelActions*\n\n▪ **V**: *Object*\n\n▪ **VS**: *Object*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *object*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS & A & V & VS, CustomC, CustomS›*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is ModelCreationType2<PROPS, CustomC> | this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[is](itype.md#is)*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is ModelCreationType2<PROPS, CustomC> | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  named\n\n▸ **named**(`newName`: string): *[IModelType](imodeltype.md)‹PROPS, OTHERS, CustomC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:197](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L197)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`newName` | string |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS, CustomC, CustomS›*\n\n___\n\n###  postProcessSnapshot\n\n▸ **postProcessSnapshot**<**NewS**>(`fn`: function): *[IModelType](imodeltype.md)‹PROPS, OTHERS, CustomC, NewS›*\n\n*Defined in [src/types/complex-types/model.ts:225](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L225)*\n\n**Type parameters:**\n\n▪ **NewS**\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`snapshot`: ModelSnapshotType2‹PROPS, CustomS›): *NewS*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | ModelSnapshotType2‹PROPS, CustomS› |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS, CustomC, NewS›*\n\n___\n\n###  preProcessSnapshot\n\n▸ **preProcessSnapshot**<**NewC**>(`fn`: function): *[IModelType](imodeltype.md)‹PROPS, OTHERS, NewC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:221](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L221)*\n\n**Type parameters:**\n\n▪ **NewC**\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`snapshot`: NewC): *WithAdditionalProperties‹ModelCreationType2‹PROPS, CustomC››*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | NewC |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS, NewC, CustomS›*\n\n___\n\n###  props\n\n▸ **props**<**PROPS2**>(`props`: PROPS2): *[IModelType](imodeltype.md)‹PROPS & ModelPropertiesDeclarationToProperties‹PROPS2›, OTHERS, CustomC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:201](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L201)*\n\n**Type parameters:**\n\n▪ **PROPS2**: *ModelPropertiesDeclaration*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`props` | PROPS2 |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS & ModelPropertiesDeclarationToProperties‹PROPS2›, OTHERS, CustomC, CustomS›*\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: ModelCreationType2‹PROPS, CustomC› | ModelInstanceType‹PROPS, OTHERS›, `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Inherited from [IType](itype.md).[validate](itype.md#validate)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | ModelCreationType2‹PROPS, CustomC› &#124; ModelInstanceType‹PROPS, OTHERS› | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n\n___\n\n###  views\n\n▸ **views**<**V**>(`fn`: function): *[IModelType](imodeltype.md)‹PROPS, OTHERS & V, CustomC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:205](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L205)*\n\n**Type parameters:**\n\n▪ **V**: *Object*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *V*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS & V, CustomC, CustomS›*\n\n___\n\n###  volatile\n\n▸ **volatile**<**TP**>(`fn`: function): *[IModelType](imodeltype.md)‹PROPS, OTHERS & TP, CustomC, CustomS›*\n\n*Defined in [src/types/complex-types/model.ts:213](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/complex-types/model.ts#L213)*\n\n**Type parameters:**\n\n▪ **TP**: *object*\n\n**Parameters:**\n\n▪ **fn**: *function*\n\n▸ (`self`: [Instance](../index.md#instance)‹this›): *TP*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`self` | [Instance](../index.md#instance)‹this› |\n\n**Returns:** *[IModelType](imodeltype.md)‹PROPS, OTHERS & TP, CustomC, CustomS›*\n"
  },
  {
    "path": "docs/API/interfaces/ipatchrecorder.md",
    "content": "---\nid: \"ipatchrecorder\"\ntitle: \"IPatchRecorder\"\nsidebar_label: \"IPatchRecorder\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IPatchRecorder](ipatchrecorder.md)\n\n## Hierarchy\n\n* **IPatchRecorder**\n\n## Index\n\n### Properties\n\n* [inversePatches](ipatchrecorder.md#inversepatches)\n* [patches](ipatchrecorder.md#patches)\n* [recording](ipatchrecorder.md#recording)\n* [reversedInversePatches](ipatchrecorder.md#reversedinversepatches)\n\n### Methods\n\n* [replay](ipatchrecorder.md#replay)\n* [resume](ipatchrecorder.md#resume)\n* [stop](ipatchrecorder.md#stop)\n* [undo](ipatchrecorder.md#undo)\n\n## Properties\n\n###  inversePatches\n\n• **inversePatches**: *ReadonlyArray‹[IJsonPatch](ijsonpatch.md)›*\n\n*Defined in [src/core/mst-operations.ts:137](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L137)*\n\n___\n\n###  patches\n\n• **patches**: *ReadonlyArray‹[IJsonPatch](ijsonpatch.md)›*\n\n*Defined in [src/core/mst-operations.ts:136](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L136)*\n\n___\n\n###  recording\n\n• **recording**: *boolean*\n\n*Defined in [src/core/mst-operations.ts:139](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L139)*\n\n___\n\n###  reversedInversePatches\n\n• **reversedInversePatches**: *ReadonlyArray‹[IJsonPatch](ijsonpatch.md)›*\n\n*Defined in [src/core/mst-operations.ts:138](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L138)*\n\n## Methods\n\n###  replay\n\n▸ **replay**(`target?`: IAnyStateTreeNode): *void*\n\n*Defined in [src/core/mst-operations.ts:142](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L142)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target?` | IAnyStateTreeNode |\n\n**Returns:** *void*\n\n___\n\n###  resume\n\n▸ **resume**(): *void*\n\n*Defined in [src/core/mst-operations.ts:141](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L141)*\n\n**Returns:** *void*\n\n___\n\n###  stop\n\n▸ **stop**(): *void*\n\n*Defined in [src/core/mst-operations.ts:140](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L140)*\n\n**Returns:** *void*\n\n___\n\n###  undo\n\n▸ **undo**(`target?`: IAnyStateTreeNode): *void*\n\n*Defined in [src/core/mst-operations.ts:143](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/mst-operations.ts#L143)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`target?` | IAnyStateTreeNode |\n\n**Returns:** *void*\n"
  },
  {
    "path": "docs/API/interfaces/ireversiblejsonpatch.md",
    "content": "---\nid: \"ireversiblejsonpatch\"\ntitle: \"IReversibleJsonPatch\"\nsidebar_label: \"IReversibleJsonPatch\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IReversibleJsonPatch](ireversiblejsonpatch.md)\n\n## Hierarchy\n\n* [IJsonPatch](ijsonpatch.md)\n\n  ↳ **IReversibleJsonPatch**\n\n## Index\n\n### Properties\n\n* [oldValue](ireversiblejsonpatch.md#oldvalue)\n* [op](ireversiblejsonpatch.md#op)\n* [path](ireversiblejsonpatch.md#path)\n* [value](ireversiblejsonpatch.md#optional-value)\n\n## Properties\n\n###  oldValue\n\n• **oldValue**: *any*\n\n*Defined in [src/core/json-patch.ts:14](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L14)*\n\n___\n\n###  op\n\n• **op**: *\"replace\" | \"add\" | \"remove\"*\n\n*Inherited from [IJsonPatch](ijsonpatch.md).[op](ijsonpatch.md#op)*\n\n*Defined in [src/core/json-patch.ts:8](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L8)*\n\n___\n\n###  path\n\n• **path**: *string*\n\n*Inherited from [IJsonPatch](ijsonpatch.md).[path](ijsonpatch.md#path)*\n\n*Defined in [src/core/json-patch.ts:9](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L9)*\n\n___\n\n### `Optional` value\n\n• **value**? : *any*\n\n*Inherited from [IJsonPatch](ijsonpatch.md).[value](ijsonpatch.md#optional-value)*\n\n*Defined in [src/core/json-patch.ts:10](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/json-patch.ts#L10)*\n"
  },
  {
    "path": "docs/API/interfaces/iserializedactioncall.md",
    "content": "---\nid: \"iserializedactioncall\"\ntitle: \"ISerializedActionCall\"\nsidebar_label: \"ISerializedActionCall\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [ISerializedActionCall](iserializedactioncall.md)\n\n## Hierarchy\n\n* **ISerializedActionCall**\n\n## Index\n\n### Properties\n\n* [args](iserializedactioncall.md#optional-args)\n* [name](iserializedactioncall.md#name)\n* [path](iserializedactioncall.md#optional-path)\n\n## Properties\n\n### `Optional` args\n\n• **args**? : *any[]*\n\n*Defined in [src/middlewares/on-action.ts:33](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L33)*\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Defined in [src/middlewares/on-action.ts:31](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L31)*\n\n___\n\n### `Optional` path\n\n• **path**? : *undefined | string*\n\n*Defined in [src/middlewares/on-action.ts:32](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/middlewares/on-action.ts#L32)*\n"
  },
  {
    "path": "docs/API/interfaces/isimpletype.md",
    "content": "---\nid: \"isimpletype\"\ntitle: \"ISimpleType\"\nsidebar_label: \"ISimpleType\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [ISimpleType](isimpletype.md)\n\nA simple type, this is, a type where the instance and the snapshot representation are the same.\n\n## Type parameters\n\n▪ **T**\n\n## Hierarchy\n\n* [IType](itype.md)‹T, T, T›\n\n  ↳ **ISimpleType**\n\n## Index\n\n### Properties\n\n* [identifierAttribute](isimpletype.md#optional-identifierattribute)\n* [name](isimpletype.md#name)\n\n### Methods\n\n* [create](isimpletype.md#create)\n* [describe](isimpletype.md#describe)\n* [is](isimpletype.md#is)\n* [validate](isimpletype.md#validate)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Inherited from [IType](itype.md).[identifierAttribute](itype.md#optional-identifierattribute)*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IType](itype.md).[name](itype.md#name)*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n## Methods\n\n###  create\n\n▸ **create**(`snapshot?`: T | ExcludeReadonly‹T›, `env?`: any): *this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[create](itype.md#create)*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | T &#124; ExcludeReadonly‹T› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Inherited from [IType](itype.md).[describe](itype.md#describe)*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is T | this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[is](itype.md#is)*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is T | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: T | T, `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Inherited from [IType](itype.md).[validate](itype.md#validate)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | T &#124; T | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n"
  },
  {
    "path": "docs/API/interfaces/isnapshotprocessor.md",
    "content": "---\nid: \"isnapshotprocessor\"\ntitle: \"ISnapshotProcessor\"\nsidebar_label: \"ISnapshotProcessor\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [ISnapshotProcessor](isnapshotprocessor.md)\n\nA type that has its snapshots processed.\n\n## Type parameters\n\n▪ **IT**: *[IAnyType](ianytype.md)*\n\n▪ **CustomC**\n\n▪ **CustomS**\n\n## Hierarchy\n\n* [IType](itype.md)‹_CustomOrOther‹CustomC, IT[\"CreationType\"]›, _CustomOrOther‹CustomS, IT[\"SnapshotType\"]›, IT[\"TypeWithoutSTN\"]›\n\n  ↳ **ISnapshotProcessor**\n\n## Index\n\n### Properties\n\n* [identifierAttribute](isnapshotprocessor.md#optional-identifierattribute)\n* [name](isnapshotprocessor.md#name)\n\n### Methods\n\n* [create](isnapshotprocessor.md#create)\n* [describe](isnapshotprocessor.md#describe)\n* [is](isnapshotprocessor.md#is)\n* [validate](isnapshotprocessor.md#validate)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Inherited from [IType](itype.md).[identifierAttribute](itype.md#optional-identifierattribute)*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Inherited from [IType](itype.md).[name](itype.md#name)*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n## Methods\n\n###  create\n\n▸ **create**(`snapshot?`: _CustomOrOther‹CustomC, IT[\"CreationType\"]› | ExcludeReadonly‹IT[\"TypeWithoutSTN\"]›, `env?`: any): *this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[create](itype.md#create)*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | _CustomOrOther‹CustomC, IT[\"CreationType\"]› &#124; ExcludeReadonly‹IT[\"TypeWithoutSTN\"]› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Inherited from [IType](itype.md).[describe](itype.md#describe)*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is _CustomOrOther<CustomC, IT[\"CreationType\"]> | this[\"Type\"]*\n\n*Inherited from [IType](itype.md).[is](itype.md#is)*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is _CustomOrOther<CustomC, IT[\"CreationType\"]> | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: _CustomOrOther‹CustomC, IT[\"CreationType\"]› | IT[\"TypeWithoutSTN\"], `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Inherited from [IType](itype.md).[validate](itype.md#validate)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | _CustomOrOther‹CustomC, IT[\"CreationType\"]› &#124; IT[\"TypeWithoutSTN\"] | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n"
  },
  {
    "path": "docs/API/interfaces/isnapshotprocessors.md",
    "content": "---\nid: \"isnapshotprocessors\"\ntitle: \"ISnapshotProcessors\"\nsidebar_label: \"ISnapshotProcessors\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [ISnapshotProcessors](isnapshotprocessors.md)\n\nSnapshot processors.\n\n## Type parameters\n\n▪ **IT**: *[IAnyType](ianytype.md)*\n\n▪ **CustomC**\n\n▪ **CustomS**\n\n## Hierarchy\n\n* **ISnapshotProcessors**\n\n## Index\n\n### Methods\n\n* [postProcessor](isnapshotprocessors.md#optional-postprocessor)\n* [preProcessor](isnapshotprocessors.md#optional-preprocessor)\n\n## Methods\n\n### `Optional` postProcessor\n\n▸ **postProcessor**(`snapshot`: IT[\"SnapshotType\"], `node`: [Instance](../index.md#instance)‹IT›): *_CustomOrOther‹CustomS, IT[\"SnapshotType\"]›*\n\n*Defined in [src/types/utility-types/snapshotProcessor.ts:230](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/snapshotProcessor.ts#L230)*\n\nFunction that transforms an output snapshot.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`snapshot` | IT[\"SnapshotType\"] |   |\n`node` | [Instance](../index.md#instance)‹IT› | - |\n\n**Returns:** *_CustomOrOther‹CustomS, IT[\"SnapshotType\"]›*\n\n___\n\n### `Optional` preProcessor\n\n▸ **preProcessor**(`snapshot`: _CustomOrOther‹CustomC, IT[\"CreationType\"]›): *IT[\"CreationType\"]*\n\n*Defined in [src/types/utility-types/snapshotProcessor.ts:224](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/snapshotProcessor.ts#L224)*\n\nFunction that transforms an input snapshot.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot` | _CustomOrOther‹CustomC, IT[\"CreationType\"]› |\n\n**Returns:** *IT[\"CreationType\"]*\n"
  },
  {
    "path": "docs/API/interfaces/itype.md",
    "content": "---\nid: \"itype\"\ntitle: \"IType\"\nsidebar_label: \"IType\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IType](itype.md)\n\nA type, either complex or simple.\n\n## Type parameters\n\n▪ **C**\n\n▪ **S**\n\n▪ **T**\n\n## Hierarchy\n\n* **IType**\n\n  ↳ [IAnyType](ianytype.md)\n\n  ↳ [ISimpleType](isimpletype.md)\n\n  ↳ [IAnyComplexType](ianycomplextype.md)\n\n  ↳ [ISnapshotProcessor](isnapshotprocessor.md)\n\n  ↳ [IModelType](imodeltype.md)\n\n## Index\n\n### Properties\n\n* [identifierAttribute](itype.md#optional-identifierattribute)\n* [name](itype.md#name)\n\n### Methods\n\n* [create](itype.md#create)\n* [describe](itype.md#describe)\n* [is](itype.md#is)\n* [validate](itype.md#validate)\n\n## Properties\n\n### `Optional` identifierAttribute\n\n• **identifierAttribute**? : *undefined | string*\n\n*Defined in [src/core/type/type.ts:92](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L92)*\n\nName of the identifier attribute or null if none.\n\n___\n\n###  name\n\n• **name**: *string*\n\n*Defined in [src/core/type/type.ts:87](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L87)*\n\nFriendly type name.\n\n## Methods\n\n###  create\n\n▸ **create**(`snapshot?`: C | ExcludeReadonly‹T›, `env?`: any): *this[\"Type\"]*\n\n*Defined in [src/core/type/type.ts:99](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L99)*\n\nCreates an instance for the type given an snapshot input.\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`snapshot?` | C &#124; ExcludeReadonly‹T› |\n`env?` | any |\n\n**Returns:** *this[\"Type\"]*\n\nAn instance of that type.\n\n___\n\n###  describe\n\n▸ **describe**(): *string*\n\n*Defined in [src/core/type/type.ts:121](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L121)*\n\nGets the textual representation of the type as a string.\n\n**Returns:** *string*\n\n___\n\n###  is\n\n▸ **is**(`thing`: any): *thing is C | this[\"Type\"]*\n\n*Defined in [src/core/type/type.ts:107](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L107)*\n\nChecks if a given snapshot / instance is of the given type.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | any | Snapshot or instance to be checked. |\n\n**Returns:** *thing is C | this[\"Type\"]*\n\ntrue if the value is of the current type, false otherwise.\n\n___\n\n###  validate\n\n▸ **validate**(`thing`: C | T, `context`: [IValidationContext](../index.md#ivalidationcontext)): *[IValidationResult](../index.md#ivalidationresult)*\n\n*Defined in [src/core/type/type.ts:116](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type.ts#L116)*\n\nRun's the type's typechecker on the given value with the given validation context.\n\n**Parameters:**\n\nName | Type | Description |\n------ | ------ | ------ |\n`thing` | C &#124; T | Value to be checked, either a snapshot or an instance. |\n`context` | [IValidationContext](../index.md#ivalidationcontext) | Validation context, an array of { subpaths, subtypes } that should be validated |\n\n**Returns:** *[IValidationResult](../index.md#ivalidationresult)*\n\nThe validation result, an array with the list of validation errors.\n"
  },
  {
    "path": "docs/API/interfaces/ivalidationcontextentry.md",
    "content": "---\nid: \"ivalidationcontextentry\"\ntitle: \"IValidationContextEntry\"\nsidebar_label: \"IValidationContextEntry\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IValidationContextEntry](ivalidationcontextentry.md)\n\nValidation context entry, this is, where the validation should run against which type\n\n## Hierarchy\n\n* **IValidationContextEntry**\n\n## Index\n\n### Properties\n\n* [path](ivalidationcontextentry.md#path)\n* [type](ivalidationcontextentry.md#type)\n\n## Properties\n\n###  path\n\n• **path**: *string*\n\n*Defined in [src/core/type/type-checker.ts:17](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L17)*\n\nSubpath where the validation should be run, or an empty string to validate it all\n\n___\n\n###  type\n\n• **type**: *[IAnyType](ianytype.md)*\n\n*Defined in [src/core/type/type-checker.ts:19](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L19)*\n\nType to validate the subpath against\n"
  },
  {
    "path": "docs/API/interfaces/ivalidationerror.md",
    "content": "---\nid: \"ivalidationerror\"\ntitle: \"IValidationError\"\nsidebar_label: \"IValidationError\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [IValidationError](ivalidationerror.md)\n\nType validation error\n\n## Hierarchy\n\n* **IValidationError**\n\n## Index\n\n### Properties\n\n* [context](ivalidationerror.md#context)\n* [message](ivalidationerror.md#optional-message)\n* [value](ivalidationerror.md#value)\n\n## Properties\n\n###  context\n\n• **context**: *[IValidationContext](../index.md#ivalidationcontext)*\n\n*Defined in [src/core/type/type-checker.ts:28](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L28)*\n\nValidation context\n\n___\n\n### `Optional` message\n\n• **message**? : *undefined | string*\n\n*Defined in [src/core/type/type-checker.ts:32](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L32)*\n\nError message\n\n___\n\n###  value\n\n• **value**: *any*\n\n*Defined in [src/core/type/type-checker.ts:30](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/core/type/type-checker.ts#L30)*\n\nValue that was being validated, either a snapshot or an instance\n"
  },
  {
    "path": "docs/API/interfaces/referenceoptionsgetset.md",
    "content": "---\nid: \"referenceoptionsgetset\"\ntitle: \"ReferenceOptionsGetSet\"\nsidebar_label: \"ReferenceOptionsGetSet\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [ReferenceOptionsGetSet](referenceoptionsgetset.md)\n\n## Type parameters\n\n▪ **IT**: *[IAnyComplexType](ianycomplextype.md)*\n\n## Hierarchy\n\n* **ReferenceOptionsGetSet**\n\n## Index\n\n### Methods\n\n* [get](referenceoptionsgetset.md#get)\n* [set](referenceoptionsgetset.md#set)\n\n## Methods\n\n###  get\n\n▸ **get**(`identifier`: [ReferenceIdentifier](../index.md#referenceidentifier), `parent`: IAnyStateTreeNode | null): *ReferenceT‹IT›*\n\n*Defined in [src/types/utility-types/reference.ts:472](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L472)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`identifier` | [ReferenceIdentifier](../index.md#referenceidentifier) |\n`parent` | IAnyStateTreeNode &#124; null |\n\n**Returns:** *ReferenceT‹IT›*\n\n___\n\n###  set\n\n▸ **set**(`value`: ReferenceT‹IT›, `parent`: IAnyStateTreeNode | null): *[ReferenceIdentifier](../index.md#referenceidentifier)*\n\n*Defined in [src/types/utility-types/reference.ts:473](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L473)*\n\n**Parameters:**\n\nName | Type |\n------ | ------ |\n`value` | ReferenceT‹IT› |\n`parent` | IAnyStateTreeNode &#124; null |\n\n**Returns:** *[ReferenceIdentifier](../index.md#referenceidentifier)*\n"
  },
  {
    "path": "docs/API/interfaces/referenceoptionsoninvalidated.md",
    "content": "---\nid: \"referenceoptionsoninvalidated\"\ntitle: \"ReferenceOptionsOnInvalidated\"\nsidebar_label: \"ReferenceOptionsOnInvalidated\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [ReferenceOptionsOnInvalidated](referenceoptionsoninvalidated.md)\n\n## Type parameters\n\n▪ **IT**: *[IAnyComplexType](ianycomplextype.md)*\n\n## Hierarchy\n\n* **ReferenceOptionsOnInvalidated**\n\n## Index\n\n### Properties\n\n* [onInvalidated](referenceoptionsoninvalidated.md#oninvalidated)\n\n## Properties\n\n###  onInvalidated\n\n• **onInvalidated**: *[OnReferenceInvalidated](../index.md#onreferenceinvalidated)‹ReferenceT‹IT››*\n\n*Defined in [src/types/utility-types/reference.ts:478](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/reference.ts#L478)*\n"
  },
  {
    "path": "docs/API/interfaces/unionoptions.md",
    "content": "---\nid: \"unionoptions\"\ntitle: \"UnionOptions\"\nsidebar_label: \"UnionOptions\"\n---\n\n[mobx-state-tree - v7.0.2](../index.md) › [UnionOptions](unionoptions.md)\n\n## Type parameters\n\n▪ **Types**: *[IAnyType](ianytype.md)[]*\n\n## Hierarchy\n\n* **UnionOptions**\n\n## Index\n\n### Properties\n\n* [dispatcher](unionoptions.md#optional-dispatcher)\n* [eager](unionoptions.md#optional-eager)\n\n## Properties\n\n### `Optional` dispatcher\n\n• **dispatcher**? : *[ITypeDispatcher](../index.md#itypedispatcher)‹Types›*\n\n*Defined in [src/types/utility-types/union.ts:38](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L38)*\n\nA function that returns the type to be used given an input snapshot.\n\n___\n\n### `Optional` eager\n\n• **eager**? : *undefined | false | true*\n\n*Defined in [src/types/utility-types/union.ts:33](https://github.com/mobxjs/mobx-state-tree/blob/1be40a3e/src/types/utility-types/union.ts#L33)*\n\nWhether or not to use eager validation.\n\nWhen `true`, the first matching type will be used. Otherwise, all types will be checked and the\nvalidation will pass if and only if a single type matches.\n"
  },
  {
    "path": "docs/API_header.md",
    "content": "# Mobx-State-Tree API reference guide\n\n_This reference guide lists all methods exposed by MST. Contributions like linguistic improvements, adding more details to the descriptions or additional examples are highly appreciated! Please note that the docs are generated from source. Most methods are declared in the [mst-operations.ts](https://github.com/mobxjs/mobx-state-tree/blob/master/packages/mobx-state-tree/src/core/mst-operations.ts) file._\n"
  },
  {
    "path": "docs/compare/context-reducer-vs-mobx-state-tree.md",
    "content": "---\nid: context-reducer-vs-mobx-state-tree\ntitle: React Context vs. MobX-State-Tree\n---\n\nIf you're using React, you have the option to manage application state with built in hooks, like [`useContext`](https://react.dev/reference/react/useContext) and [`useReducer`](https://react.dev/reference/react/useReducer). The React docs have [an example showing how to combine these two hooks to manage more complex state](https://react.dev/learn/scaling-up-with-reducer-and-context).\n\nReact built-ins are a great choice if you're opposed to adding dependencies to your project, or if you want to write flexible JavaScript code with your own set of conventions.\n\nMobX-State-Tree can provide you with the same features as React's built-in state management hooks, but with the added benefits of:\n\n- Better performance out of the box due to MST's reactive, observable state.\n- Automatic TypeScript inference of your state, which makes your code easier to write (with auto-completions) and harder to break (with static analysis of TypeScript).\n- Runtime type safety for your state, which also helps keep your application bug free as your codebase and team grows.\n- Clearer data modeling with our rich runtime type system as opposed to writing plain JS objects.\n- Built-in immutability with [snapshots](../concepts/snapshots.md). This makes it easy to build common requirements like \"undo/redo\", time travel debugging, or synchronizing with external systems\n- Easy persistence with utilities like [mst-persist](https://www.npmjs.com/package/mst-persist)\n\n## React Context/Reducer Code Review\n\nIf you haven't worked with complex contexts and reducers in React, you should read through [their guide on advanced usage](https://react.dev/learn/scaling-up-with-reducer-and-context). It will help you make a fair assessment between React state hooks and MobX-State-Tree.\n\n[Here is the CodeSandbox of their final product in that article](https://codesandbox.io/p/sandbox/react-dev-wy7lfd?file=%2Fsrc%2FTasksContext.js%3A54%2C4&utm_medium=sandpack).\n\n[And here is the same set of features, built with MobX-State-Tree instead of Context/Reducers](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-8824l8?file=%2Fsrc%2FViewModel.ts%3A15%2C24.).\n\nLet's focus on comparing just the state-management code in React's `src/TasksContext.js`, and MST's `src/ViewModel.ts`. To start, we'll compare code, and then we'll move on to feature comparisons.\n\n```js\n// React context/reducer in `src/TasksContext.js`\n// https://codesandbox.io/p/sandbox/react-dev-wy7lfd?file=%2Fsrc%2FTasksContext.js%3A43%2C17&utm_medium=sandpack\nimport { createContext, useContext, useReducer } from \"react\"\n\nconst TasksContext = createContext(null)\n\nconst TasksDispatchContext = createContext(null)\n\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks)\n\n  return (\n    <TasksContext.Provider value={tasks}>\n      <TasksDispatchContext.Provider value={dispatch}>{children}</TasksDispatchContext.Provider>\n    </TasksContext.Provider>\n  )\n}\n\nexport function useTasks() {\n  return useContext(TasksContext)\n}\n\nexport function useTasksDispatch() {\n  return useContext(TasksDispatchContext)\n}\n\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case \"added\": {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false\n        }\n      ]\n    }\n    case \"changed\": {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task\n        } else {\n          return t\n        }\n      })\n    }\n    case \"deleted\": {\n      return tasks.filter((t) => t.id !== action.id)\n    }\n    default: {\n      throw Error(\"Unknown action: \" + action.type)\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: \"Philosopher’s Path\", done: true },\n  { id: 1, text: \"Visit the temple\", done: false },\n  { id: 2, text: \"Drink matcha\", done: false }\n]\n```\n\n### React Code is Tightly Coupled\n\nThe Context/Reducer code is, understandably, very coupled to React. It exports JSX directly:\n\n```js\nexport function TasksProvider({ children }) {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks)\n\n  return (\n    <TasksContext.Provider value={tasks}>\n      <TasksDispatchContext.Provider value={dispatch}>{children}</TasksDispatchContext.Provider>\n    </TasksContext.Provider>\n  )\n}\n```\n\nIt also mixes concerns. Note how in `TasksProvider`, the reducer, initial tasks, and dispatch value have to come together with the UI code to become useful. It's not entirely clear from a top-to-bottom glance where the source of truth for state is.\n\n### Reducer Functions Lack Convention\n\nCheck out the reducer function:\n\n```js\nfunction tasksReducer(tasks, action) {\n  switch (action.type) {\n    case \"added\": {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false\n        }\n      ]\n    }\n    case \"changed\": {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task\n        } else {\n          return t\n        }\n      })\n    }\n    case \"deleted\": {\n      return tasks.filter((t) => t.id !== action.id)\n    }\n    default: {\n      throw Error(\"Unknown action: \" + action.type)\n    }\n  }\n}\n```\n\nWith three actions, this feels somewhat manageable. But what if your state mutations are more numerous or more complex? Of course you can split those out into other files, but then your codebase gets fragmented, and it's becomes more difficult to reason about it overtime.\n\nMoreover, the `action` argument is opaque. What types are valid? What other data will come along with it? You could write these out in TypeScript and define valid shapes, but that's more work and boilerplate for you.\n\n### Unclear Initial State in Context/Reducer\n\nThe reducer/context example provides `initialTasks`, like this:\n\n```js\nconst initialTasks = [\n  { id: 0, text: \"Philosopher’s Path\", done: true },\n  { id: 1, text: \"Visit the temple\", done: false },\n  { id: 2, text: \"Drink matcha\", done: false }\n]\n```\n\nBut those are just the initial tasks. If you followed the React tutorial, you might be wondering:\n\n1. How do we know if an item is being edited?\n2. Where are we storing `nextId`?\n\nTurns out, the item being edited is managed as local state with `useState` in [`src/TaskList.js`](https://codesandbox.io/p/sandbox/react-dev-wy7lfd?file=%2Fsrc%2FTaskList.js%3A15%2C2&utm_medium=sandpack):\n\n```js\n// ...\nfunction Task({ task }) {\n  const [isEditing, setIsEditing] = useState(false);\n  const dispatch = useTasksDispatch();\n  let taskContent;\n  if (isEditing) {\n    taskContent = (\n      <>\n        <input\n          value={task.text}\n          onChange={e => {\n            dispatch({\n              type: 'changed',\n              task: {\n                ...task,\n                text: e.target.value\n              }\n            });\n          }} />\n        <button onClick={() => setIsEditing(false)}>\n          Save\n        </button>\n      </>\n    );\n  } else {\n    taskContent = (\n      <>\n        {task.text}\n        <button onClick={() => setIsEditing(true)}>\n          Edit\n        </button>\n      </>\n    );\n  }\n// ...\n```\n\nWe use an auto-incrementing number for IDs. In the React example, this is stored and initialized in [`src/AddTask.js`](https://codesandbox.io/p/sandbox/react-dev-wy7lfd?file=%2Fsrc%2FAddTask.js%3A27%2C1&utm_medium=sandpack):\n\n```js\n// At the bottom of `src/AddTask.js`:\nlet nextId = 3\n```\n\n## MobX-State-Tree Code Review\n\n```ts\n// MST's viewmodel in `src/ViewModel.ts`.\n// https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-8824l8?file=%2Fsrc%2FViewModel.ts%3A88%2C1\nimport { t, Instance } from \"mobx-state-tree\"\n\nconst Task = t\n  .model(\"Task\", {\n    id: t.identifierNumber,\n    text: t.string,\n    done: t.optional(t.boolean, false),\n    isBeingEdited: t.optional(t.boolean, false)\n  })\n  .actions((self) => ({\n    setText(text: string) {\n      self.text = text\n    },\n    setDone(done: boolean) {\n      self.done = done\n    },\n    setIsBeingEdited(beingEdited: boolean) {\n      self.isBeingEdited = beingEdited\n    }\n  }))\n\nexport interface ITask extends Instance<typeof Task> {}\n\nconst ViewModel = t\n  .model(\"ViewModel\", {\n    taskInputText: \"\",\n    nextId: 0,\n    tasks: t.array(Task)\n  })\n  .actions((self) => ({\n    addTask() {\n      const { nextId, taskInputText } = self\n\n      if (!taskInputText) {\n        return\n      }\n\n      const newTask = Task.create({\n        id: nextId,\n        text: taskInputText\n      })\n\n      self.tasks.push(newTask)\n\n      self.nextId += 1\n\n      self.taskInputText = \"\"\n    },\n    deleteTask(id: number) {\n      const task = self.tasks.find((t) => t.id === id)\n      if (task) {\n        self.tasks.remove(task)\n      }\n    },\n    setInputText(text: string) {\n      self.taskInputText = text\n    }\n  }))\n\nexport const ViewModelSingleton = ViewModel.create({\n  nextId: 3,\n  tasks: [\n    { id: 0, text: \"Philosopher’s Path\", done: true },\n    { id: 1, text: \"Visit the temple\", done: false },\n    { id: 2, text: \"Drink matcha\", done: false }\n  ]\n})\n```\n\n### MobX-State-Tree Decouples State from UI\n\nThe MobX-State-Tree code doesn't really \"know\" anything about React (or Vue, or Angular, or Solid, or Svelte, or any other library you might be using). It is Just TypeScript. Which means it does not suffer from the [coupling problems of React state built-ins](#react-code-is-tightly-coupled). We can't really fault React tools for being coupled to React, but using MST will provide you with more flexibility to change your UI code, and even your entire UI library if you ever choose to.\n\n### Conventional State Change with Actions\n\nThe `.actions` block in our MST code replaces the React reducer. Rather than managing our actions with dispatches and a switch statement, we can write state mutations as regular TypeScript functions. Each aciton gets its own set of parameters. You can call those actions like regular functions, rather than \"dispatching\" the action boilerplate. This is the code we're talking about:\n\n```ts\n  .actions((self) => ({\n    addTask() {\n      const { nextId, taskInputText } = self;\n\n      if (!taskInputText) {\n        return;\n      }\n\n      const newTask = Task.create({\n        id: nextId,\n        text: taskInputText,\n      });\n\n      self.tasks.push(newTask);\n\n      self.nextId += 1;\n\n      self.taskInputText = \"\";\n    },\n    deleteTask(id: number) {\n      const task = self.tasks.find((t) => t.id === id);\n      if (task) {\n        self.tasks.remove(task);\n      }\n    },\n    setInputText(text: string) {\n      self.taskInputText = text;\n    },\n  }));\n```\n\nIf you want to add a task, you'd call:\n\n```ts\nViewModelSingleton.addtask()\n```\n\nAnd we'd create a task based on the current state of the `taskInputText`. State would update, and the UI would respond to the granular updates. Simple and lovely to work with!\n\n### MST is a Single Source of Truth for State\n\nIt's easier to clarify initial state in MobX-State-Tree. In our example, we provide it much like the [initial state in Context](#unclear-initial-state-in-contextreducer):\n\n```ts\nexport const ViewModelSingleton = ViewModel.create({\n  nextId: 3,\n  tasks: [\n    { id: 0, text: \"Philosopher’s Path\", done: true },\n    { id: 1, text: \"Visit the temple\", done: false },\n    { id: 2, text: \"Drink matcha\", done: false }\n  ]\n})\n```\n\nThis code is creating a new instance of a ViewModel, and it's providing it with all of the initial state we need. If we gave an invalid initial state, MobX-State-Tree would warn us:\n\n```ts\nexport const ViewModelSingleton = ViewModel.create({\n  nextId: \"3\", // In this example, we're using numbers for IDs, not strings. TS will error.\n  tasks: [\n    { id: 0, text: \"Philosopher’s Path\", done: true },\n    { id: 1, text: \"Visit the temple\", done: false },\n    { id: 2, text: \"Drink matcha\", done: false }\n  ]\n})\n```\n\nIf we use the wrong kind of value for our `nextId`, we'll get a TypeScript error like:\n\n```\nType 'string' is not assignable to type 'number'.typescript(2322)\n```\n\nEven if you're not using TypeScript, MST will let you know about it in the runtime:\n\n```\n[mobx-state-tree] Error while converting `{\"nextId\":\"3\",\"tasks\":[{\"id\":0,\"text\":\"Philosopher’s Path\",\"done\":true},{\"id\":1,\"text\":\"Visit the temple\",\"done\":false},{\"id\":2,\"text\":\"Drink matcha\",\"done\":false}]}` to `ViewModel`: at path \"/nextId\" value `\"3\"` is not assignable to type: `number` (Value is not a number).\n```\n\nIf you want to avoid even this much initial code, you can initialize the ViewModel with no tasks. Since we wrote the `nextId` value as a literal, MST will assume it's optional, and the provided value is the default. So this code:\n\n```ts\nconst ViewModel = t.model(\"ViewModel\", {\n  taskInputText: t.maybe(t.string),\n  nextId: 0,\n  tasks: t.array(Task)\n})\n```\n\nAllows us to write:\n\n```ts\nexport const ViewModelSingleton = ViewModel.create({})\n```\n\nIt _also_ keeps all of this state in one central place. We can read the file top-to-bottom and understand the entirety of our state at a glance.\n\n## React Context/Reducer Rendering Performance\n\nImagine you want to use React Context in a large React application with many layers of nesting. As a simple demonstration, consider what happens if we wrap our code in some `MiddleComponent`:\n\n```js\n// src/MiddleComponent.js\nexport default function MiddleComponent(props) {\n  const { children } = props;\n  console.log(\"MiddleComponent evaluated\");\n\n  return <div>{children}</div>;\n}\n\n// src/App.js\n\nimport AddTask from \"./AddTask.js\";\nimport TaskList from \"./TaskList.js\";\nimport MiddleComponent from \"./MiddleComponent.js\";\nimport { TasksProvider } from \"./TasksContext.js\";\n\nexport default function TaskApp() {\n  return (\n    <TasksProvider>\n      <MiddleComponent>\n        <h1>Day off in Kyoto</h1>\n        <AddTask />\n        <TaskList />\n      </MiddleComponent>\n    </TasksProvider>\n  );\n}\n```\n\n[Play around with this in CodeSandbox and pay attention to the console](https://codesandbox.io/p/sandbox/react-dev-reducer-context-with-middle-component-cjgg72?file=%2Fsrc%2FMiddleComponent.js%3A11%2C1). Add some to-dos, delete some, check some off. You'll see this output in the console:\n\n```\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\nMiddleComponent evaluated\n```\n\nAnd so on, for as many times as you change the values in the context provider.\n\n### React Makes You Manage Optimization Yourself\n\nYou can improve this in React with memoization:\n\n```jsx\n// src/MiddleComponent.js\nimport React from \"react\"\n\nconst MiddleComponent = React.memo(function MiddleComponent(props) {\n  const { children } = props\n  console.log(\"MiddleComponent evaluated\")\n\n  return <div>{children}</div>\n})\n\nexport default MiddleComponent\n```\n\nOr you can split context into many sub-contexts and provide them to children more granularly.\n\nBut all that said, _you_ still have to manage this complexity in some way. This is advantageous if you and your team are adept at performance work, and want to have fine-grained control of the primitive building blocks provided by React. But many teams lack the expertise, time, or interest in managing this themselves. MobX-State-Tree solves this performance issue for you by default.\n\n## MobX-State-Tree Performance\n\nGiven a similar component and setup:\n\n```tsx\n// src/MiddleComponent.tsx\nimport React from \"react\"\n\nexport default function MiddleComponent(props) {\n  const { children } = props\n  console.log(\"MiddleComponent evaluated\")\n\n  return <div>{children}</div>\n}\n\n// src/App.tsx\nimport AddTask from \"./AddTask\"\nimport TaskList from \"./TaskList\"\nimport MiddleComponent from \"./MiddleComponent\"\n\nexport default function TaskApp() {\n  return (\n    <>\n      <MiddleComponent>\n        <h1>Day off in Kyoto</h1>\n        <AddTask />\n        <TaskList />\n      </MiddleComponent>\n    </>\n  )\n}\n```\n\n### Handles Granular Updates Automatically\n\n[Try the same set of actions in CodeSandbox](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-with-middle-component-y2h558?file=%2Fsrc%2FMiddleComponent.tsx%3A9%2C1), and you'll see that the `MiddleComponent` does _not_ get re-evaluated. MobX-State-Tree does this for you with its [observer higher-order-component](../intro/getting-started#getting-to-the-ui), which [only re-renders components when their observed data changes](../intro/getting-started#improving-render-performance).\n\n## Automatic TypeScript Types with MobX-State-Tree\n\nSo far we've been comparing React's [JavaScript only example](https://codesandbox.io/p/sandbox/react-dev-wy7lfd?file=%2Fsrc%2Findex.js%3A28%2C30&utm_medium=sandpack) against a MobX-State-Tree example [written in TypeScript](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-8824l8?file=%2Fsrc%2FViewModel.ts%3A11%2C22).\n\nThe TypeScript story for MobX-State-Tree is very straightforward. In [`src/ViewModel.ts`](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-8824l8?file=%2Fsrc%2FViewModel.ts%3A69%2C20) you can write `ViewModelSingleton.` and get auto-complete for all its properties and actions.\n\nIf you want to do more with these types, we [have a recommended set of type helpers](../tips/typescript#using-a-mst-type-at-design-time). In our example, you can see we use:\n\n```ts\nexport interface ITask extends Instance<typeof Task> {}\n```\n\nTo tell the [Task component](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-8824l8?file=%2Fsrc%2FTaskList.tsx%3A27%2C19) what to expect in its props.\n\nYou don't have to make any choices about the TypeScript design. Model out your state with MST, and we'll give you an opinionated set of TypeScript types back. You trade off control for quicker development overall, much like the performance management tradeoffs. If you want good, sensible defaults for rapid development, choose MobX-State-Tree.\n\n## Write Your Own Types for React Context/Reducer\n\nIf you want to use React Context/Reducer with TypeScript, you'll need to specify your types from the ground up. Many teams might like this approach, but it does require you to take the time to do so. Here's one way you might type the context:\n\n```tsx\nimport React, { createContext, useContext, useReducer, ReactNode, Dispatch, JSX } from \"react\"\n\nexport interface Task {\n  id: number\n  text: string\n  done: boolean\n}\n\ntype Action =\n  | { type: \"added\"; id: number; text: string }\n  | { type: \"changed\"; task: Task }\n  | { type: \"deleted\"; id: number }\n\nconst TasksContext = createContext<Task[] | null>(null)\nconst TasksDispatchContext = createContext<Dispatch<Action> | null>(null)\n\nexport function TasksProvider({ children }: { children: ReactNode }): JSX.Element {\n  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks)\n\n  return (\n    <TasksContext.Provider value={tasks}>\n      <TasksDispatchContext.Provider value={dispatch}>{children}</TasksDispatchContext.Provider>\n    </TasksContext.Provider>\n  )\n}\n\nexport function useTasks(): Task[] {\n  const context = useContext(TasksContext)\n  if (!context) {\n    throw new Error(\"useTasks must be used within a TasksProvider\")\n  }\n  return context\n}\n\nexport function useTasksDispatch(): Dispatch<Action> {\n  const context = useContext(TasksDispatchContext)\n  if (!context) {\n    throw new Error(\"useTasksDispatch must be used within a TasksProvider\")\n  }\n  return context\n}\n\nfunction tasksReducer(tasks: Task[], action: Action): Task[] {\n  switch (action.type) {\n    case \"added\": {\n      return [\n        ...tasks,\n        {\n          id: action.id,\n          text: action.text,\n          done: false\n        }\n      ]\n    }\n    case \"changed\": {\n      return tasks.map((t) => {\n        if (t.id === action.task.id) {\n          return action.task\n        } else {\n          return t\n        }\n      })\n    }\n    case \"deleted\": {\n      return tasks.filter((t) => t.id !== action.id)\n    }\n    default: {\n      throw Error(\"Unknown action: \" + action)\n    }\n  }\n}\n\nconst initialTasks = [\n  { id: 0, text: \"Philosopher’s Path\", done: true },\n  { id: 1, text: \"Visit the temple\", done: false },\n  { id: 2, text: \"Drink matcha\", done: false }\n]\n```\n\n[See the whole example converted to TypeScript in CodeSandbox](https://codesandbox.io/p/sandbox/react-dev-context-reducer-example-with-typescript-l8ym3t?file=%2Fsrc%2FTasksContext.tsx%3A91%2C1)\n\n## Context/Reducer Cannot Guarantee Type Safety at Runtime\n\nIn the React Context/Reducer example, you are required to understand the kinds of initial data that satisfy your requirements. You must remember how to write them, and write them consistently. The example provides initial tasks like this:\n\n```js\nconst initialTasks = [\n  { id: 0, text: \"Philosopher’s Path\", done: true },\n  { id: 1, text: \"Visit the temple\", done: false },\n  { id: 2, text: \"Drink matcha\", done: false }\n]\n```\n\nBut if you write an invalid task, React won't stop you:\n\n```js\nconst initialTasks = [\n  { id: 0, text: \"Philosopher’s Path\", done: true },\n  { id: 1, text: \"Visit the temple\", done: false },\n  { id: 2, text: \"Drink matcha\", done: false },\n  {\n    something: \"else\",\n    works: false,\n    id: () => {\n      console.log(\"here\")\n    }\n  }\n]\n```\n\nIn fact, React _almost_ does the right thing here. If you check out the [CodeSandbox with this incorrect data](https://codesandbox.io/p/sandbox/react-dev-context-reducer-with-incorrect-task-data-nqw67d?file=%2Fsrc%2FTasksContext.js%3A56%2C1), you'll see that a fourth item shows up. You can even edit/delete/check it off. The React components themselves are pretty resilient.\n\nBut if you check off the task, or edit its name, you'll get a warning in the console:\n\n```\nWarning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components\n```\n\nThis is because we've left `text` and `done` to be `undefined`, and then the reducer modifies those values.\n\nIn the small, toy React example, this isn't a huge deal. But this kind of unexpected behavior can lead to serious bugs in a larger application.\n\n## MobX-State-Tree Provides Runtime Type Safety by Default\n\n[Open up the MST example in CodeSandbox](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-8824l8?file=%2Fsrc%2FViewModel.ts%3A15%2C24.) and change the ViewModel instantiation to be:\n\n```ts\nexport const ViewModelSingleton = ViewModel.create({\n  nextId: 3,\n  tasks: [\n    { id: 0, text: \"Philosopher’s Path\", done: true },\n    { id: 1, text: \"Visit the temple\", done: false },\n    { id: 2, text: \"Drink matcha\", done: false },\n    {\n      something: \"else\",\n      works: false,\n      id: () => {\n        console.log(\"here\")\n      }\n    }\n  ]\n})\n```\n\nYou'll _immediately receive an error from MobX-State-Tree_:\n\n```\n[mobx-state-tree] Error while converting `{\"nextId\":3,\"tasks\":[{\"id\":0,\"text\":\"Philosopher’s Path\",\"done\":true},{\"id\":1,\"text\":\"Visit the temple\",\"done\":false},{\"id\":2,\"text\":\"Drink matcha\",\"done\":false},{\"something\":\"else\",\"works\":false}]}` to `ViewModel`:\n\n    at path \"/tasks/3/id\" snapshot <function id> is not assignable to type: `identifierNumber` (Value is not a valid identifierNumber, expected a number), expected an instance of `identifierNumber` or a snapshot like `identifierNumber` instead.\n    at path \"/tasks/3/text\" value `undefined` is not assignable to type: `string` (Value is not a string).\n```\n\nThis error will both prevent you from making costly mistakes in the future, and it even attempts to give you information about _precisely what's wrong_, which makes debugging things easier.\n\n_(Note: by default, MST will not run this check in production mode for performance reasons)_\n\n## MobX-State-Tree Gives you Building Blocks for Advanced Data Modeling\n\nIn the Reducer/Context example, we arbitrarily decide that a task looks like this:\n\n```js\n{ id: 0, text: \"Philosopher’s Path\", done: true },\n```\n\nWith TypeScript, we can annotate the types of these objects. But if you're building a complex app, you may want to enforce your data modeling beyond conventions and static types.\n\nIn MobX-State-Tree, we turned that object syntax into a model itself:\n\n```ts\nconst Task = t\n  .model(\"Task\", {\n    id: t.identifierNumber,\n    text: t.string,\n    done: t.optional(t.boolean, false),\n    isBeingEdited: t.optional(t.boolean, false)\n  })\n  .actions((self) => ({\n    setText(text: string) {\n      self.text = text\n    },\n    setDone(done: boolean) {\n      self.done = done\n    },\n    setIsBeingEdited(beingEdited: boolean) {\n      self.isBeingEdited = beingEdited\n    }\n  }))\n```\n\nNow our program understands that a `Task` is a real entity with a well-defined set of properties, and well-defined actions it can take at runtime. This is a clearer way to communicate your intention to other programmers, and to enforce rules for your data modeling in your application.\n\nThere are [many different types](../overview/types.md) you can extend and build with to provide this same kind of structure and safety to your application at all levels. This is another tradeoff: MST primitives and models have rules that plain JavaScript objects do not. But if you learn those rules, you can improve your developer experience, and more rigorously model your application state for your future self and the rest of your team to work with correctly.\n\n## React Context/Reducer Needs Custom Code for Time Travel Debugging\n\n[Time travel debugging](https://medium.com/the-web-tub/time-travel-in-react-redux-apps-using-the-redux-devtools-5e94eba5e7c0) is a popular tool used to observe how application state changes over time, and diagnose any errors or inaccuracies. The idea is to keep a record of the state and its mutations over time, and then play it back through some dev tooling or observability that understands how to represent the state.\n\nBuilding this kind of functionality is possible with Reducers and Context, but you have to build it yourself, from the ground up.\n\n## MobX-State-Tree Has Built-in Time Travel Primitives\n\nMobX-State-Tree generates [snapshots](../concepts/snapshots.md), which are immutable, serialized versions of the state at each point it gets changes. You can listen to the snapshots with the `onSnapshot` listener, like this:\n\n```ts\nconst initialSnapshot = JSON.stringify(getSnapshot(ViewModelSingleton))\nconst timeTravel: string[] = [initialSnapshot]\nonSnapshot(ViewModelSingleton, (snapshot) => {\n  timeTravel.push(JSON.stringify(snapshot))\n})\n```\n\nIn this code, we take an inital snapshot of the `ViewModelSingleton`, and then store each subsequent snapshot. You can play around with this in [CodeSandbox](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-snapshots-qvr529?file=%2Fsrc%2FViewModel.ts%3A54%2C31). Open up the console, and store the `timeTravel` variable as a global variable. Log it out after you make some changes, and you'll see a series of snapshots.\n\nSnapshots like this make time travel debugging easy to implement, with very little custom code. It also makes it easy to do things like persistence, re-hydrating state from the server, and other operations where serialized state can be deserialized into something more useful. The following section is a great example of this.\n\n## Persist State Easily with mst-persist\n\nSince MobX-State-Tree state is always serializable and we have utilities like snapshot listeners, libraries like [mst-persist](https://www.npmjs.com/package/mst-persist) are readily available.\n\nWith one import and one line of code, we can persist our application state to localStorage:\n\n```\nimport { persist } from \"mst-persist\";\n\npersist(\"ViewModelSingleton\", ViewModelSingleton)\n```\n\n[Open this CodeSandbox example](https://codesandbox.io/p/sandbox/mobx-state-tree-instead-of-reducer-and-context-persistence-hjmrzg?file=%2Fsrc%2FViewModel.ts%3A70%2C59), make some changes, and then reload it. You'll see your changes have persisted.\n\nReact Context can also be persisted to localStorage, but again, it requires you to write the logic from the ground up. If you need this kind of functionality in a large project, the MST community has already taken care of it for you, and we have conventions and maintainers behind the code, so you're never really on your own.\n\n## MST is State Management on Easy Mode\n\nAt this point, we hope the benefits of MobX-State-Tree are clear. If you have a complex application, or if your application is going to become complex over time, MST offers a pre-built set of tools and conventions that will allow you to focus on building features and solving user problems, rather than reinventing the wheel for your state management system.\n\nThere are many more MST-specific utilities available, like [data normalization](../concepts/references.md), [JSON patches](../concepts/patches.md), [middleware](../concepts/middleware.md), and libraries like [mst-query](https://github.com/ConrabOpto/mst-query) and [mst-gql](https://github.com/mobxjs/mst-gql) to help you manage asynchronous state. Much like the prior examples in this article, using these tools will save you a lot of work building and maintaining your own bespoke solutions.\n\nIf you've been working with React Reducer and Context, MobX-State-Tree will feel like easy-mode for state management. On top of that, you'll join a [welcoming, active community](https://github.com/mobxjs/mobx-state-tree/discussions) where we can help you with state modeling questions, and any learning curve you experience while getting used to MobX-State-Tree.\n\nQuestions? Comments? [Let us know in the forum](https://github.com/mobxjs/mobx-state-tree/discussions)\n"
  },
  {
    "path": "docs/concepts/actions.md",
    "content": "---\nid: actions\ntitle: Actions\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 2: Attach Behavior to mobx-state-tree Models Using Actions</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-attach-behavior-to-mobx-state-tree-models-using-actions/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-attach-behavior-to-mobx-state-tree-models-using-actions\">Hosted on egghead.io</a>\n</details>\n\nBy default, nodes can only be modified by one of their actions, or by actions higher up in the tree.\nActions can be defined by returning an object from the action initializer function that was passed to `actions`.\nThe initializer function is executed for each instance, so that `self` is always bound to the current instance.\nAlso, the closure of that function can be used to store so called _volatile_ state for the instance or to create private functions that can only\nbe invoked from the actions, but not from the outside.\n\n```javascript\nconst Todo = types\n    .model({\n        title: types.string\n    })\n    .actions(self => {\n        function setTitle(newTitle) {\n            self.title = newTitle\n        }\n\n        return {\n            setTitle\n        }\n    })\n```\n\nShorter form if no local state or private functions are involved:\n\n```javascript\nconst Todo = types\n    .model({\n        title: types.string\n    })\n    .actions(self => ({\n        // note the `({`, we are returning an object literal\n        setTitle(newTitle) {\n            self.title = newTitle\n        }\n    }))\n```\n\nActions are replayable and are therefore constrained in several ways:\n\n-   Trying to modify a node without using an action will throw an exception.\n-   It's recommended to make sure action arguments are serializable. Some arguments can be serialized automatically such as relative paths to other nodes\n-   Actions can only modify models that belong to the (sub)tree on which they are invoked\n-   You cannot use `this` inside actions. Instead, use `self`. This makes it safe to pass actions around without binding them or wrapping them in arrow functions.\n\nUseful methods:\n\n-   [`onAction`](/API/#onaction) listens to any action that is invoked on the model or any of its descendants.\n-   [`addMiddleware`](/API/#addmiddleware) adds an interceptor function to any action invoked on the subtree.\n-   [`applyAction`](/API/#applyaction) invokes an action on the model according to the given action description\n\n\n#### Action listeners versus middleware\n\nThe difference between action listeners and middleware is: middleware can intercept the action that is about to be invoked, modify arguments, return types, etc. Action listeners cannot intercept and are only notified. Action listeners receive the action arguments in a serializable format, while middleware receives the raw arguments. (`onAction` is actually just a built-in middleware).\n\nFor more details on creating middleware, see the [docs](/concepts/middleware).\n\n#### Disabling protected mode\n\nThis may be desired if the default protection of `mobx-state-tree` doesn't fit your use case. For example, if you are not interested in replayable actions or hate the effort of writing actions to modify any field, `unprotect(tree)` will disable the protected mode of a tree allowing anyone to directly modify the tree.\n"
  },
  {
    "path": "docs/concepts/async-actions.md",
    "content": "---\nid: async-actions\ntitle: Asynchronous actions\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 12: Defining Asynchronous Processes Using Flow</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-defining-asynchronous-processes-using-flow/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-defining-asynchronous-processes-using-flow\">Hosted on egghead.io</a>\n</details>\n\nThe recommended way to write asynchronous actions is by using `flow` and generators. They always return a promise, and work for all practical purposes the same as async / await. For a real working example see the [bookshop sources](https://github.com/mobxjs/mobx-state-tree/blob/adba1943af263898678fe148a80d3d2b9f8dbe63/examples/bookshop/src/stores/BookStore.js#L25). A detailed break-down is made below, but a quick example to get the gist:\n\n_Warning: don't import `flow` from `\"mobx\"`, but from `\"mobx-state-tree\"` instead!_\n\n```javascript\nimport { types, flow } from \"mobx-state-tree\"\n\nsomeModel.actions((self) => {\n    const fetchProjects = flow(function* () {\n        // <- note the star, this is a generator function!\n        self.state = \"pending\"\n        try {\n            // ... yield can be used in async/await style\n            self.githubProjects = yield fetchGithubProjectsSomehow()\n            self.state = \"done\"\n        } catch (error) {\n            // ... including try/catch error handling\n            console.error(\"Failed to fetch projects\", error)\n            self.state = \"error\"\n        }\n        // The action will return a promise that resolves to the returned value\n        // (or rejects with anything thrown from the action)\n        return self.githubProjects.length\n    })\n\n    return { fetchProjects }\n})\n```\n\n# Creating asynchronous actions\n\nAsynchronous actions are a first class concept in Mobx-State-Tree. Modelling an asynchronous flow can be done in two ways:\n\n1. Model each step of the flow as separate action\n2. Use generators\n\nThe recommended approach is to use _generators_, for reasons mentioned below.\nBut let's take a look at modelling asynchronous actions as a set of actions first.\n\n## Using separate actions\n\nMST doesn't allow changing state outside actions (except when the tree is unprotected).\nThis means that each step in an asynchronous flow that needs to actually change the model needs to become a separate action.\nFor example:\n\n```javascript\nconst Store = types\n    .model({\n        githubProjects: types.array(types.frozen),\n        state: types.enumeration(\"State\", [\"pending\", \"done\", \"error\"])\n    })\n    .actions((self) => ({\n        fetchProjects() {\n            self.githubProjects = []\n            self.state = \"pending\"\n            fetchGithubProjectsSomehow().then(\n                // when promise resolves, invoke the appropiate action\n                // (note that there is no need to bind here)\n                self.fetchProjectsSuccess,\n                self.fetchProjectsError\n            )\n        },\n        fetchProjectsSuccess(projects) {\n            self.state = \"done\"\n            self.githubProjects = projects\n        },\n        fetchProjectsError(error) {\n            console.error(\"Failed to fetch projects\", error)\n            self.state = \"error\"\n        }\n    }))\n```\n\nThis approach works fine and has great type inference, but comes with a few downsides:\n\n1. For complex flows, which update data in the middle of the flow, a lot of \"utility\" actions need to be created.\n2. Each step of the flow is exposed as action to the outside world. In the above example, one could (but shouldn't) directly invoke `store.fetchProjectsSuccess([])`\n3. Middleware cannot distinguish the flow initiating action from the handler actions. This means that actions like `fetchProjectsSuccess` will become part of the recorded action list, although you probably never want to replay it (as replaying `fetchProjects` itself will cause the handler actions to be fired in the end).\n\n## Using generators\n\nGenerators might sound scary, but they are very suitable for expressing asynchronous flows. The above example looks as follows when using generators:\n\n```javascript\nimport { flow } from \"mobx-state-tree\"\n\nconst Store = types\n    .model({\n        githubProjects: types.array(types.frozen),\n        state: types.enumeration(\"State\", [\"pending\", \"done\", \"error\"])\n    })\n    .actions((self) => ({\n        fetchProjects: flow(function* fetchProjects() {\n            // <- note the star, this a generator function!\n            self.githubProjects = []\n            self.state = \"pending\"\n            try {\n                // ... yield can be used in async/await style\n                self.githubProjects = yield fetchGithubProjectsSomehow()\n                self.state = \"done\"\n            } catch (error) {\n                // ... including try/catch error handling\n                console.error(\"Failed to fetch projects\", error)\n                self.state = \"error\"\n            }\n        })\n    }))\n\nconst store = Store.create({})\n// async actions will always return a promise resolving to the returned value\nstore.fetchProjects().then(() => {\n    console.log(\"done\")\n})\n```\n\nCreating asynchronous actions using generators works as follow:\n\n1. The action needs to be marked as generator, by postfixing the `function` keyword with a `*` and a name (which will be used by middleware), and wrapping it with `flow`\n2. The action can be paused by using a `yield` statement. Yield always needs to return a `Promise`.\n3. If the promise resolves, the resolved value will be returned from the `yield` statement, and the action will continue to run\n4. If the promise rejects, the action continues and the rejection reason will be thrown from the `yield` statement\n5. Invoking the asynchronous action returns a promise. That will resolve with the return value of the function, or rejected with any exception that escapes from the asynchronous actions.\n\n> Note: `flow()` is available in `v1.1.0` and above. If you see an error message like: `_mobxStateTree.flow is not a function`, check your version and upgrade if necessary.\n\nUsing generators is syntactically clean.\nBut the main advantage is that they receive first class support from MST.\nMiddleware (see below) can implement specific behavior for asynchronous actions.\nFor example, the `onAction` middleware will only record starting asynchronous flows, but not any async steps that are taking during the flow.\nAfter all, when replaying the invocation will lead to the other steps being executed automatically.\nBesides that, each step in the generator is allowed to modify its own instance, and there is no need to expose the individual flow steps as actions.\n\nSee the [bookshop example sources](https://github.com/coolsoftwaretyler/mst-example-bookshop/blob/main/src/stores/BookStore.js#L25) for a more extensive example.\n\nUsing generators requires Promises and generators to be available. Promises can easily be polyfilled although they tend to be available on every modern JS environment. Generators are well supported as well, and both TypeScript and Babel can compile generators to ES5.\n\nTo see how `flows`s can be monitored and detected in middleware, see the [middleware docs](middleware.md).\n\n## What about async / await?\n\nAsync/await can only be used in trees that are unprotected. Async / await is not flexible enough to allow MST to wrap asynchronous steps in actions automatically, as is done for the generator functions.\nLuckily, using generators in combination with `flow` is very similar to `async / await`: `async function() {}` becomes `flow(function* () {})`, and `await promise` becomes `yield promise`, and further behavior should be the same.\n"
  },
  {
    "path": "docs/concepts/dependency-injection.md",
    "content": "---\nid: dependency-injection\ntitle: Dependency Injection\n---\n\n<div id=\"codefund\"></div>\n\nWhen creating a new state tree it is possible to pass in environment specific data by passing an object as the second argument to a `.create` call.\nThis object should be (shallowly) immutable and can be accessed by any model in the tree by calling `getEnv(self)`.\n\nThis is useful to inject environment or test-specific utilities like a transport layer, loggers, etc. This is also very useful to mock behavior in unit tests or provide instantiated utilities to models without requiring singleton modules.\nSee also the [bookshop example](https://github.com/mobxjs/mobx-state-tree/blob/a4f25de0c88acf0e239acb85e690e91147a8f0f0/examples/bookshop/src/stores/ShopStore.test.js#L9) for inspiration.\n\n```javascript\nimport { types, getEnv } from \"mobx-state-tree\"\n\nconst Todo = types\n    .model({\n        title: \"\"\n    })\n    .actions(self => ({\n        setTitle(newTitle) {\n            // grab injected logger and log\n            getEnv(self).logger.log(\"Changed title to: \" + newTitle)\n            self.title = newTitle\n        }\n    }))\n\nconst Store = types.model({\n    todos: types.array(Todo)\n})\n\n// setup logger and inject it when the store is created\nconst logger = {\n    log(msg) {\n        console.log(msg)\n    }\n}\n\nconst store = Store.create(\n    {\n        todos: [{ title: \"Grab tea\" }]\n    },\n    {\n        logger: logger // inject logger to the tree\n    }\n)\n\nstore.todos[0].setTitle(\"Grab coffee\")\n// prints: Changed title to: Grab coffee\n```\n"
  },
  {
    "path": "docs/concepts/listeners.md",
    "content": "---\nid: listeners\ntitle: Listening to observables, snapshots, patches and actions\nsidebar_label: Listening to changes\n---\n\n<div id=\"codefund\"></div>\n\nMST is powered by MobX. This means that it is immediately compatible with `observer` components or reactions like `autorun`:\n\n```javascript\nimport { autorun } from \"mobx\"\n\nautorun(() => {\n    console.log(storeInstance.selectedTodo.title)\n})\n```\n\nBecause MST keeps immutable snapshots in the background, it is also possible to be notified when a new snapshot of the tree is available. This is similar to `.subscribe` on a redux store:\n\n```javascript\nonSnapshot(storeInstance, (newSnapshot) => {\n    console.info(\"Got new snapshot:\", newSnapshot)\n})\n```\n\nHowever, sometimes it is more useful to precisely know what has changed rather than just receiving a complete new snapshot.\nFor that, MST supports json-patches out of the box.\n\n```javascript\nonPatch(storeInstance, patch => {\n    console.info(\"Got change: \", patch)\n})\n\nstoreInstance.todos[0].setTitle(\"Add milk\")\n// prints:\n{\n    path: \"/todos/0\",\n    op: \"replace\",\n    value: \"Add milk\"\n}\n```\n\nSimilarly, you can be notified whenever an action is invoked by using `onAction`.\n\n```javascript\nonAction(storeInstance, call => {\n    console.info(\"Action was called:\", call)\n})\n\nstoreInstance.todos[0].setTitle(\"Add milk\")\n// prints:\n{\n    path: \"/todos/0\",\n    name: \"setTitle\",\n    args: [\"Add milk\"]\n}\n```\n\nIt is even possible to intercept actions before they are applied by adding middleware using `addMiddleware`:\n\n```javascript\naddMiddleware(storeInstance, (call, next) => {\n    call.args[0] = call.args[0].replace(/tea/gi, \"Coffee\")\n    return next(call)\n})\n```\n\nA more extensive middleware example can be found in this [code sandbox](https://codesandbox.io/s/0yt72).\nFor more details on creating middleware and the exact specification of middleware events, see the [docs](middleware).\n\nFinally, it is not only possible to be notified about snapshots, patches or actions. It is also possible to re-apply them by using `applySnapshot`, `applyPatch` or `applyAction`!\n"
  },
  {
    "path": "docs/concepts/middleware.md",
    "content": "---\nid: middleware\ntitle: Middleware\n---\n\n<div id=\"codefund\"></div>\n\nMiddlewares can be used to intercept any action on a subtree.\n\nIt is allowed to attach multiple middlewares to a node.\nThe order in which middlewares are invoked is inside-out:\nThis means that the middlewares are invoked in the order you attach them.\nThe value returned by the action invoked/ the aborted value gets passed through the middleware chain and can be manipulated.\n\nThe community has created a small set of [pre-built / example middlewares](https://github.com/coolsoftwaretyler/mst-middlewares).\n\nPlay around with a simple example of middleware in action with [this CodeSandbox](https://codesandbox.io/s/vjoql07ool).\n\n## Custom Middleware\n\nMiddlewares can be attached by using:\n\n`addMiddleware(target: IAnyStateTreeNode, handler: IMiddlewareHandler, includeHooks: boolean = true) : IDisposer`\n\n### target\n\nthe middleware will only be attached to actions of the `target` and further sub nodes of such.\n\n### handler\n\nAn example of this is as follows:\n\n```js\nconst store = SomeStore.create()\nconst disposer = addMiddleware(store, (call, next, abort) => {\n    console.log(`action ${call.name} was invoked`)\n    // runs the next middleware\n    // or the implementation of the targeted action\n    // if there is no middleware left to run\n\n    // the value returned from the next can be manipulated\n    next(call, (value) => value + 1)\n})\n```\n\n```js\nconst store = SomeStore.create()\nconst disposer = addMiddleware(store, (call, next, abort) => {\n    console.log(`action ${call.name} was invoked`)\n    // aborts running the middlewares and returns the 'value' instead.\n    // note that the targeted action won't be reached either.\n    return abort(\"value\")\n})\n```\n\nA middleware handler receives three arguments:\n\n1. the description of the the call,\n\n-   a function to invoke the next middleware in the chain and manipulate the returned value from the next middleware in the chain.\n-   a function to abort the middleware queue and return a value.\n\n_Note: You must call either `next(call)` or `abort(value)` within a middleware._\n\n_Note: If you abort, the action invoked will never be reached._\n\n_Note: The value from either `abort('value')` or the returned value from the `action` can be manipulated by previous middlewares._\n\n_Note: It is important to invoke `next(call)` or `abort(value)` synchronously._\n\n_Note: The value of the `abort(value)` must be a promise in case of aborting a `flow`._\n\n#### call\n\n```javascript\nexport type IMiddleWareEvent = {\n    type: IMiddlewareEventType\n    name: string\n    id: number\n    parentId: number\n    rootId: number\n    allParentIds: number[]\n    tree: IStateTreeNode\n    context: IStateTreeNode\n    args: any[]\n}\n\nexport type IMiddlewareEventType =\n    | \"action\"\n    | \"flow_spawn\"\n    | \"flow_resume\"\n    | \"flow_resume_error\"\n    | \"flow_return\"\n    | \"flow_throw\"\n```\n\n-   `name` is the name of the action\n-   `context` is the object on which the action was defined & invoked\n-   `tree` is the root of the MST tree in which the action was fired (`tree === getRoot(context)`)\n-   `args` are the original arguments passed to the action\n-   `id` is a number that is unique per external action invocation.\n-   `parentId` is the number of the action / process that called this action. `0` if it wasn't called by another action but directly from user code\n-   `rootid` is the id of the action that spawned this action. If an action was not spawned by another action, but something external (user event etc), `id` and `rootId` will be equal (and `parentid` `0`)\n-   `allParentIds` is the chain from root until current (excluding current) that called this action. `[]` if it wasn't called by another action but directly from user code\n\n`type` Indicates which kind of event this is\n\n-   `action`: this is a normal synchronous action invocation\n-   `flow_spawn`: The invocation / kickoff of a `process` block (see [asynchronous actions](async-actions.md))\n-   `flow_resume`: a promise that was returned from `yield` earlier has resolved. `args` contains the value it resolved to, and the action will now continue with that value\n-   `flow_resume_error`: a promise that was returned from `yield` earlier was rejected. `args` contains the rejection reason, and the action will now continue throwing that error into the generator\n-   `flow_return`: the generator completed successfully. The promise returned by the action will resolve with the value found in `args`\n-   `flow_throw`: the generator threw an uncatched exception. The promise returned by the action will reject with the exception found in `args`\n\nTo see how a bunch of calls from an asynchronous process look, see the [unit tests](https://github.com/mobxjs/mobx-state-tree/blob/09708ba86d04f433cc23fbcb6d1dc4db170f798e/test/async.ts#L289)\n\nA minimal, empty process will fire the following events if started as action:\n\n1. `action`: An `action` event will always be emitted if a process is exposed as action on a model)\n2. `flow_spawn`: This is just the notification that a new generator was started\n3. `flow_resume`: This will be emitted when the first \"code block\" is entered. (So, with zero yields there is one `flow_resume` still)\n4. `flow_return`: The process has completed\n\n#### next\n\nuse next to call the next middleware.\n\n`next(call: IMiddlewareEvent, callback?: (value: any) => any): void`\n\n-   `call` Before passing the call middleware, feel free to (clone and) modify the `call.args`.\n    Other properties should not be modified\n\n-   `callback` can be used to manipulate values returned by later middlewares or the implementation of the targeted action.\n\n#### abort\n\nuse `abort` if you want to kill the queue of middlewares and immediately return.\nthe implementation of the targeted action won't be reached if you abort the queue.\n\n`abort(value: any) : void`\n\n-   `value` is returned instead of the return value from the implementation of the targeted action.\n\n### includeHooks\n\nset this flag to `false` if you want to avoid having hooks passed to the middleware.\n\n## FAQ\n\n-   I alter a property and the change does not appear in the middleware.\n\n-   _If you alter a value of an unprotected node, the change won't reach the middleware. Only actions can be intercepted._\n"
  },
  {
    "path": "docs/concepts/patches.md",
    "content": "---\nid: patches\ntitle: Patches\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 3: Test mobx-state-tree Models by Recording Snapshots or Patches</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-test-mobx-state-tree-models-by-recording-snapshots-or-patches/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-test-mobx-state-tree-models-by-recording-snapshots-or-patches\">Hosted on egghead.io</a>\n</details>\n\nModifying a model does not only result in a new snapshot, but also in a stream of [JSON-patches](http://jsonpatch.com/) describing which modifications were made.\nPatches have the following signature:\n\n    export interface IJsonPatch {\n        op: \"replace\" | \"add\" | \"remove\"\n        path: string\n        value?: any\n    }\n\n- Patches are constructed according to JSON-Patch, RFC 6902\n- Patches are emitted immediately when a mutation is made and don't respect transaction boundaries (like snapshots)\n- Patch listeners can be used to achieve deep observing\n- The `path` attribute of a patch contains the path of the event relative to the place where the event listener is attached\n- A single mutation can result in multiple patches, for example when splicing an array\n- Patches can be reverse applied, which enables many powerful patterns like undo / redo\n\nUseful methods:\n\n- `onPatch(model, listener)` attaches a patch listener to the provided model, which will be invoked whenever the model or any of its descendants is mutated\n- `applyPatch(model, patch)` applies a patch (or array of patches) to the provided model\n"
  },
  {
    "path": "docs/concepts/react.md",
    "content": "---\nid: using-react\ntitle: React and MST\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 5: Render mobx-state-tree Models in React</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-render-mobx-state-tree-models-in-react/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-render-mobx-state-tree-models-in-react\">Hosted on egghead.io</a>\n</details>\n\n### Can I use React and MST together?\n\nYep, that works perfectly fine, everything that applies to MobX and React applies to MST and React as well.  `observer`, `autorun`, etc. will work as expected.\nTo share MST trees between components we recommend to use `React.createContext`.\n\nIn the examples folder several examples of React and MST can be found, or check this [example](https://github.com/impulse/react-hooks-mobx-state-tree) which uses hooks (recommended).\n\n### Tips\n\nWhen passing models in to a component **do not** use the spread syntax, e.g. `<Component foo={bar} {...model}>`. See [here](https://github.com/mobxjs/mobx-state-tree/issues/726).\n"
  },
  {
    "path": "docs/concepts/reconciliation.md",
    "content": "---\nid: reconciliation\ntitle: Reconciliation\n---\n\n<div id=\"codefund\"></div>\n\n-   When applying snapshots, MST will always try to reuse existing object instances for snapshots with the same identifier (see `types.identifier`).\n-   If no identifier is specified, but the type of the snapshot is correct, MST will reconcile objects as well if they are stored in a specific model property or under the same map key.\n-   In arrays, items without an identifier are never reconciled.\n\nIf an object is reconciled, the consequence is that localState is preserved and `afterCreate` / `attach` life-cycle hooks are not fired because applying a snapshot results just in an existing tree node being updated.\n"
  },
  {
    "path": "docs/concepts/references.md",
    "content": "---\nid: references\ntitle: Identifiers and references\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 13: Create Relationships in your Data with mobx-state-tree Using References and Identifiers</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-create-relationships-in-your-data-with-mobx-state-tree-using-references-and-identifiers/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-create-relationships-in-your-data-with-mobx-state-tree-using-references-and-identifiers\">Hosted on egghead.io</a>\n</details>\n\nReferences and identifiers are a first-class concept in MST.\nThis makes it possible to declare references and keep the data normalized in the background, while you interact with it in a denormalized manner.\n\nExample:\n\n```javascript\nconst Todo = types.model({\n    id: types.identifier,\n    title: types.string\n})\n\nconst TodoStore = types.model({\n    todos: types.array(Todo),\n    selectedTodo: types.reference(Todo)\n})\n\n// create a store with a normalized snapshot\nconst storeInstance = TodoStore.create({\n    todos: [\n        {\n            id: \"47\",\n            title: \"Get coffee\"\n        }\n    ],\n    selectedTodo: \"47\"\n})\n\n// because `selectedTodo` is declared to be a reference, it returns the actual Todo node with the matching identifier\nconsole.log(storeInstance.selectedTodo.title)\n// prints \"Get coffee\"\n```\n\n#### Identifiers\n\n-   Each model can define zero or one `identifier()` properties\n-   The identifier property of an object cannot be modified after initialization\n-   Each identifier / type combination should be unique within the entire tree\n-   Identifiers are used to reconcile items inside arrays and maps - wherever possible - when applying snapshots\n-   The `map.put()` method can be used to simplify adding an object that has an identifiers to a map without specifying the key\n-   The primary goal of identifiers is not validation, but reconciliation and reference resolving. For this reason identifiers cannot be defined or updated after creation. If you want to check if some value just looks as an identifier, without providing the above semantics; use something like: `types.refinement(types.string, v => v.match(/someregex/))`\n\n_Tip: If you know the format of the identifiers in your application, leverage `types.refinement` to actively check this, for example the following definition enforces that identifiers of `Car` always start with the string `\"Car_\"`:_\n\n```javascript\nconst Car = types.model(\"Car\", {\n    id: types.refinement(types.identifier, identifier => identifier.indexOf(\"Car_\") === 0)\n})\n```\n\n#### References\n\nReferences are defined by mentioning the type they should resolve to. The targeted type should have exactly one attribute of the type `identifier`.\nReferences are looked up through the entire tree but per type, so identifiers need to be unique in the entire tree.\n\n#### Customizable references\n\nThe default implementation uses the `identifier` cache to resolve references (See [`resolveIdentifier`](/API#resolveidentifier)).\nHowever, it is also possible to override the resolve logic and provide your own custom resolve logic.\nThis also makes it possible to, for example, trigger a data fetch when trying to resolve the reference ([example](https://github.com/mobxjs/mobx-state-tree/blob/master/__tests__/core/reference-custom.test.ts#L127)).\n\nExample:\n\n```javascript\nconst User = types.model({\n    id: types.identifier,\n    name: types.string\n})\n\nconst UserByNameReference = types.maybeNull(\n    types.reference(User, {\n        // given an identifier, find the user\n        get(identifier /* string */, parent: any /*Store*/) {\n            return parent.users.find(u => u.name === identifier) || null\n        },\n        // given a user, produce the identifier that should be stored\n        set(value /* User */) {\n            return value.name\n        }\n    })\n)\n\nconst Store = types.model({\n    users: types.array(User),\n    selection: UserByNameReference\n})\n\nconst s = Store.create({\n    users: [{ id: \"1\", name: \"Michel\" }, { id: \"2\", name: \"Mattia\" }],\n    selection: \"Mattia\"\n})\n```\n\n#### Reference validation: `isValidReference`, `tryReference`, `onInvalidated` hook and `types.safeReference`\n\nAccessing an invalid reference (a reference to a dead/detached node) triggers an exception.\n\nIn order to check if a reference is valid, MST offers the `isValidReference(() => ref): boolean` function:\n\n```ts\nconst isValid = isValidReference(() => store.myRef)\n```\n\nAlso, if you are unsure if a reference is valid or not you can use the `tryReference(() => ref): ref | undefined` function:\n\n```ts\n// the result will be the passed ref if ok, or undefined if invalid\nconst maybeValidRef = tryReference(() => store.myRef)\n```\n\nThe options parameter for references also accepts an optional `onInvalidated` hook, which will be called when the reference target node that the reference is pointing to is about to be detached/destroyed. It has the following signature:\n\n```ts\nconst refWithOnInvalidated = types.reference(Todo, {\n    onInvalidated(event: {\n        // what is causing the target to become invalidated\n        cause: \"detach\" | \"destroy\" | \"invalidSnapshotReference\"\n        // the target that is about to become invalidated (undefined if \"invalidSnapshotReference\")\n        invalidTarget: STN | undefined\n        // the identifier that is about to become invalidated\n        invalidId: string | number\n        // parent node of the reference (not the reference target)\n        parent: IAnyStateTreeNode\n        // a function to remove the reference from its parent (or set to undefined in the case of models)\n        removeRef: () => void\n        // a function to set our reference to a new target\n        replaceRef: (newRef: STN | null | undefined) => void\n    }) {\n        // do something\n    }\n})\n```\n\nNote that invalidation will only trigger while the reference is attached to a parent (be it a model, an array, a map, etc.).\n\nA default implementation of such `onInvalidated` hook is provided by the `types.safeReference` type. It is like a standard reference, except that once the target node becomes invalidated it will:\n\n-   If its parent is a model: Set its own property to `undefined`\n-   If its parent is an array: Remove itself from the array\n-   If its parent is a map: Remove itself from the map\n\nIn addition to the options possible for a plain reference type, the optional options parameter object also accepts a parameter named `acceptsUndefined`, which is set to true by default, so it is suitable for model properties.\nWhen used inside collections (arrays/maps) it is recommended to set this option to false so it can't take undefined as value, which is usually the desired in those cases.\n\nStrictly speaking, `safeReference` with `acceptsUndefined` set to true (the default) is implemented as\n\n```js\ntypes.maybe(\n    types.reference(Type, {\n        ...customGetSetIfAvailable,\n        onInvalidated(ev) {\n            ev.removeRef()\n        }\n    })\n)\n```\n\nand with `acceptsUndefined` set to false as\n\n```js\ntypes.reference(Type, {\n    ...customGetSetIfAvailable,\n    onInvalidated(ev) {\n        ev.removeRef()\n    }\n})\n```\n\n```js\nconst Todo = types.model({ id: types.identifier })\nconst Store = types.model({\n    todos: types.array(Todo),\n    selectedTodo: types.safeReference(Todo),\n    multipleSelectedTodos: types.array(types.safeReference(Todo, { acceptsUndefined: false }))\n})\n\n// given selectedTodo points to a valid Todo and that Todo is later removed from the todos\n// array, then selectedTodo will automatically become undefined, and if it is included in multipleSelectedTodos\n// then it will be removed from the array\n```\n"
  },
  {
    "path": "docs/concepts/snapshots.md",
    "content": "---\nid: snapshots\ntitle: Snapshots\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 3: Test mobx-state-tree Models by Recording Snapshots or Patches</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-test-mobx-state-tree-models-by-recording-snapshots-or-patches/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-test-mobx-state-tree-models-by-recording-snapshots-or-patches\">Hosted on egghead.io</a>\n</details>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 9: Store Store in Local Storage</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-store-store-in-local-storage/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-store-store-in-local-storage\">Hosted on egghead.io</a>\n</details>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 16: Automatically Send Changes to the Server by Using onSnapshot</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-automatically-send-changes-to-the-server-by-using-onsnapshot/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-automatically-send-changes-to-the-server-by-using-onsnapshot\">Hosted on egghead.io</a>\n</details>\n\nSnapshots are the immutable serialization, in plain objects, of a tree at a specific point in time.\nSnapshots can be inspected through `getSnapshot(node, applyPostProcess)`.\nSnapshots don't contain any type information and are stripped from all actions, etc., so they are perfectly suitable for transportation.\nRequesting a snapshot is cheap as MST always maintains a snapshot of each node in the background and uses structural sharing.\n\n```javascript\ncoffeeTodo.setTitle(\"Tea instead plz\")\n\nconsole.dir(getSnapshot(coffeeTodo))\n// prints `{ title: \"Tea instead plz\" }`\n```\n\nSome interesting properties of snapshots:\n\n-   Snapshots are immutable\n-   Snapshots can be transported\n-   Snapshots can be used to update models or restore them to a particular state\n-   Snapshots are automatically converted to models when needed. So, the two following statements are equivalent: `store.todos.push(Todo.create({ title: \"test\" }))` and `store.todos.push({ title: \"test\" })`.\n\nUseful methods:\n\n-   `getSnapshot(model, applyPostProcess)`: returns a snapshot representing the current state of the model\n-   `onSnapshot(model, callback)`: creates a listener that fires whenever a new snapshot is available (but only one per MobX transaction).\n-   `applySnapshot(model, snapshot)`: updates the state of the model and all its descendants to the state represented by the snapshot\n\n`mobx-state-tree` also supports customizing snapshots when they are generated or when they are applied with [`types.snapshotProcessor`](/overview/hooks)."
  },
  {
    "path": "docs/concepts/trees.md",
    "content": "---\nid: trees\ntitle: Types, models, trees & state\n---\n\n<div id=\"codefund\"></div>\n\n### tree = type + state\n\nEach **node** in the tree is described by two things: Its **type** (the shape of the thing) and its **data** (the state it is currently in).\n\nThe simplest tree possible:\n\n```javascript\nimport { types } from \"mobx-state-tree\" // alternatively, `import { t } from \"mobx-state-tree\"`\n\n// declaring the shape of a node with the type `Todo`\nconst Todo = types.model({\n  title: types.string\n})\n\n// creating a tree based on the \"Todo\" type, with initial data:\nconst coffeeTodo = Todo.create({\n  title: \"Get coffee\"\n})\n```\n\nThe `types.model` type declaration is used to describe the shape of an object.\nOther built-in types include arrays, maps, primitives, etc. See the [types overview](/overview/types).\n\n### Creating models\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 1: Describe Your Application Domain Using mobx-state-tree(MST) Models</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-describe-your-application-domain-using-mobx-state-tree-mst-models/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-describe-your-application-domain-using-mobx-state-tree-mst-models\">Hosted on egghead.io</a>\n</details>\n\nThe most important type in MST is `types.model`, which can be used to describe the shape of an object.\nAn example:\n\n```javascript\nconst TodoStore = types\n  // 1\n  .model(\"TodoStore\", {\n    loaded: types.boolean, // 2\n    endpoint: \"http://localhost\", // 3\n    todos: types.array(Todo), // 4\n    selectedTodo: types.reference(Todo) // 5\n  })\n  .views((self) => {\n    return {\n      // 6\n      get completedTodos() {\n        return self.todos.filter((t) => t.done)\n      },\n      // 7\n      findTodosByUser(user) {\n        return self.todos.filter((t) => t.assignee === user)\n      }\n    }\n  })\n  .actions((self) => {\n    return {\n      addTodo(title) {\n        self.todos.push({\n          id: Math.random(),\n          title\n        })\n      }\n    }\n  })\n```\n\nWhen defining a model, it is advised to give the model a name for debugging purposes (see `// 1`).\nA model takes additionally object argument defining the properties.\n\nThe _properties_ argument is a key-value set where each key indicates the introduction of a property, and the value its type. The following types are acceptable:\n\n1.  A type. This can be a simple primitive type like `types.boolean`, see `// 2`, or a complex, possibly pre-defined type (`// 4`)\n2.  A primitive. Using a primitive as type is syntactic sugar for introducing a property with a default value. See `// 3`, `endpoint: \"http://localhost\"` is the same as `endpoint: types.optional(types.string, \"http://localhost\")`. The primitive type is inferred from the default value. Properties with a default value can be omitted in snapshots.\n3.  A [computed property](https://mobx.js.org/computeds.html), see `// 6`. Computed properties are tracked and memoized by MobX. Computed properties will not be stored in snapshots or emit patch events. It is possible to provide a setter for a computed property as well. A setter should always invoke an action.\n4.  A view function (see `// 7`). A view function can, unlike computed properties, take arbitrary arguments. It won't be memoized, but its value can be tracked by MobX nonetheless. View functions are not allowed to change the model, but should rather be used to retrieve information from the model.\n\n_Tip: `(self) => ({ action1() { }, action2() { }})` is ES6 syntax for `function (self) { return { action1: function() { }, action2: function() { } }}`. In other words, it's short way of directly returning an object literal.\nFor that reason a comma between each member of a model is mandatory, unlike classes which are syntactically a totally different concept._\n\n`types.model` creates a chainable model type, where each chained method produces a new type:\n\n- `.named(name)` clones the current type, but gives it a new name\n- `.props(props)` produces a new type, based on the current one, and adds / overrides the specified properties\n- `.actions(self => object literal with actions)` produces a new type, based on the current one, and adds / overrides the specified actions\n- `.views(self => object literal with view functions)` produces a new type, based on the current one, and adds / overrides the specified view functions\n- `.preProcessSnapshot(snapshot => snapshot)` can be used to pre-process the raw JSON before instantiating a new model. See [Lifecycle hooks](/overview/hooks) or alternatively `types.snapshotProcessor`\n- `.postProcessSnapshot(snapshot => snapshot)` can be used to post-process the raw JSON before getting a model snapshot. See [Lifecycle hooks](/overview/hooks) or alternatively `types.snapshotProcessor`\n\nNote that `views` and `actions` don't define actions and views directly, but rather they should be given a function.\nThe function will be invoked when a new model instance is created. The instance will be passed in as the first and only argument typically called `self`.\nThis has two advantages:\n\n1.  All methods will always be bound correctly, and won't suffer from an unbound `this`\n2.  The closure can be used to store private state or methods of the instance. See also [actions](/concepts/actions) and [volatile state](/concepts/volatiles).\n\nQuick example:\n\n```javascript\nconst TodoStore = types\n  .model(\"TodoStore\", {\n    /* props */\n  })\n  .actions((self) => {\n    const instantiationTime = Date.now()\n\n    function addTodo(title) {\n      console.log(`Adding Todo ${title} after ${(Date.now() - instantiationTime) / 1000}s.`)\n      self.todos.push({\n        id: Math.random(),\n        title\n      })\n    }\n\n    return { addTodo }\n  })\n```\n\nIt is perfectly fine to chain multiple `views`, `props` calls etc in arbitrary order. This can be a great way to structure complex types, mix-in utility functions, etc. Each call in the chain creates a new, immutable type which can itself be stored and reused as part of other types, etc.\n\nIt is also possible to define lifecycle hooks in the _actions_ object. These are actions with a predefined name that are run at a specific moment. See [Lifecycle hooks](/overview/hooks).\n\n### Composing trees\n\nIn MST every node in the tree is a tree in itself.\nTrees can be composed by composing their types:\n\n```javascript\nconst TodoStore = types.model({\n  todos: types.array(Todo)\n})\n\nconst storeInstance = TodoStore.create({\n  todos: [\n    {\n      title: \"Get biscuit\"\n    }\n  ]\n})\n```\n\nThe _snapshot_ passed to the `create` method of a type will recursively be turned in MST nodes. So, you can safely call:\n\n```javascript\nstoreInstance.todos[0].setTitle(\"Chocolate instead plz\")\n```\n\nBecause any node in a tree is a tree in itself, any built-in method in MST can be invoked on any node in the tree, not just the root.\nThis makes it possible to get a patch stream of a certain subtree, or to apply middleware to a certain subtree only.\n\n### Tree semantics in detail\n\nMST trees have very specific semantics. These semantics purposefully constrain what you can do with MST. The reward for that is all kinds of generic features out of the box like snapshots, replayability, etc. If these constraints don't suit your app, you are probably better off using plain MobX with your own model classes, which is fine as well.\n\n1.  Each object in an MST tree is considered a _node_. Each primitive (and frozen) value is considered a _leaf_.\n1.  MST has only three types of nodes: _model_, _array_ and _map_.\n1.  Every _node_ tree in an MST tree is a tree in itself. Any operation that can be invoked on the complete tree can also be applied to a subtree.\n1.  A node can only exist exactly _once_ in a tree. This ensures it has a unique, identifiable position.\n1.  It is however possible to refer to another object in the _same_ tree by using _references_\n1.  There is no limit to the number of MST trees that live in an application. However, each node can only live in exactly one tree.\n1.  All _leaves_ in the tree must be serializable. It is not possible to store, for example, functions in a MST.\n1.  The only free-form type in MST is frozen, with the requirement that frozen values are immutable and serializable so that the MST semantics can still be upheld.\n1.  At any point in the tree it is possible to assign a snapshot to the tree instead of a concrete instance of the expected type. In that case an instance of the correct type, based on the snapshot, will be automatically created for you.\n1.  Nodes in the MST tree will be reconciled (the exact same instance will be reused) when updating the tree by any means, based on their _identifier_ property. If there is no identifier property, instances won't be reconciled.\n1.  If a node in the tree is replaced by another node, the original node will die and become unusable. This makes sure you are not accidentally holding on to stale objects anywhere in your application.\n1.  If you want to create a new node based on an existing node in a tree, you can either `detach` that node, or `clone` it.\n\nThese egghead.io lessons nicely leverage the specific semantics of MST trees:\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 6: Build Forms with React to Edit mobx-state-tree Models</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-build-forms-with-react-to-edit-mobx-state-tree-models/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-build-forms-with-react-to-edit-mobx-state-tree-models\">Hosted on egghead.io</a>\n</details>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 7: Remove Model Instances from the Tree</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-remove-model-instances-from-the-tree/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-remove-model-instances-from-the-tree\">Hosted on egghead.io</a>\n</details>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 8: Create an Entry Form to Add Models to the State Tree</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-create-an-entry-form-to-add-models-to-the-state-tree/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-create-an-entry-form-to-add-models-to-the-state-tree\">Hosted on egghead.io</a>\n</details>\n"
  },
  {
    "path": "docs/concepts/views.md",
    "content": "---\nid: views\ntitle: Derived values\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 4: Derive Information from Models Using Views</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-derive-information-from-models-using-views/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-derive-information-from-models-using-views\">Hosted on egghead.io</a>\n</details>\n\nAny fact that can be derived from your state is called a \"view\" or \"derivation\".\nSee [The gist of MobX](https://mobx.js.org/the-gist-of-mobx.html) for some background.\n\nViews come in two flavors: views with arguments and views without arguments. The latter are called computed values, based on the [computed](https://mobx.js.org/computeds.html) concept in MobX. The main difference between the two is that computed properties create an explicit caching point, but later they work the same and any other computed value or MobX based reaction like [`@observer`](https://mobx.js.org/react-integration.html) components can react to them. Computed values are defined using _getter_ functions.\n\nExample:\n\n```javascript\nimport { autorun } from \"mobx\"\n\nconst UserStore = types\n    .model({\n        users: types.array(User)\n    })\n    .views(self => ({\n        get numberOfChildren() {\n            return self.users.filter(user => user.age < 18).length\n        },\n        numberOfPeopleOlderThan(age) {\n            return self.users.filter(user => user.age > age).length\n        }\n    }))\n\nconst userStore = UserStore.create(/* */)\n\n// Every time the userStore is updated in a relevant way, log messages will be printed\nautorun(() => {\n    console.log(\"There are now \", userStore.numberOfChildren, \" children\")\n})\nautorun(() => {\n    console.log(\"There are now \", userStore.numberOfPeopleOlderThan(75), \" pretty old people\")\n})\n```\n\nIf you want to share volatile state between views and actions, use `.extend` instead of `.views` + `.actions`. See the [volatile state](volatiles) section.\n"
  },
  {
    "path": "docs/concepts/volatiles.md",
    "content": "---\nid: volatiles\ntitle: Volatile state\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 15: Use Volatile State and Lifecycle Methods to Manage Private State</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-use-volatile-state-and-lifecycle-methods-to-manage-private-state/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-use-volatile-state-and-lifecycle-methods-to-manage-private-state\">Hosted on egghead.io</a>\n</details>\n\nMST models primarily aid in storing _persistable_ state. State that can be persisted, serialized, transferred, patched, replaced, etc.\nHowever, sometimes you need to keep track of temporary, non-persistable state. This is called _volatile_ state in MST. Examples include promises, sockets, DOM elements, etc. - state which is needed for local purposes as long as the object is alive.\n\nVolatile state (which is also private) can be introduced by creating variables inside any of the action initializer functions.\n\nVolatile is preserved for the life-time of an object and not reset when snapshots are applied, etc. Note that the life time of an object depends on proper reconciliation, see the [how does reconciliation work?](reconciliation) section.\n\nThe following is an example of an object with volatile state. Note that volatile state here is used to track a XHR request and clean up resources when it is disposed. Without volatile state this kind of information would need to be stored in an external WeakMap or something similar.\n\n```javascript\nconst Store = types\n    .model({\n        todos: types.array(Todo),\n        state: types.enumeration(\"State\", [\"loading\", \"loaded\", \"error\"])\n    })\n    .actions(self => {\n        let pendingRequest = null // a Promise\n\n        function afterCreate() {\n            self.state = \"loading\"\n            pendingRequest = someXhrLib.createRequest(\"someEndpoint\")\n        }\n\n        function beforeDestroy() {\n            // abort the request, no longer interested\n            pendingRequest.abort()\n        }\n\n        return {\n            afterCreate,\n            beforeDestroy\n        }\n    })\n```\n\nSome tips:\n\n1.  Note that multiple `actions` calls can be chained. This makes it possible to create multiple closures with their own protected volatile state.\n2.  Although in the above example the `pendingRequest` could be initialized directly in the action initializer, it is recommended to do this in the `afterCreate` hook, which will only be called once the entire instance has been set up (there might be many action and property initializers for a single type).\n\n3.  The above example doesn't actually use the promise. For how to work with promises / asynchronous flows, see the [asynchronous actions](async-actions) section.\n\n4.  It is possible to share volatile state between views and actions by using `extend`. `.extend` works like a combination of `.actions` and `.views` and should return an object with a `actions` and `views` field:\n\nHere's an example of how to do your own volatile state using an observable:\n\n```javascript\n// if your local state is part of a view getter (computed) then\n// it is important to make sure that state used such getters are observable,\n// or else the value returned by the view would become stale upon observation\nconst Todo = types.model({}).extend(self => {\n    const localState = observable.box(3)\n\n    return {\n        views: {\n            // note this one IS a getter (computed value)\n            get x() {\n                return localState.get()\n            }\n        },\n        actions: {\n            setX(value) {\n                localState.set(value)\n            }\n        }\n    }\n})\n```\n\nAnd here's an example of how to do your own volatile state _not_ using an observable (but if you do this make sure the local state will _never_ be used in a computed value first and bear in mind it _won't_ be reactive!):\n\n```javascript\n// if not using an observable then make sure your local state is NOT part of a view getter or computed value of any kind!\n// also changes to it WON'T be reactive\nconst Todo = types.model({}).extend(self => {\n    let localState = 3\n\n    return {\n        views: {\n            // note this one is NOT a getter (NOT a computed value)\n            // if this were a getter this value would get stale upon observation\n            getX() {\n                return localState\n            }\n        },\n        actions: {\n            setX(value) {\n                localState = value\n            }\n        }\n    }\n})\n```\n\n\n### model.volatile\n\nSince the pattern above (having a volatile state that is _observable_ (in terms of Mobx observables) and _readable_ from outside the instance) is such a common pattern there is a shorthand to declare such properties. The example above can be rewritten as:\n\n```javascript\nconst Todo = types\n    .model({})\n    .volatile(self => ({\n        localState: 3\n    }))\n    .actions(self => ({\n        setX(value) {\n            self.localState = value\n        }\n    }))\n```\n\nThe object that is returned from the `volatile` initializer function can contain any piece of data and will result in an instance property with the same name. Volatile properties have the following characteristics:\n\n1.  They can be read from outside the model (if you want hidden volatile state, keep the state in your closure as shown in the previous section, and _only_ if it is not used on a view consider not making it observable)\n2.  The volatile properties will be only observable, see [observable _references_](https://mobx.js.org/api.html#observableref). Values assigned to them will be unmodified and not automatically converted to deep observable structures.\n3.  Like normal properties, they can only be modified through actions\n4.  Volatile props will not show up in snapshots, and cannot be updated by applying snapshots\n5.  Volatile props are preserved during the lifecycle of an instance. See also [reconciliation](reconciliation)\n6.  Changes in volatile props won't show up in the patch or snapshot stream\n7.  It is currently not supported to define getters / setters in the object returned by `volatile`\n8.  Volatile prop values aren't limited to values of MST's types and can be assigned any value. This includes [JS primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) such as `string`, `number`, `Symbol`, [Object types](https://developer.mozilla.org/en-US/docs/Glossary/Object) such as `Function`, POJOs, classes - and platform API's like `localStorage`, `window.fetch` and basically anything you want.\n"
  },
  {
    "path": "docs/intro/examples.md",
    "content": "---\nid: examples\ntitle: Examples\n---\n\n<div id=\"codefund\"></div>\n\n- [Bookshop](https://github.com/coolsoftwaretyler/mst-example-bookshop/) Example webshop application with references, identifiers, routing, testing, etc.\n- [Boxes](https://github.com/coolsoftwaretyler/mst-example-boxes) Example app where one can draw, drag, and drop boxes. With time-travelling and multi-client synchronization over websockets.\n- [TodoMVC](https://github.com/coolsoftwaretyler/mst-example-todomvc) Classic example app using React and MST.\n- [Redux TodoMVC](https://github.com/coolsoftwaretyler/mst-example-redux-todomvc) Redux TodoMVC application, except that the reducers are replaced with a MST. Tip: open the Redux devtools; they will work!\n"
  },
  {
    "path": "docs/intro/getting-started.md",
    "content": "---\nid: getting-started\ntitle: Getting Started Tutorial\n---\n\n<div id=\"codefund\"></div>\n\nThis tutorial will introduce you to the basics of `mobx-state-tree` (MST) by building a TODO application. The application will also have the ability to assign each TODO to a user.\n\n## Prerequisites\n\nThis tutorial assumes that you know the basics of how to use React. If you don't know what React is and how to use it, you may wish to read [this tutorial](https://facebook.github.io/react/tutorial/tutorial.html) first.\n\n### Do I need to learn MobX?\n\nMST is heavily based on MobX. A basic understanding of the MobX library will help when dealing with complex situations and connecting of the data with React components. If you don't have MobX experience, don't worry, working with MST does not require any MobX API knowledge.\n\n## How to follow this tutorial\n\nYou can write the code for this tutorial in the browser using the CodeSandbox playground, or in your preferred code editor (e.g. VSCode).\n\n### Writing code in the browser\n\nFor each example you'll find a CodeSandbox playground link. You can start from the playground of each point and manually progress to the next tutorial step by using it. If you're stuck, feel free to have a sneak peak from the next playground link! :)\n\n### Writing code in the editor\n\nSetting up the whole environment for a React project involves transpilers, bundlers, linters, etc., and setting them up may become very tedious and not fun. Thanks to `create-react-app` setting up all those tools becomes as easy as typing a couple of lines in your terminal.\n\n```\nnpx create-react-app mst-todo\n```\n\nNext install `mobx`, `mobx-react-lite` and `mobx-state-tree` dependencies.\n\n```\nyarn add mobx mobx-react-lite mobx-state-tree\n```\n\nNow you can run `npm run start` and a basic React page will show up. You're all set up and can begin editing the project files!\n\n## Overview\n\nMST is a state container that combines the simplicity and ease of mutable data with the traceability of immutable data and the reactiveness and performance of observable data.\n\nIf the above sentence confused you, don't worry. We will dive deeper together and explore what it means step by step.\n\n## Getting Started\n\nWhen building applications with MST, the first exercise that will help you building your application is thinking about what is the minimal set of entities and their relative attributes.\n\nIn our example application we will deal with TODOs, so we need a `Todo` entity. The `Todo` entity will have a `name` and a `done` attribute to store if the `Todo` is done or not. We will also have knowledge of users, so we need a `User` entity that will have a `name` attribute and will be assignable to TODOs.\n\nSo far our entities and their attributes look like this:\n\n`Todo`\n\n- name\n- done\n\n`User`\n\n- name\n\n## Creating our first model\n\nCentral to MST is the concept of a living tree. The tree consists of mutable, but strictly protected objects enriched with run-time type information. In other words; each tree has a shape (type information) and state (data). From this living tree, immutable and structurally shared snapshots are generated automatically.\n\nThis means that in order to make our application work, we need to describe to MST how our attributes are shaped. Knowing that, MST will be able to automatically generate all those boundaries, and help us avoid making silly mistakes, like putting a string in price field or a boolean where an array is expected.\n\nThe simplest way to define a model for an entity in MST is to provide sample data that will be used as defaults for it, and pass it to the `types.model` function.\n\n```javascript\nimport { types } from \"mobx-state-tree\" // alternatively, `import { t } from \"mobx-state-tree\"`\n\nconst Todo = types.model({\n  name: \"\",\n  done: false\n})\n\nconst User = types.model({\n  name: \"\"\n})\n```\n\n[View sample in the playground](https://codesandbox.io/s/235jykjp90)\n\nThe above code will create two models, a `Todo` and a `User` model, but as we said before, a tree model in MST consists of type information (and we just saw how to define them) and state (the instance data). So how do we create instances of the `Todo` and `User` models?\n\n## Creating model instances (tree nodes)\n\nThis can be easily done by calling `.create()` on the `Todo` and `User` models we just defined.\n\n```javascript\nimport { types, getSnapshot } from \"mobx-state-tree\"\n\nconst Todo = types.model({\n  name: \"\",\n  done: false\n})\n\nconst User = types.model({\n  name: \"\"\n})\n\nconst john = User.create()\nconst eat = Todo.create()\n\nconsole.log(\"John:\", getSnapshot(john))\nconsole.log(\"Eat TODO:\", getSnapshot(eat))\n```\n\n[View sample in the playground](https://codesandbox.io/s/kkl8kn4pq5)\n\nAs you will see, using models ensures that all the attributes defined will always be present and defaulted to the predefined values. If you want to change those values when creating the model instance, you can simply pass an object with the values to use into the `.create` function.\n\n```javascript\nconst eat = Todo.create({ name: \"eat\" })\n\nconsole.log(\"Eat TODO:\", getSnapshot(eat)) // => will print {name: \"eat\", done: false}\n```\n\n[View sample in the playground](https://codesandbox.io/s/jpmpyj7pm3)\n\n## Meeting types\n\nWhen playing with this feature and passing in values to the `.create` function, you may encounter an error like this:\n\n```javascript\nconst eat = Todo.create({ name: \"eat\", done: 1 })\n```\n\n```\nError: [mobx-state-tree] Error while converting `{\"name\":\"eat\",\"done\":1}` to `AnonymousModel`:\nat path \"/done\" value `1` is not assignable to type: `boolean`.\n```\n\nWhat does this mean? As I said before, MST nodes are type-enriched. This means that providing a value (number) of the wrong type (expected boolean) will make MST throw an error. This is very helpful when building applications, as it will keep your state consistent and avoid entering illegal states due to data of the wrong type. To be honest with you, I lied when I told you how to define models. The syntax you used was only a shortcut for the following syntax:\n\n```javascript\nconst Todo = types.model({\n  name: types.optional(types.string, \"\"),\n  done: types.optional(types.boolean, false)\n})\n\nconst User = types.model({\n  name: types.optional(types.string, \"\")\n})\n```\n\n[View sample in the playground](https://codesandbox.io/s/kx9x4973z3)\n\nThe `types` namespace provided in the MST package provides a lot of useful types and utility types like array, map, maybe, refinements and unions. If you are interested in them, feel free to check out the [types overview](/overview/types) for the whole list and their parameters.\n\nIn version `5.4.z` and above, MST also provides a namespace called `t`, which is the exact same export as `types`, but may offer some clarity when you are discussing things like TypeScript types and MobX-State-Tree \"types\" in the same context. We hope this gives user a tool to disambiguate their thoughts, both in code and person-to-person communication.\n\nWe can now use this knowledge to combine models and define the root model of our store that will hold `Todo` and `User` instances in the `todos` and `users` maps.\n\n```javascript\nimport { types } from \"mobx-state-tree\" // alternatively, `import { t } from \"mobx-state-tree\"`\n\nconst Todo = types.model({\n  name: types.optional(types.string, \"\"),\n  done: types.optional(types.boolean, false)\n})\n\nconst User = types.model({\n  name: types.optional(types.string, \"\")\n})\n\nconst RootStore = types.model({\n  users: types.map(User),\n  todos: types.optional(types.map(Todo), {})\n})\n\nconst store = RootStore.create({\n  users: {} // users is not required really since arrays and maps are optional by default since MST3\n})\n```\n\n[View sample in the playground](https://codesandbox.io/s/kk63vox225)\n\nNotice that the `types.optional` second argument is required as long you don't pass a value in the `.create` function of the model. If you want, for example, to make the `name` or `todos` attribute required when calling `.create`, remove the `types.optional` function call and pass the `types.*` included inside.\n\n## Modifying data\n\nMST tree nodes (model instances) can be modified using actions. Actions are collocated with your model and can easily be defined by declaring `.actions` over your model and passing it a function that accepts the model instance and returns an object with the functions that modify that tree node.\n\nFor example, the following actions will be defined on the `Todo` model, and will allow you to toggle the `done` and set the `name` attribute of the provided `Todo` instance.\n\n```javascript\nconst Todo = types\n  .model({\n    name: types.optional(types.string, \"\"),\n    done: types.optional(types.boolean, false)\n  })\n  .actions((self) => ({\n    setName(newName) {\n      self.name = newName\n    },\n\n    toggle() {\n      self.done = !self.done\n    }\n  }))\n\nconst User = types.model({\n  name: types.optional(types.string, \"\")\n})\n\nconst RootStore = types\n  .model({\n    users: types.map(User),\n    todos: types.map(Todo)\n  })\n  .actions((self) => ({\n    addTodo(id, name) {\n      self.todos.set(id, Todo.create({ name }))\n    }\n  }))\n```\n\n[View sample in the playground](https://codesandbox.io/s/3xw9x060mp)\n\nPlease notice the use of `self`. `self` is the object being constructed when an instance of your model is created. Thanks to the `self` object, instance actions are \"this-free\", allowing you to be sure that they are correctly bound.\n\nCalling the actions is as simple as what you would do with plain JavaScript classes, you simply call them on a model instance!\n\n```javascript\nstore.addTodo(1, \"Eat a cake\")\nstore.todos.get(1).toggle()\n```\n\n[View sample in the playground](https://codesandbox.io/s/r673zxw4p)\n\n## Snapshots are awesome!\n\nDealing with mutable data and objects makes it easy to change data on the fly, but on the other hand it makes testing hard. Immutable data makes that very easy. Is there a way to have the best of both worlds? Nature is a great example of that. Beings are living and mutable, but we may eternalize nature's beauty by taking awesome snapshots. Can we do the same with the state of our application?\n\nThanks to MST's knowledge of models and relative property types, MST is able to generate serializable snapshots of our store! You can easily get a snapshot of the store by using the `getSnapshot` function exported by the MST package.\n\n```javascript\nconsole.log(getSnapshot(store))\n/*\n{\n    \"users\": {},\n    \"todos\": {\n        \"1\": {\n            \"name\": \"Eat a cake\",\n            \"done\": true\n        }\n    }\n}\n*/\n```\n\nBecause the nature of state is mutable, a snapshot will be emitted whenever the state is mutated. To listen to the new snapshots, you can use `onSnapshot(store, snapshot => console.log(snapshot))` and log them as they are emitted.\n\n## From snapshot to model\n\nAs we just saw, getting a snapshot from a model instance is pretty easy, but wouldn't it be neat to be able to restore a model from a snapshot? The good news is that you can!\n\nThat basically means that you can restore your objects with your custom methods by just knowing the type of the tree and its snapshot! You can perform this operation in two ways.\n\n1. By creating a new model instance, and passing in the snapshot as argument to the `.create` function. This means that you will need to update all your store references, if used in React components, to the new one.\n\n2. Avoiding this reference problem by applying the snapshot to an existing model instance. Properties will be updated, but the store reference will remain the same. This will trigger an operation called \"reconciliation\". We will talk about this phase later.\n\n```javascript\n// 1st\nconst store = RootStore.create({\n  users: {},\n  todos: {\n    1: {\n      name: \"Eat a cake\",\n      done: true\n    }\n  }\n})\n\n// 2nd\napplySnapshot(store, {\n  users: {},\n  todos: {\n    1: {\n      name: \"Eat a cake\",\n      done: true\n    }\n  }\n})\n```\n\n[View sample in the playground](https://codesandbox.io/s/3x3v5kl5mq)\n\n## Time travel\n\nThe ability of getting snapshots and applying them makes implementing time travel really easy in user-land. What you need to do is listen for snapshots, store them and re-apply them to enable time travel!\n\nA sample implementation would look like this:\n\n```javascript\nimport { applySnapshot, onSnapshot } from \"mobx-state-tree\"\n\nvar states = []\nvar currentFrame = -1\n\nonSnapshot(store, (snapshot) => {\n  if (currentFrame === states.length - 1) {\n    currentFrame++\n    states.push(snapshot)\n  }\n})\n\nexport function previousState() {\n  if (currentFrame === 0) return\n  currentFrame--\n  applySnapshot(store, states[currentFrame])\n}\n\nexport function nextState() {\n  if (currentFrame === states.length - 1) return\n  currentFrame++\n  applySnapshot(store, states[currentFrame])\n}\n```\n\n## Getting to the UI\n\nMST loves MobX, and is fully compatible with it's `autorun`, `reaction`, `observe` and other parts of the API. You can use the `mobx-react-lite` package to connect a MST store to a React component. More details can be found in the `mobx-react-lite` package documentation, but keep in mind that any view engine could be easily integrated with MST, just listen to `onSnapshot` and update accordingly!\n\n```javascript\nimport { observer } from \"mobx-react-lite\"\n\nconst App = observer((props) => (\n  <div>\n    <button onClick={(e) => props.store.addTodo(randomId(), \"New Task\")}>Add Task</button>\n    {Array.from(props.store.todos.values()).map((todo) => (\n      <div>\n        <input type=\"checkbox\" checked={todo.done} onChange={(e) => todo.toggle()} />\n        <input type=\"text\" value={todo.name} onChange={(e) => todo.setName(e.target.value)} />\n      </div>\n    ))}\n  </div>\n))\n```\n\n[View sample in the playground](https://codesandbox.io/s/310ol795x6)\n\n## Improving render performance\n\nIf you have the React DevTools installed, enable the \"Highlight Updates\" check and you will see that the entire application will re-render whenever a `Todo` is toggled or a `name` is changed. That's a shame, as this can cause performance issues if there's a lot of `Todo`'s in our list!\n\nThanks to the ability of MobX to emit granular updates, fixing that becomes pretty easy! You just need to split the rendering of a `Todo` into another component to only re-render that component whenever the `Todo` data changes.\n\n```javascript\nconst TodoView = observer((props) => (\n  <div>\n    <input type=\"checkbox\" checked={props.todo.done} onChange={(e) => props.todo.toggle()} />\n    <input\n      type=\"text\"\n      value={props.todo.name}\n      onChange={(e) => props.todo.setName(e.target.value)}\n    />\n  </div>\n))\n\nconst AppView = observer((props) => (\n  <div>\n    <button onClick={(e) => props.store.addTodo(randomId(), \"New Task\")}>Add Task</button>\n    {Array.from(props.store.todos.values()).map((todo) => (\n      <TodoView todo={todo} />\n    ))}\n  </div>\n))\n```\n\n[View sample in the playground](https://codesandbox.io/s/jvmw9oxyxv)\n\nEach `observer` declaration will enable the React component to only re-render if any of it's observed data changes. Since our `App` component was observing everything, it was re-rendering whenever you changed something.\n\nNow that we have split the rendering logic out into a separate observer, the `TodoView` will re-render only if that `Todo` changes, and `AppView` will re-render only if a new `Todo` is added or removed since it's observing only the length of the `todos` map.\n\n## Computed properties\n\nWe now want to display the count of TODOs to be done in our application, to help users know how many TODOs are left. That means that we need to count the number of TODOs with `done` set to `false`. To do this, we need to modify the `RootStore` declaration and add a getter property over our model by calling `.views` that will count how many TODOs are left.\n\n```javascript\nconst RootStore = types\n  .model({\n    users: types.map(User),\n    todos: types.map(Todo)\n  })\n  .views((self) => ({\n    get pendingCount() {\n      return Array.from(self.todos.values()).filter((todo) => !todo.done).length\n    },\n    get completedCount() {\n      return Array.from(self.todos.values()).filter((todo) => todo.done).length\n    }\n  }))\n  .actions((self) => ({\n    addTodo(id, name) {\n      self.todos.set(id, Todo.create({ name }))\n    }\n  }))\n```\n\n[View sample in the playground](https://codesandbox.io/s/7z01y57no0)\n\nThese properties are called \"computed\" because they keep track of the changes to the observed attributes and recompute automatically if anything used by that attribute changes. This allows for performance savings; for example changing the `name` of a TODO won't affect the number of pending and completed count, as such it wont trigger a recalculation of those counters.\n\nWe can easily see that by creating an additional component in our application that observes the store and renders those counters. Using the React DevTools and tracing updates, you'll see that changing the `name` of a TODO won't re-render the counters, while checking completed or uncompleted will re-render the `TodoView` and `TodoCounterView`.\n\n```javascript\nconst TodoCounterView = observer((props) => (\n  <div>\n    {props.store.pendingCount} pending, {props.store.completedCount} completed\n  </div>\n))\n\nconst AppView = observer((props) => (\n  <div>\n    <button onClick={(e) => props.store.addTodo(randomId(), \"New Task\")}>Add Task</button>\n    {Array.from(props.store.todos.values()).map((todo) => (\n      <TodoView todo={todo} />\n    ))}\n    <TodoCounterView store={props.store} />\n  </div>\n))\n```\n\n[View sample in the playground](https://codesandbox.io/s/k21ol780xr)\n\nIf you `console.log` your snapshot you'll notice that computed properties won't appear in snapshots. That's fine and intended, since those properties must be computed over the other properties of the tree, they can be re-produced by knowing just their definition. For the same reason, if you provide a computed value in a snapshot you'll end up with an error when you attempt to apply it.\n\n## Model views\n\nYou may need to use the list of `todos` filtered by completion in various locations of your application. Even if accessing the list of `todos` and filtering them every time may look like a viable solution, if the filter logic is complex or changes over time you'll find out that it's not a viable solution.\n\nMST solves that by providing the ability to declare model views. A model's `.views` is declared as a function over the properties (first argument) of the model declaration. Model views can accept parameters and only read data from our store. If you try to change your store from a model view, MST will throw an error and prevent you from doing so.\n\n```javascript\nconst RootStore = types\n  .model({\n    users: types.map(User),\n    todos: types.map(Todo)\n  })\n  .views((self) => ({\n    get pendingCount() {\n      return Array.from(self.todos.values()).filter((todo) => !todo.done).length\n    },\n    get completedCount() {\n      return Array.from(self.todos.values()).filter((todo) => todo.done).length\n    },\n    getTodosWhereDoneIs(done) {\n      return Array.from(self.todos.values()).filter((todo) => todo.done === done)\n    }\n  }))\n  .actions((self) => ({\n    addTodo(id, name) {\n      self.todos.set(id, Todo.create({ name }))\n    }\n  }))\n```\n\n[View sample in the playground](https://codesandbox.io/s/x293k4q95o)\n\nNotice that other views and View components may call `getTodosWhereDoneIs` outside of the store definition.\n\n## Going further: References\n\nOk, the basics of our TODO application are done! But as I said when starting this tutorial, we want to be able to provide assignees for each of our TODOs!\n\nWe will focus on this feature; to do that let's assume that the list of users comes from an XHR request or another data source. Feel free to either implement it or add to the TODO application a user management feature.\n\nFirst, we need to populate the `users` map. To do so, we will simply pass in some users when creating the `users` map.\n\n```javascript\nconst store = RootStore.create({\n  users: {\n    1: {\n      name: \"mweststrate\"\n    },\n    2: {\n      name: \"mattiamanzati\"\n    },\n    3: {\n      name: \"johndoe\"\n    }\n  },\n  todos: {\n    1: {\n      name: \"Eat a cake\",\n      done: true\n    }\n  }\n})\n```\n\n[View sample in the playground](https://codesandbox.io/s/7wwn0x4xkq)\n\nNow we need to change our `Todo` model to store the user assigned to the TODO. You could do that by storing the `User` map `id`, and provide a computed that resolves to the user (you can do it as an exercise), but you would end up with a copious amount of code.\n\nMST supports references out of the box. That means that we can define a `user` attribute on the `Todo` model that's a reference to a `User` instance. When getting the snapshot, the value of that attribute will be the identifier of the `User`, when reading, it will resolve to the correct instance of the `User` model and when setting you could provide either the `User` model instance or the `User` identifier.\n\n### Identifiers\n\nIn order to make our reference work, we need to tell MST which attribute to use as a unique identifier of each `User` model instance.\n\nThe identifier attribute cannot be mutated once the model instance has been created. That also means that if you try to apply a snapshot with a different identifier on that model, it will throw an error. On the other hand, providing an identifier helps MST understand elements in maps and arrays, and allows it to correctly reuse model instances in arrays and maps when possible.\n\nTo define an identifier, you will need to define a property using the `types.identifier` type composer. For example, we want the identifier to be a string.\n\n```javascript\nconst User = types.model({\n  id: types.identifier,\n  name: types.optional(types.string, \"\")\n})\n```\n\nAs I said before, identifiers are required upon creation of the element and cannot be mutated, so if you end up receiving an error like this, it's because you also have to provide ids for the users in the snapshot for the `.create` of `RootStore`.\n\n```\nError: [mobx-state-tree] Error while converting `{\"users\":{\"1\":{\"name\":\"mweststrate\"},\"2\":{\"name\":\"mattiamanzati\"},\"3\":{\"name\":\"johndoe\"}},\"todos\":{\"1\":{\"name\":\"Eat a cake\",\"done\":true}}}` to `AnonymousModel`:\nat path \"/users/1/id\" value `undefined` is not assignable to type: `identifier(string)`, expected an instance of `identifier(string)` or a snapshot like `identifier(string)` instead.\nat path \"/users/2/id\" value `undefined` is not assignable to type: `identifier(string)`, expected an instance of `identifier(string)` or a snapshot like `identifier(string)` instead.\nat path \"/users/3/id\" value `undefined` is not assignable to type: `identifier(string)`, expected an instance of `identifier(string)` or a snapshot like `identifier(string)` instead.\n```\n\nWe can easily fix that by providing a correct snapshot.\n\n```javascript\nconst store = RootStore.create({\n  users: {\n    1: {\n      id: \"1\",\n      name: \"mweststrate\"\n    },\n    2: {\n      id: \"2\",\n      name: \"mattiamanzati\"\n    },\n    3: {\n      id: \"3\",\n      name: \"johndoe\"\n    }\n  },\n  todos: {\n    1: {\n      name: \"Eat a cake\",\n      done: true\n    }\n  }\n})\n```\n\n[View sample in the playground](https://codesandbox.io/s/44jn3pv2x)\n\n### How to define the reference\n\nThe reference we are looking for can be easily defined as `types.reference(User)`. Sometimes this can lead to circular references that may use a model before it's declared. To postpone the resolution of the model, you can use `types.late(() => User)` instead of just `User` and that will hoist the model and defer its evaluation. The `user` assignee for the `Todo` could also be omitted, so we will use `types.maybe(...)` to allow the `user` property to be `null` and be initialized as `null`.\n\n```javascript\nconst Todo = types\n  .model({\n    name: types.optional(types.string, \"\"),\n    done: types.optional(types.boolean, false),\n    user: types.maybe(types.reference(types.late(() => User)))\n  })\n  .actions((self) => ({\n    setName(newName) {\n      self.name = newName\n    },\n    toggle() {\n      self.done = !self.done\n    }\n  }))\n```\n\n[View sample in the playground](https://codesandbox.io/s/xv1lkqw9oq)\n\n### Setting a reference value\n\nThe reference value can be set by providing either the identifier or a model instance. First of all, we need to define an action that will allow you to change the `user` of the `Todo`.\n\n```javascript\nconst Todo = types\n  .model({\n    name: types.optional(types.string, \"\"),\n    done: types.optional(types.boolean, false),\n    user: types.maybe(types.reference(types.late(() => User)))\n  })\n  .actions((self) => ({\n    setName(newName) {\n      self.name = newName\n    },\n    setUser(user) {\n      if (user === \"\") {\n        // When selected value is empty, set as undefined\n        self.user = undefined\n      } else {\n        self.user = user\n      }\n    },\n    toggle() {\n      self.done = !self.done\n    }\n  }))\n```\n\nNow we need to edit our views to display a select along with each `TodoView`, where the user can choose the assignee for that task. To do so, we will create a separate component `UserPickerView` and use it inside the `TodoView` component to trigger the `setUser` call. That's it!\n\n```javascript\nconst UserPickerView = observer((props) => (\n  <select value={props.user ? props.user.id : \"\"} onChange={(e) => props.onChange(e.target.value)}>\n    <option value=\"\">-none-</option>\n    {Array.from(props.store.users.values()).map((user) => (\n      <option value={user.id}>{user.name}</option>\n    ))}\n  </select>\n))\n\nconst TodoView = observer((props) => (\n  <div>\n    <input type=\"checkbox\" checked={props.todo.done} onChange={(e) => props.todo.toggle()} />\n    <input\n      type=\"text\"\n      value={props.todo.name}\n      onChange={(e) => props.todo.setName(e.target.value)}\n    />\n    <UserPickerView\n      user={props.todo.user}\n      store={props.store}\n      onChange={(userId) => props.todo.setUser(userId)}\n    />\n  </div>\n))\n\nconst TodoCounterView = observer((props) => (\n  <div>\n    {props.store.pendingCount} pending, {props.store.completedCount} completed\n  </div>\n))\n\nconst AppView = observer((props) => (\n  <div>\n    <button onClick={(e) => props.store.addTodo(randomId(), \"New Task\")}>Add Task</button>\n    {Array.from(props.store.todos.values()).map((todo) => (\n      <TodoView store={props.store} todo={todo} />\n    ))}\n    <TodoCounterView store={props.store} />\n  </div>\n))\n```\n\n[View sample in the playground](https://codesandbox.io/s/6j3qy74kpw)\n\n## References are safe!\n\nOne neat feature of references, is that they will throw an error if you accidentally remove a model that is required by a computed property! If you try to remove a user that's used by a reference, you'll get something like this:\n\n```\n[mobx-state-tree] Failed to resolve reference of type <late>: '1' (in: /todos/1/user)\n```\n"
  },
  {
    "path": "docs/intro/installation.md",
    "content": "---\nid: installation\ntitle: Installation\n---\n\n<div id=\"codefund\"></div>\n\n-   NPM: `npm install mobx mobx-state-tree --save`\n-   Yarn: `yarn add mobx mobx-state-tree`\n-   CDN: https://unpkg.com/mobx-state-tree/dist/mobx-state-tree.umd.js (exposed as `window.mobxStateTree`)\n-   CodeSandbox [TodoList demo](https://codesandbox.io/s/y64pzxj01) fork for testing and bug reporting\n\nTypeScript typings are included in the packages. Use `module: \"commonjs\"` or `moduleResolution: \"node\"` to make sure they are picked up automatically in any consuming project.\n\nSupported environments:\n\n-   MobX-State-Tree 4+ runs in any JavaScript environment, including browsers, Node, React Native (including Hermes), and more\n\nSupported devtools:\n\n-   [Reactotron](https://github.com/infinitered/reactotron)\n-   [MobX DevTools](https://chrome.google.com/webstore/detail/mobx-developer-tools/pfgnfdagidkfgccljigdamigbcnndkod)\n-   The Redux DevTools can be connected as well as demonstrated [here](https://github.com/coolsoftwaretyler/mst-example-redux-todomvc/blob/main/src/index.js#L6)\n"
  },
  {
    "path": "docs/intro/philosophy.md",
    "content": "---\nid: philosophy\ntitle: Overview & Philosophy\n---\n\n<div id=\"codefund\"></div>\n\n`mobx-state-tree` (also known as \"MST\") is a state container that combines the _simplicity and ease of mutable data_ with the _traceability of immutable data_ and the _reactiveness and performance of observable data_.\n\nSimply put, MST tries to combine the best features of both immutability (transactionality, traceability and composition) and mutability (discoverability, co-location and encapsulation) based approaches to state management; everything to provide the best developer experience possible.\nUnlike MobX itself, MST is very opinionated about how data should be structured and updated.\nThis makes it possible to solve many common problems out of the box.\n\nCentral in MST is the concept of a _living tree_. The tree consists of mutable, but strictly protected objects enriched with _runtime type information_. In other words, each tree has a _shape_ (type information) and _state_ (data).\nFrom this living tree, immutable, structurally shared, snapshots are automatically generated.\n\n```javascript\nimport { types, onSnapshot } from \"mobx-state-tree\"\n\nconst Todo = types\n    .model(\"Todo\", {\n        title: types.string,\n        done: false\n    })\n    .actions((self) => ({\n        toggle() {\n            self.done = !self.done\n        }\n    }))\n\nconst Store = types.model(\"Store\", {\n    todos: types.array(Todo)\n})\n\n// create an instance from a snapshot\nconst store = Store.create({\n    todos: [\n        {\n            title: \"Get coffee\"\n        }\n    ]\n})\n\n// listen to new snapshots\nonSnapshot(store, (snapshot) => {\n    console.dir(snapshot)\n})\n\n// invoke action that modifies the tree\nstore.todos[0].toggle()\n// prints: `{ todos: [{ title: \"Get coffee\", done: true }]}`\n```\n\nBy using the type information available, snapshots can be converted to living trees, and vice versa, with zero effort.\nBecause of this, [time travelling](https://github.com/coolsoftwaretyler/mst-example-boxes/blob/main/src/stores/time.js) is supported out of the box, and tools like <abbr title=\"Hot Module Reload\">HMR</abbr> are trivial to support, see this [HMR example](https://github.com/coolsoftwaretyler/mst-example-boxes/blob/main/src/stores/domain-state.js#L116-L126).\n\nThe type information is designed in such a way that it is used both at design- and run-time to verify type correctness (Design time type checking works in TypeScript only at the moment; Flow PR's are welcome!)\n\n```\n[mobx-state-tree] Value '{\\\"todos\\\":[{\\\"turtle\\\":\\\"Get tea\\\"}]}' is not assignable to type: Store, expected an instance of Store or a snapshot like '{ todos: { title: string; done: boolean }[] }' instead.\n```\n\n_Runtime type error_\n\n![typescript error](/img/tserror.png)\n\n_Designtime type error_\n\nBecause state trees are living, mutable models, actions are straight-forward to write; just modify local instance properties where appropriate. See the `toggle()`-action in the Todo-store above or the examples below. It is not necessary to produce a new state tree yourself, MST's snapshot functionality will derive one for you automatically.\n\nAlthough mutable sounds scary to some, fear not, actions have many interesting properties.\nBy default trees can only be modified by using an action that belongs to the same subtree.\nFurthermore, actions are replayable and can be used to distribute changes ([example](https://github.com/coolsoftwaretyler/mst-example-boxes/blob/main/src/stores/time.js)).\n\nMoreover, because changes can be detected on a fine grained level, JSON patches are supported out of the box.\nSimply subscribing to the patch stream of a tree is another way to sync diffs with, for example, back-end servers or other clients ([example](https://github.com/coolsoftwaretyler/mst-example-boxes/blob/main/src/stores/socket.js)).\n\n![patches](/img/patches.png)\n\nSince MST uses MobX behind the scenes, it integrates seamlessly with [mobx](https://mobx.js.org) and [mobx-react-lite (or mobx-react)](https://mobx.js.org/react-integration.html). See also this [egghead.io lesson: Render mobx-state-tree Models in React](https://egghead.io/lessons/react-render-mobx-state-tree-models-in-react).\nEven cooler, because it supports snapshots, middleware and replayable actions out of the box, it is possible to replace a Redux store and reducer with a MobX state tree.\nThis makes it possible to connect the Redux devtools to MST. See the [Redux / MST TodoMVC example](https://github.com/coolsoftwaretyler/mst-example-redux-todomvc/blob/main/src/index.js#L6).\n\n---\n\nFor futher reading: the conceptual difference between snapshots, patches and actions in relation to distributing state changes is extensively discussed in this [blog post](https://medium.com/@mweststrate/distributing-state-changes-using-snapshots-patches-and-actions-part-1-2811a2fcd65f)\n\n![devtools](/img/reduxdevtools.png)\n\nFinally, MST has built-in support for references, identifiers, dependency injection, change recording and circular type definitions (even across files).\nEven fancier, it analyses liveliness of objects, failing early when you try to access accidentally cached information! (More on that later)\n\nA unique feature of MST is that it offers liveliness guarantees. MST will throw an exception when reading or writing from objects that are no longer part of a state tree. This protects you against accidental stale reads of objects still referred by, for example, a closure.\n\n```javascript\nconst oldTodo = store.todos[0]\nstore.removeTodo(0)\n\nfunction logTodo(todo) {\n    setTimeout(() => console.log(todo.title), 1000)\n}\n\nlogTodo(store.todos[0])\nstore.removeTodo(0)\n// throws exception in one second for using an stale object!\n```\n\nDespite all that, you will see that in practice the API is quite straightforward!\n\n---\n\nAnother way to look at mobx-state-tree is to consider it, as argued by Daniel Earwicker, to be [\"React, but for data\"](http://danielearwicker.github.io/json_mobx_Like_React_but_for_Data_Part_2.html).\nLike React, MST consists of composable components, called _models_, which captures a small piece of state. They are instantiated from props (snapshots) and after that manage and protect their own internal state (using actions). Moreover, when applying snapshots, tree nodes are reconciled as much as possible. There is even a context-like mechanism, called environments, to pass information to deep descendants.\n\nAn introduction to the philosophy can be watched [here](https://youtu.be/ta8QKmNRXZM?t=21m52s). [Slides](https://immer-mutable-state.surge.sh/). Or, as [markdown](https://github.com/mweststrate/reactive2016-slides/blob/master/slides.md) to read it quickly.\n\nmobx-state-tree \"immutable trees\" and \"graph model\" features talk, [\"Next Generation State Management\"](https://www.youtube.com/watch?v=rwqwwn_46kA) at React Europe 2017. [Slides](http://tree.surge.sh/#1).\n"
  },
  {
    "path": "docs/intro/welcome.md",
    "content": "---\nid: welcome\ntitle: Welcome to MobX-State-Tree!\n---\n\n<div id=\"codefund\"></div>\n\n**_Full-featured reactive state management without the boilerplate._**\n\n## What is MobX-State-Tree?\n\nMobX-State-Tree (MST) is a [batteries included](<https://en.wikipedia.org/wiki/Batteries_Included#:~:text=%22Batteries%20included%22%20(slang)%2C%20in%20a%20product%20usability%20(mostly%20in%20software)%20it%20states%20that%20the%20product%20comes%20together%20with%20all%20possible%20parts%20required%20for%20full%20usability>) state management library. It only requires **one peer dependency**, and will provide you with:\n\n1. **Centralized stores** for your data\n2. **Mutable, but protected data**, which means it is easy to work with your data, but safe to modify.\n3. **Serializable and traceable updates**. The mutable, protected nature of MobX-State-Tree data means you can **generate snapshots** and do **time-travel debugging**.\n4. **Side effect management**, so you don't need to write `useEffect` hooks or their equivalent to manage the consequences of data mutations. You can do it all from MST itself.\n5. **Runtime type checking**, so you can't accidentally assign the wrong data type to a property\n6. **Static type checking** with TypeScript inference from your runtime types - automatically!\n7. **Data normalization** - MST has support for references, so you can normalize data across your application code.\n8. **Warm, welcoming community**. We pride ourselves on a healthy and kind open source community.\n\n## Basic Example\n\nHere's what MST code looks like:\n\n_You can play with it in [this CodeSandbox playground](https://codesandbox.io/s/boring-pond-cmooq?file=/src/index.js)._\n\n```javascript\nimport { t, onSnapshot } from \"mobx-state-tree\"\n\n// A tweet has a body (which is text) and whether it's read or not\nconst Tweet = t\n  .model(\"Tweet\", {\n    body: t.string,\n    read: false // automatically inferred as type \"boolean\" with default \"false\"\n  })\n  .actions((tweet) => ({\n    toggle() {\n      tweet.read = !tweet.read\n    }\n  }))\n\n// Define the Twitter \"store\" as having an array of tweets\nconst TwitterStore = t.model(\"TwitterStore\", {\n  tweets: t.array(Tweet)\n})\n\n// create your new Twitter store instance with some initial data\nconst twitterStore = TwitterStore.create({\n  tweets: [\n    {\n      body: \"Anyone tried MST?\"\n    }\n  ]\n})\n\n// Listen to new snapshots, which are created anytime something changes\nonSnapshot(twitterStore, (snapshot) => {\n  console.log(snapshot)\n})\n\n// Let's mark the first tweet as \"read\" by invoking the \"toggle\" action\ntwitterStore.tweets[0].toggle()\n\n// In the console, you should see the result: `{ tweets: [{ body: \"Anyone tried MST?\", read: true }]}`\n```\n\n## Video Demonstration\n\nJamon Holmgren has an excellent introduction video with a more realistic, robust example of MobX-State-Tree and React. Check it out!\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/n_VjjJxyd8Q?si=RxMDaUi7ExERZQsx\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen></iframe>\n\n## MobX Ecosystem\n\n[MobX](https://github.com/mobxjs/mobx) is [one of the most popular Redux alternatives](https://2019.stateofjs.com/data-layer/mobx/) and is used (along with MobX-State-Tree) by companies all over the world, including Netflix, Grow, IBM, DAZN, Baidu, and more.\n\nIf you're wondering how MobX-State-Tree is distinct from MobX, you can think of it like this: **MobX is a state management \"engine\", and MobX-State-Tree is a luxury car**. MST gives you the structure, tools, and other features to get you where you're going. MST is valuable in a large team but also useful in smaller applications when you expect your code to scale rapidly. And if we compare it to Redux, MST offers better performance with much less boilerplate code than Redux!\n\nSince MST uses MobX under the hood, MobX-State-Tree works with the MobX bindings for React, React Native, Vue, Angular, Svelte, and even barebones JavaScript apps.\n\n_You don't need to know how to use MobX in order to use MST._ Just like you don't need to know how your car's engine works to be an excellent driver. It can help, but it's not necessary.\n\n## Next Steps\n\n- Learn how to [install MobX-State-Tree](./installation.md) or jump straight to our [Getting Started](./getting-started.md) guide!\n- View [examples](./examples.md) here.\n- If you're interested in the philosophy behind MobX-State-Tree and a lot more explanation of features and benefits, check out the [Philosophy](./philosophy.md) page.\n- Or check out a talk or two on our [Resources](./../tips/resources.md) page\n"
  },
  {
    "path": "docs/overview/hooks.md",
    "content": "---\nid: hooks\ntitle: Lifecycle hooks overview\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 14: Loading Data from the Server after model creation</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-loading-data-from-the-server/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-loading-data-from-the-server\">Hosted on egghead.io</a>\n</details>\n\n`mobx-state-tree` supports passing a variety of hooks that are called throughout a node's lifecycle. Hooks are passes as actions with the name of the hook, like:\n\n```javascript\nconst Todo = types.model(\"Todo\", { done: true }).actions((self) => ({\n  afterCreate() {\n    console.log(\"Created a new todo!\")\n  }\n}))\n```\n\n| Hook                  | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `afterCreate`         | Immediately after an instance is created and initial values are applied. Children will fire this event before parents. You can't make assumptions about the parent safely, use `afterAttach` if you need to.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\n| `afterAttach`         | As soon as the _direct_ parent is assigned (this node is attached to another node). If an element is created as part of a parent, `afterAttach` is also fired. Unlike `afterCreate`, `afterAttach` will fire breadth first. So, in `afterAttach` one can safely make assumptions about the parent, but in `afterCreate` not                                                                                                                                                                                                                                                                                                                                                                 |\n| `beforeDetach`        | As soon as the node is removed from the _direct_ parent, but only if the node is _not_ destroyed. In other words, when `detach(node)` is used                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\n| `beforeDestroy`       | Called before the node is destroyed, as a result of calling `destroy`, or by removing or replacing the node from the tree. Child destructors will fire before parents                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| `preProcessSnapshot`  | Deprecated, prefer `types.snapshotProcessor`. Before creating an instance or applying a snapshot to an existing instance, this hook is called to give the option to transform the snapshot before it is applied. The hook should be a _pure_ function that returns a new snapshot. This can be useful to do some data conversion, enrichment, property renames, etc. This hook is not called for individual property updates. _\\*\\*Note 1: Unlike the other hooks, this one is \\_not_ created as part of the `actions` initializer, but directly on the type!**\\_ \\_**Note 2: The `preProcessSnapshot` transformation must be pure; it should not modify its original input argument!\\*\\*\\_ |\n| `postProcessSnapshot` | Deprecated, prefer `types.snapshotProcessor`. This hook is called every time a new snapshot is being generated. Typically it is the inverse function of `preProcessSnapshot`. This function should be a pure function that returns a new snapshot. _\\*\\*Note: Unlike the other hooks, this one is \\_not_ created as part of the `actions` initializer, but directly on the type!\\*\\*\\_                                                                                                                                                                                                                                                                                                      |\n\nAll hooks can be defined multiple times and can be composed automatically.\n\n## Lifecycle hooks for `types.array`/`types.map`\n\nHooks for `types.array`/`types.map` can be defined by using the `.hooks(self => ({}))` method.\n\nCalling `.hooks(...)` produces new type, same as calling `.actions()` for `types.model`.\n\nAvailable hooks are:\n\n| Hook            | Meaning                                                                                                                                                                                                                                                                                                                     |\n| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `afterCreate`   | Immediately after an instance is initialized: right after `.create()` for root node or after the first access for the nested one. Children will fire this event before parents. You can't make assumptions about the parent safely, use `afterAttach` if you need to.                                                       |\n| `afterAttach`   | As soon as the _direct_ parent is assigned (this node is attached to another node). If an element is created as part of a parent, `afterAttach` is also fired. Unlike `afterCreate`, `afterAttach` will fire breadth first. So, in `afterAttach` one can safely make assumptions about the parent, but in `afterCreate` not |\n| `beforeDetach`  | As soon as the node is removed from the _direct_ parent, but only if the node is _not_ destroyed. In other words, when `detach(node)` is used                                                                                                                                                                               |\n| `beforeDestroy` | Called before the node is destroyed, as a result of calling `destroy`, or by removing or replacing the node from the tree. Child destructors will fire before parents                                                                                                                                                       |\n\n### Snapshot processing hooks\n\nYou can also modify snapshots as they are generated from your nodes, or applied to your nodes with `types.snapshotProcessor`. This type wraps an existing type and allows defining custom hooks for snapshot modifications.\n\nFor example, you can wrap an existing model in a snapshot processor which transforms a snapshot from the server into the shape your model expects with `preProcess`:\n\n```javascript\nconst TodoModel = types.model(\"Todo\", {\n  done: types.boolean,\n});\n\nconst Todo = types.snapshotProcessor(TodoModel, {\n  preProcessor(snapshot) {\n    return {\n        // auto convert strings to booleans as part of preprocessing\n        done: snapshot.done === \"true\" ? true : snapshot.done === \"false\" ? false : snapshot.done\n    }\n});\n\nconst todo = Todo.create({ done: \"true\" }) // snapshot will be transformed on the way in\n```\n\nSnapshots can also be transformed from the base shape generated by `mobx-quick-tree` using the `postProcess` hook. For example, we can format a date object in the snapshot with a specific date format that a backend might accept:\n\n```javascript\nconst TodoModel = types.model(\"Todo\", {\n  done: types.boolean,\n  createdAt: types.Date\n});\n\nconst Todo = types.snapshotProcessor(TodoModel, {\n  postProcessor(snapshot, node) {\n    return {\n        ...snapshot,\n        createdAt: node.createdAt.getTime()\n    }\n});\n\nconst todo = Todo.create({done: true, createdAt: new Date()});\nconst snapshot = getSnapshot(todo);\n// { done: true, createdAt: 1699504649386 }\n```\n\n| Hook                                  | Meaning                                                                                                                                                                                                                                                                                                                                                                |\n| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `preProcessor(inputSnapshot)`         | Transform a snapshot before it is applied to a node. The output snapshot must be valid for application to the wrapped type. The `preProcess` hook is passed the input snapshot, but not passed the node, as it is not done being constructed yet, and not attached to the tree. If you need to modify the node in the context of the tree, use the `afterCreate` hook. |\n| `postProcessor(outputSnapshot, node)` | Transform a snapshot after it has been generated from a node. The transformed value will be returned by `getSnapshot`. The `postProcess` hook is passed the initial outputSnapshot, as well as the instance object the snapshot has been generated for. It is safe to access properties of the node or other nodes when post processing snapshots.                     |\n\n#### When to use snapshot hooks\n\n`preProcess` and `postProcess` hooks should be used to convert your data into types that are more acceptable to MST. Snapshots are often JSON serialized, so if you need to use richly typed objects like `URL`s or `Date`s that can't be JSON serialized, you can use snapshot processors to convert to and from the serialized form.\n\nTypically, it should be the case that `postProcessor(preProcessor(snapshot)) === snapshot`. If your snapshot processor hooks are non-deterministic, or rely on state beyond just the base snapshot, it's easy to introduce subtle bugs and is best avoided.\n\nIf you are considering adding a snapshot processor that is non-deterministic or relies on other state, consider using a dedicated property or view that produces the same information. Like snapshots, properties and views are observable and memoized, but they don't need to have an inverse for serializing back to a snapshot.\n\nFor example, if you want to capture the current time a snapshot was generated, you may be tempted to use a snapshot processor:\n\n```javascript\nconst TodoModel = types.model(\"Todo\", {\n  done: types.boolean,\n});\n\nconst Todo = types.snapshotProcessor(TodoModel, {\n  // discouraged, try not to do this\n  postProcessor(snapshot, node) {\n    return {\n        ...snapshot,\n        createdAt: new Date().toISOString();\n    }\n});\n\nconst todo = Todo.create({ done: false })\ngetSnapshot(todo) // will have a `createdAt property`\n```\n\nInstead, this data could be better represented as a property right on the model, which is included in the snapshot by default:\n\n```javascript\nconst Todo = types.model(\"Todo\", {\n  done: types.boolean,\n  createdAt: types.optional(types.Date, () => new Date())\n});\n\nconst todo = Todo.create({ done: false })\ngetSnapshot(todo) // will also have a `createdAt property`\n```\n\nAdvanced use-cases that require impure or otherwise inconsistent snapshot processors are however supported by MST.\n"
  },
  {
    "path": "docs/overview/types.md",
    "content": "---\nid: types\ntitle: Types overview\n---\n\n<div id=\"codefund\"></div>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 11: More mobx-state-tree Types: map, literal, union, and enumeration</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-more-mobx-state-tree-types-map-literal-union-and-enumeration/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-more-mobx-state-tree-types-map-literal-union-and-enumeration\">Hosted on egghead.io</a>\n</details>\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 17: Create Dynamic Types and use Type Composition to Extract Common Functionality</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-create-dynamic-types-and-use-type-composition-to-extract-common-functionality/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-create-dynamic-types-and-use-type-composition-to-extract-common-functionality\">Hosted on egghead.io</a>\n</details>\n\nThese are the types available in MST. All types can be found in the `types` namespace, e.g. `types.string`.\n\n## Complex types\n\n-   [`types.model(properties, actions)`](/API/#model) Defines a \"class like\" type with properties and actions to operate on the object.\n-   [`types.array(type)`](/API/#array) Declares an array of the specified type.\n-   [`types.map(type)`](/API/#map) Declares a map of the specified type.\n\nNote that since MST v3 `types.array` and `types.map` are wrapped in `types.optional` by default, with `[]` and `{}` set as their default values, respectively.\n\n## Primitive types\n\n-   [`types.string`](/API/#const-string)\n-   [`types.number`](/API/#const-number)\n-   [`types.integer`](/API/#const-integer)\n-   [`types.float`](/API/#const-float)\n-   [`types.finite`](/API/#const-finite)\n-   [`types.boolean`](/API/#const-boolean)\n-   [`types.Date`](/API/#const-dateprimitive)\n-   [`types.custom`](/API/#custom) creates a custom primitive type. This is useful to define your own types that map a serialized form one-to-one to an immutable object like a Decimal or Date.\n\n## Utility types\n\n-   [`types.union(options?: { dispatcher?: (snapshot) => Type, eager?: boolean }, types...)`](/API/#union) create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function to determine the type. When `eager` flag is set to `true` (default) - the first matching type will be used, if set to `false` the type check will pass only if exactly 1 type matches.\n-   [`types.optional(type, defaultValue, optionalValues?)`](/API/#optional) marks a value as being optional (in e.g. a model). If a value is not provided/`undefined` (or set to any of the primitive values passed as an optional `optionalValues` array) the `defaultValue` will be used instead. If `defaultValue` is a function, it will be evaluated. This can be used to generate, for example, IDs or timestamps upon creation.\n-   [`types.literal(value)`](/API/#literal) can be used to create a literal type, where the only possible value is specifically that value. This is very powerful in combination with `union`s. E.g. `temperature: types.union(types.literal(\"hot\"), types.literal(\"cold\"))`.\n-   [`types.enumeration(name?, options: string[])`](/API/#enumeration) creates an enumeration. This method is a shorthand for a union of string literals. If you are using typescript and want to create a type based on an string enum (e.g. `enum Color { ... }`) then use `types.enumeration<Color>(\"Color\", Object.values(Color))`, where the `\"Color\"` name argument is optional.\n-   [`types.refinement(name?, baseType, (snapshot) => boolean)`](/API/#refinement) creates a type that is more specific than the base type, e.g. `types.refinement(types.string, value => value.length > 5)` to create a type of strings that can only be longer than 5.\n-   [`types.maybe(type)`](/API/#maybe) makes a type optional and nullable. The value `undefined` will be used to represent nullability. Shorthand for `types.optional(types.union(type, types.literal(undefined)), undefined)`.\n-   [`types.maybeNull(type)`](/API/#maybenull) like `maybe`, but uses `null` to represent the absence of a value.\n-   [`types.null`](/API/#const-nulltype) the type of `null`.\n-   [`types.undefined`](/API/#const-undefinedtype) the type of `undefined`.\n-   [`types.late(() => type)`](/API/#late) can be used to create recursive or circular types, or types that are spread over files in such a way that circular dependencies between files would be an issue otherwise.\n-   [`types.frozen(subType? | defaultValue?)`](/API/#frozen) Accepts any kind of serializable value (both primitive and complex), but assumes that the value itself is **immutable** and **serializable**.\n    `frozen` can be invoked in a few different ways:\n    -   `types.frozen()` - behaves the same as types.frozen in MST 2.\n    -   `types.frozen(subType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type. Note that the type will not actually be instantiated, so it can only be used to check the shape of the data. Adding views or actions to SubType would be pointless.\n    -   `types.frozen(someDefaultValue)` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field\n    -   (Typescript) `types.frozen<TypeScriptType>(...)` - provide a typescript type, to help in strongly typing the field (design time only)\n-   [`types.compose(name?, type1...typeX)`](/API/#compose), creates a new model type by taking a bunch of existing types and combining them into a new one.\n-   [`types.reference(targetType)`](/API/#reference) creates a property that is a reference to another item of the given `targetType` somewhere in the same tree. See [references](/concepts/references#references) for more details.\n-   [`types.safeReference(targetType)`](/API/#safereference) is like a standard reference, except that it accepts the undefined value by default and automatically sets itself to undefined (when the parent is a model) / removes itself from arrays and maps when the reference it is pointing to gets detached/destroyed. See [references](/concepts/references#references) for more details.\n-   [`types.snapshotProcessor(type, processors, name?)`](/API/#snapshotprocessor) runs a pre snapshot / post snapshot processor before/after serializing a given type. [See known issue with `applySnapshot` and `preProcessSnapshot`](https://github.com/mobxjs/mobx-state-tree/issues/1317) Example:\n    ```ts\n    const Todo1 = types.model({ text: types.string })\n    // in the backend the text type must be null when empty\n    interface BackendTodo {\n        text: string | null\n    }\n    const Todo2 = types.snapshotProcessor(Todo1, {\n        // from snapshot to instance\n        preProcessor(sn: BackendTodo) {\n            return {\n                text: sn.text || \"\";\n            }\n        },\n        // from instance to snapshot\n        postProcessor(sn): BackendTodo {\n            return {\n                text: !sn.text ? null : sn.text\n            }\n        }\n    })\n    ```\n\n## Property types\n\nProperty types can only be used as a direct member of a `types.model` type and not further composed (for now).\n\n-   [`types.identifier`](/API/#const-identifier) Only one such member can exist in a `types.model` and should uniquely identify the object. See [identifiers](/concepts/references#identifiers) for more details.\n-   [`types.identifierNumber`](/API/#const-identifiernumber) Similar to `types.identifier`. However, during serialization, the identifier value will be parsed from / serialized to a number.\n"
  },
  {
    "path": "docs/overview/utilties.md",
    "content": "---\nid: api\ntitle: API overview\n---\n\n<div id=\"codefund\"></div>\n\nSee the [TypeDocs](/API/) for full details and typings.\n\n| signature                                                                                                             |                                                                                                                                                                                                                                                       |\n| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [`addDisposer(node, () => void)`](/API/#adddisposer)                                                     | Add a function to be invoked whenever the target node is about to be destroyed                                                                                                                                                                        |\n| [`addMiddleware(node, middleware: (actionDescription, next) => any, includeHooks)`](/API/#addmiddleware) | Attaches middleware to a node. See [middleware](../concepts/middleware). Returns disposer.                                                                                                                                                                |\n| [`applyAction(node, actionDescription)`](/API/#applyaction)                                              | Replays an action on the targeted node                                                                                                                                                                                                                |\n| [`applyPatch(node, jsonPatch)`](/API/#applypatch)                                                        | Applies a JSON patch, or array of patches, to a node in the tree                                                                                                                                                                                      |\n| [`applySnapshot(node, snapshot)`](/API/#applysnapshot)                                                   | Updates a node with the given snapshot                                                                                                                                                                                                                |\n| [`cast(nodeOrSnapshot)`](/API/#cast)                                                                     | Cast a node instance or snapshot to a node instance so it can be used in assignment operations                                                                                                                                                        |\n| [`castToSnapshot(nodeOrSnapshot)`](/API/#casttosnapshot)                                                 | Cast a node instance to a snapshot so it can be used inside create operations                                                                                                                                                                         |\n| [`castToReferenceSnapshot(node)`](/API/#casttoreferencesnapshot)                                         | Cast a node instance to a reference snapshot so it can be used inside create operations                                                                                                                                                               |\n| [`createActionTrackingMiddleware`](/API/#createactiontrackingmiddleware)                                 | Utility to make writing middleware that tracks async actions less cumbersome. Consider migrating to `createActionTrackingMiddleware2`                                                                                                                 |\n| [`createActionTrackingMiddleware2`](/API/#createactiontrackingmiddleware)                                | Utility to make writing middleware that tracks async actions less cumbersome                                                                                                                                                                          |\n| [`clone(node, keepEnvironment?: boolean / newEnvironment)`](/API/#clone)                          | Creates a full clone of the given node. By default preserves the same environment                                                                                                                                                                     |\n| [`decorate(handler, function)`](/API/#decorate)                                                          | Attaches middleware to a specific action (or flow)                                                                                                                                                                                                    |\n| [`destroy(node)`](/API/#destroy)                                                                         | Kills `node`, making it unusable. Removes it from any parent in the process                                                                                                                                                                           |\n| [`detach(node)`](/API/#detach)                                                                           | Removes `node` from its current parent, and lets it live on as standalone tree                                                                                                                                                                        |\n| [`flow(generator)`](/API/#flow)                                                                          | Creates an asynchronous flow based on a generator function                                                                                                                                                                                            |\n| [`castFlowReturn(value)`](/API/#castflowreturn)                                                          | Casts a flow return value so it can be correctly inferred as return type. Only needed when using TypeScript and when returning a Promise.                                                                                                             |\n| [`getChildType(node, property?)`](/API/#getchildtype)                                                    | Returns the declared type of the given `property` of `node`. For arrays and maps `property` can be omitted as they all have the same type                                                                                                             |\n| [`getEnv(node)`](/API/#getenv)                                                                           | Returns the environment of `node`, see [dependency injection](../concepts/dependency-injection)                                                                                                                                                                                  |\n| [`getParent(node, depth=1)`](/API/#getparent)                                                            | Returns the intermediate parent of the `node`, or a higher one if `depth > 1`                                                                                                                                                                         |\n| [`getParentOfType(node, type)`](/API/#getparentoftype)                                                   | Return the first parent that satisfies the provided type                                                                                                                                                                                              |\n| [`getPath(node)`](/API/#getpath)                                                                         | Returns the path of `node` in the tree                                                                                                                                                                                                                |\n| [`getPathParts(node)`](/API/#getpathparts)                                                               | Returns the path of `node` in the tree, unescaped as separate parts                                                                                                                                                                                   |\n| [`getRelativePath(base, target)`](/API/#getrelativepath)                                                 | Returns the short path, which one could use to walk from node `base` to node `target`, assuming they are in the same tree. Up is represented as `../`                                                                                                 |\n| [`getRoot(node)`](/API/#getroot)                                                                         | Returns the root element of the tree containing `node`                                                                                                                                                                                                |\n| [`getIdentifier(node)`](/API/#getidentifier)                                                             | Returns the identifier of the given element                                                                                                                                                                                                           |\n| [`getNodeId(node)`](/API/#getnodeid)                                                                     | Returns the unique node id (not to be confused with the instance identifier) for a given instance                                                                                                                                                     |\n| [`getSnapshot(node, applyPostProcess)`](/API/#getsnapshot)                                               | Returns the snapshot of the `node`. See [snapshots](../concepts/snapshots)                                                                                                                                                                                       |\n| [`getType(node)`](/API/#gettype)                                                                         | Returns the type of `node`                                                                                                                                                                                                                            |\n| [`hasParent(node, depth=1)`](/API/#hasparent)                                                            | Returns `true` if `node` has a parent at `depth`                                                                                                                                                                                                      |\n| [`hasParentOfType(node, type)`](/API/#hasparentoftype)                                                   | Returns `true` if the `node` has a parent that satisfies the provided type                                                                                                                                                                            |\n| [`isAlive(node)`](/API/#isalive)                                                                         | Returns `true` if `node` is alive                                                                                                                                                                                                                     |\n| [`isStateTreeNode(value)`](/API/#isstatetreenode)                                                        | Returns `true` if `value` is a node of a mobx-state-tree                                                                                                                                                                                              |\n| [`isProtected(value)`](/API/#isprotected)                                                                | Returns `true` if the given node is protected, see [actions](../concepts/actions)                                                                                                                                                                                |\n| [`isValidReference(() => node / null / undefined, checkIfAlive = true)`](/API/#isvalidreference)       | Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns if the check passes or not.                                                                                                                          |\n| [`isRoot(node)`](/API/#isroot)                                                                           | Returns true if `node` has no parents                                                                                                                                                                                                                 |\n| [`joinJsonPath(parts)`](/API/#joinjsonpath)                                                              | Joins and escapes the given path `parts` into a JSON path                                                                                                                                                                                             |\n| [`onAction(node, (actionDescription) => void)`](/API/#onaction)                                          | A built-in middleware that calls the provided callback with an action description upon each invocation. Returns disposer                                                                                                                              |\n| [`onPatch(node, (patch) => void)`](/API/#onpatch)                                                        | Attach a JSONPatch listener, that is invoked for each change in the tree. Returns disposer                                                                                                                                                            |\n| [`onSnapshot(node, (snapshot) => void)`](/API/#onsnapshot)                                               | Attach a snapshot listener, that is invoked for each change in the tree. Returns disposer                                                                                                                                                             |\n| [`process(generator)`](/API/#process)                                                                    | `DEPRECATED` – replaced with [flow](/API/#flow)                                                                                                                                                                                          |\n| [`protect(node)`](/API/#protect)                                                                         | Protects an unprotected tree against modifications from outside actions                                                                                                                                                                               |\n| [`recordActions(node)`](/API/#recordactions)                                                             | Creates a recorder that listens to all actions in `node`. Call `.stop()` on the recorder to stop this, and `.replay(target)` to replay the recorded actions on another tree                                                                           |\n| [`recordPatches(node)`](/API/#recordpatches)                                                             | Creates a recorder that listens to all patches emitted by the node. Call `.stop()` on the recorder to stop this, and `.replay(target)` to replay the recorded patches on another tree                                                                 |\n| [`getMembers(node)`](/API/#getMembers)                                                                   | Returns the model name, properties, actions, views, volatiles of a model node instance                                                                                                                                                                |\n| [`getPropertyMembers(typeOrNode)`](/API/#getPropertyMembers)                                             | Returns the model name and properties of a model type for either a model type or a model node                                                                                                                                                         |\n| [`resolve(node, path)`](/API/#resolve)                                                                   | Resolves a `path` (json path) relatively to the given `node`                                                                                                                                                                                          |\n| [`resolveIdentifier(type, target, identifier)`](/API/#resolveidentifier)                                 | resolves an identifier of a given type in a model tree                                                                                                                                                                                                |\n| [`resolvePath(target, path)`](/API/#resolvepath)                                                         | resolves a JSON path, starting at the specified target                                                                                                                                                                                                |\n| [`setLivelinessChecking(\"warn\" / \"ignore\" / \"error\")`](/API/#setlivelinesschecking)                    | Defines what MST should do when running into reads / writes to objects that have died. By default it will print a warning. Use te `\"error\"` option to easy debugging to see where the error was thrown and when the offending read / write took place |\n| [`getLivelinessChecking()`](/API/#getlivelinesschecking)                                                 | Returns the current liveliness checking mode.                                                                                                                                                                                                         |\n| [`splitJsonPath(path)`](/API/#splitjsonpath)                                                             | Splits and unescapes the given JSON `path` into path parts                                                                                                                                                                                            |\n| [`typecheck(type, value)`](/API/#typecheck)                                                              | Typechecks a value against a type. Throws on errors. Use this if you need typechecks even in a production build. NOTE: set process.env.ENABLE_TYPE_CHECK = \"true\" if you want to enable type checking in any environment                                                                                                                                      |\n| [`tryResolve(node, path)`](/API/#tryresolve)                                                             | Like `resolve`, but just returns `null` if resolving fails at any point in the path                                                                                                                                                                   |\n| [`tryReference(() => node / null / undefined, checkIfAlive = true)`](/API/#tryreference)                 | Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if it the check passes, else it returns undefined.                                                                                    |\n| [`unprotect(node)`](/API/#unprotect)                                                                     | Unprotects `node`, making it possible to directly modify any value in the subtree, without actions                                                                                                                                                    |\n| [`walk(startNode, (node) => void)`](/API/#walk)                                                          | Performs a depth-first walk through a tree                                                                                                                                                                                                            |\n| [`escapeJsonPath(path)`](/API/#escapejsonpath)                                                           | escape special characters in an identifier, according to http://tools.ietf.org/html/rfc6901                                                                                                                                                           |\n| [`unescapeJsonPath(path)`](/API/#unescapejsonpath)                                                       | escape special characters in an identifier, according to http://tools.ietf.org/html/rfc6901                                                                                                                                                           |\n| [`isType(value)`](/API/#isType)                                                                          | Returns if a given value represents a type.                                                                                                                                                                                                           |\n| [`isArrayType(value)`](/API/#isArrayType)                                                                | Returns if a given value represents an array type.                                                                                                                                                                                                    |\n| [`isFrozenType(value)`](/API/#isFrozenType)                                                              | Returns if a given value represents a frozen type.                                                                                                                                                                                                    |\n| [`isIdentifierType(value)`](/API/#isIdentifierType)                                                      | Returns if a given value represents an identifier type.                                                                                                                                                                                               |\n| [`isLateType(value)`](/API/#isLateType)                                                                  | Returns if a given value represents a late type.                                                                                                                                                                                                      |\n| [`isLiteralType(value)`](/API/#isLiteralType)                                                            | Returns if a given value represents a literal type.                                                                                                                                                                                                   |\n| [`isMapType(value)`](/API/#isMapType)                                                                    | Returns if a given value represents a map type.                                                                                                                                                                                                       |\n| [`isModelType(value)`](/API/#isModelType)                                                                | Returns if a given value represents a model type.                                                                                                                                                                                                     |\n| [`isOptionalType(value)`](/API/#isOptionalType)                                                          | Returns if a given value represents an optional type.                                                                                                                                                                                                 |\n| [`isPrimitiveType(value)`](/API/#isPrimitiveType)                                                        | Returns if a given value represents a primitive type.                                                                                                                                                                                                 |\n| [`isReferenceType(value)`](/API/#isReferenceType)                                                        | Returns if a given value represents a reference type.                                                                                                                                                                                                 |\n| [`isRefinementType(value)`](/API/#isRefinementType)                                                      | Returns if a given value represents a refinement type.                                                                                                                                                                                                |\n| [`isUnionType(value)`](/API/#isUnionType)                                                                | Returns if a given value represents a union type.                                                                                                                                                                                                     |\n| [`getRunningActionContext()`](/API/#getrunningactioncontext)                                             | Returns the currently executing MST action context, or undefined if none.                                                                                                                                                                             |\n| [`isActionContextChildOf(actionContext, parent)`](/API/#isActionContextChildOf)                          | Returns if the given action context is a parent of this action context.                                                                                                                                                                               |\n| [`isActionContextThisOrChildOf(actionContext, parentOrSame)`](/API/#isActionContextThisOrChildOf)        | Returns if the given action context is this or a parent of this action context.                                                                                                                                                                       |\n\nA _disposer_ is a function that cancels the effect for which it was created.\n\n"
  },
  {
    "path": "docs/recipes/auto-generated-property-setter-actions.md",
    "content": "---\nid: auto-generated-property-setter-actions\ntitle: Auto-Generated Property Setter Actions\n---\n\nThis recipe was [originally developed by Infinite Red](https://shift.infinite.red/a-mobx-state-tree-shortcut-for-setter-actions-ac88353df060).\n\nIf you want to modify your MobX-State-Tree model properties, you usually have to write one setter per-property. In a model with two fields, that looks like this:\n\n```ts\nimport { types } from \"mobx-state-tree\"\n\nconst UserModel = types\n  .model(\"User\", {\n    name: types.string,\n    age: types.number\n  })\n  .actions((self) => ({\n    setName(newName: string) {\n      self.name = newName\n    },\n    setAge(newAge: number) {\n      self.age = newAge\n    }\n  }))\n```\n\nAs your model grows in size and complexity, these setter actions can be tedious to write, and increase your source file size, making it harder to read through the actual logic of your model.\n\nYou can write a generic action in your model, like this:\n\n```ts\nimport { types, SnapshotIn } from \"mobx-state-tree\"\nconst UserModel = types\n  .model(\"User\", {\n    name: types.string,\n    age: types.number\n  })\n  .actions((self) => ({\n    setProp<K extends keyof SnapshotIn<typeof self>, V extends SnapshotIn<typeof self>[K]>(\n      field: K,\n      newValue: V\n    ) {\n      self[field] = newValue\n    }\n  }))\nconst user = UserModel.create({ name: \"Jamon\", age: 40 })\nuser.setProp(\"name\", \"Joe\") // all good!\n// typescript will error, like it's supposed to\nuser.setProp(\"age\", \"shouldn't work\")\n```\n\nOr, if you want to extract that for easier reuse across different models, you can write a helper, like this:\n\n```ts\nimport { IStateTreeNode, SnapshotIn } from \"mobx-state-tree\"\n\n// This custom type helps TS know what properties can be modified by our returned function. It excludes actions and views, but still correctly infers model properties for auto-complete and type safety.\ntype OnlyProperties<T> = {\n  [K in keyof SnapshotIn<T>]: K extends keyof T ? T[K] : never\n}\n\n/**\n * If you include this in your model in an action() block just under your props,\n * it'll allow you to set property values directly while retaining type safety\n * and also is executed in an action. This is useful because often you find yourself\n * making a lot of repetitive setter actions that only update one prop.\n *\n * E.g.:\n *\n *  const UserModel = types.model(\"User\")\n *    .props({\n *      name: types.string,\n *      age: types.number\n *    })\n *    .actions(withSetPropAction)\n *\n *   const user = UserModel.create({ name: \"Jamon\", age: 40 })\n *\n *   user.setProp(\"name\", \"John\") // no type error\n *   user.setProp(\"age\", 30)      // no type error\n *   user.setProp(\"age\", \"30\")    // type error -- must be number\n */\nexport const withSetPropAction = <T extends IStateTreeNode>(mstInstance: T) => ({\n  setProp<K extends keyof OnlyProperties<T>, V extends SnapshotIn<T>[K]>(field: K, newValue: V) {\n    ;(mstInstance as T & OnlyProperties<T>)[field] = newValue\n  }\n})\n```\n\nYou can use the helper in a model like so:\n\n```ts\nimport { t } from \"mobx-state-tree\"\nimport { withSetPropAction } from \"./withSetPropAction\"\n\nconst Person = t\n  .model(\"Person\", {\n    name: t.string\n  })\n  .views((self) => ({\n    get lowercaseName() {\n      return self.name.toLowerCase()\n    }\n  }))\n  .actions((self) => ({\n    setName(name: string) {\n      self.name = name\n    }\n  }))\n  .actions(withSetPropAction)\n\nconst you = Person.create({\n  name: \"your name\"\n})\n\nyou.setProp(\"name\", \"Another Name\")\n\n// These will all trigger runtime errors. They are included to demonstrate TS support for\n// withSetPropAction.\ntry {\n  // @ts-expect-error - this should error because it's the wrong type for name.\n  you.setProp(\"name\", 123)\n  // @ts-expect-error - this should error since 'nah' is not a property.\n  you.setProp(\"nah\", 123)\n  // @ts-expect-error - we cannot set views like we can with properties.\n  you.setProp(\"lowercaseName\", \"your name\")\n  // @ts-expect-error - we cannot set actions like we can with properties.\n  you.setProp(\"setName\", \"your name\")\n} catch (e) {\n  console.error(e)\n}\n```\n\n[See this working in CodeSandbox](https://codesandbox.io/p/sandbox/set-prop-action-ts-fix-p5psk7?file=%2Fsrc%2Findex.ts%3A9%2C23).\n\nThis is a type-safe way to reduce boilerplate and make your MobX-State-Tree models more readable.\n"
  },
  {
    "path": "docs/recipes/mst-query.md",
    "content": "---\nid: mst-query\ntitle: Manage Asynchronous Data with mst-query \n---\n\nFind the `mst-query` library on GitHub: https://github.com/ConrabOpto/mst-query.\n\n# mst-query\n\nmst-query is a query library designed specifically for MobX-State-Tree. It functions similarly to [react-query](https://tanstack.com/query/latest) but operates as a thin layer on top of a MobX-State-Tree store.\n\nKey features include:\n\n* Asynchronous data management with React hooks\n* Automatic normalization\n* Query invalidation upon stale data\n* Imperative api\n* Optimistic update\n* Garbage collection\n\nIn this recipes section, we'll briefly discuss each of these features and how they solve common problems when using MST.\n\n## Async data managment with React hooks\n\nCreating your own React hook for data fetching in components can be challenging. Managing all potential edge cases that may arise is both complex and error-prone.\n\nOpting for a third-party hook provides a more reliable solution. However, this approach can sometimes lead to redundant data storage, as data may be cached within the hook as well as within your models.\n\nmst-query offers a convenient method for fetching data directly within your components, seamlessly integrating with MST:\n\n```tsx\n// Regular MST:\nconst Todo = observer(({ id }) => {\n  useEffect(() => {\n    store.loadTodo(id);\n  }, [id]);  \n  \n  if (store.todoError) return <div>Got an error...</div>;\n  \n  if (store.todoIsLoading) return <div>Is loading...</div>;\n  \n  return <Todo todo={store.todo} />;\n});\n\n// With mst-query:\nconst Todo = observer(({ id }) => {\n  const { data, error, isLoading } = useQuery(store.todoQuery, { request: { id } })  \n  \n  if (error) return <div>Got an error...</div>;\n  \n  if (isLoading) return <div>Is loading...</div>;\n\n  return <Todo todo={data} />;\n});\n```\n\n## Creating queries\n\nIn mst-query, queries are treated as models. This means you can observe and update them just like regular models. You define a query model using `createQuery`:\n\n```ts\nconst LoadTodoQuery = createQuery(\"LoadTodoQuery\", {\n  data: t.reference(Todo),\n  request: t.model({ id: t.string }),\n  async endpoint({ request }) {\n    return todoApi.get(request.id)\n  }\n});\n```\n\nThe first option, `data`, represents the shape of the data returned from the endpoint. The second option, `request`, represents the arguments passed to the endpoint function. Both data and request undergo runtime type checking.\n\n## Automatic normalization\n\nA unique feature of mst-query is that data received from the server is automatically normalized. Because queries already understand the shape of the data returned from the API they consume, we can automate the process of creating and updating models with identifiers:\n\n```ts\nimport { t, flow } from \"mobx-state-tree\"\n\nconst User = t.model(\"User\", {\n  id: t.identifier,\n  name: t.string\n})\n\nconst Todo = t.model(\"Todo\", {\n  id: t.identifier,\n  title: t.string,\n  message: t.string,\n  done: t.boolean,\n  createdBy: t.reference(User)\n})\n\n// Regular MST:\nconst TodoStore = t\n  .model(\"RootStore\", {\n    todos: t.map(Todo)\n  })\n  .actions((self) => ({\n    loadTodo: flow(function* loadTodo(todoId: string) {\n        const todo = yield todoApi.getTodo(todoId);\n\n        const root = getRoot(self);\n        const user = root.userStore.createOrUpdateUser(todo.createdBy);\n        todo.createdBy = user;\n\n        const oldTodo = self.todos.get(todoId);\n        if (!oldTodo) {\n          self.todos.put({ todo });\n        } else {\n          self.todos.put({ ...getSnapshot(oldTodo), ...todo });\n        }\n    })\n  }))\n\n// With mst-query:\nconst UserStore = createModelStore('UserStore', User);\n\nconst TodoStore = createModelStore(\"TodoStore\", Todo).props({\n  todoQuery: createQuery(\"TodoQuery\", {\n    data: t.reference(Todo),\n    request: t.model({ id: t.string }),\n    async endpoint({ request }) {\n      return todoApi.getTodo(request.id)\n    }\n  })\n})\n\nconst RootStore = createRootStore({\n    userStore: t.optional(UserStore, {}),\n    todoStore: t.optional(TodoStore, {})\n});\n```\n\nThe functions `createRootStore` and `createModelStore` let mst-query know about your models that should be normalized. Note that you don't have to manually update the createdBy property on the todo, as this is done automatically for you.\n\nIn this example, we only had one nested data model in our response. However, in a real-world scenario, such as querying a GraphQL endpoint, you may need to handle dozens of similar properties. Mst-query will normalize all of these for you without additional code.\n\n## Query invalidation upon stale data\n\nJust like in react-query, you can pass a `staleTime` option to `useQuery`. This ensures your data gets periodically updated as the user navigates through your app. The default value of `staleTime` is 0, which means your users always see fresh data.\n\nIn mst-query, models are also automatically updated when you use `createMutation` and `mutate`. The only requirements are that your API returns the new data and that the data property is a reference type:\n\n```ts\nconst TodoRequestModel = t.model({ id: t.string, done: t.boolean, title: t.string });\n\nconst TodoUpdateMutation = createMutation(\"TodoUpdateMutation\", {\n    data: t.reference(Todo),\n    request: TodoRequestModel,\n    async endpoint({ request }) {\n      return todoApi.update(request)\n    }\n});\n\nconst TodoStore = createModelStore(\"TodoStore\", Todo)\n  .props({\n    todoQuery: TodoQuery,\n    todoUpdateMutation: TodoUpdateMutation\n  })\n  .actions(self => ({\n    update(data) {\n      // When mutate successfully resolves, the Todo will be automatically updated.\n      self.todoUpdateMutation.mutate({ request: data });\n    }\n  }))\n```\n\nYou can also manually refetch a query by calling `invalidate`. This pairs nicely with `createMutation` and a new listener called `onMutate`.\n\nA common use case for this is refetching a list of items:\n\n```tsx\nconst TodoListQuery = createQuery(\"TodoListQuery\", {\n    data: t.array(t.reference(Todo)),\n    async endpoint() {\n      return todoApi.getList();\n    }\n});\n\nconst TodoAddMutation = createMutation(\"TodoAddMutation\", {\n    data: t.reference(Todo),\n    request: TodoRequestModel,\n    async endpoint({ request }) {\n      return todoApi.update(request)\n    }\n});\n\nconst TodoStore = createModelStore(\"TodoStore\", Todo)\n  .props({\n    todoListQuery: TodoListQuery,\n    todoAddMutation: todoAddMutation\n  })\n  .actions(self => ({\n    afterCreate() {\n      onMutate(self.todoAdd, (result) => {\n        // Call invalidate to refetch the list...\n        self.todoListQuery.invalidate();\n\n        // ...or add the new item directly if you don't need to refetch\n        self.todoListQuery.data.push(result);\n      });\n    }  \n  }));\n\nconst TodoListContainer = observer(() => {\n  const { data } = useQuery(store.todoListQuery);\n  \n  const [addTodo, { isLoading }] = useMutation(store.todoAddMutation);\n  \n  return <TodoList todos={data} onAdd={addTodo} isAdding={isLoading} />;\n});\n```\n\n## Imperative api\n\nUsing hooks is convenient, but sometimes your data fetching logic can become more complex, resulting in a lot of business logic in your components.\n\nThankfully, most things you can do with hooks can also be accomplished with an imperative API:\n\n```tsx\nconst TodoStore = createModelStore(\"TodoStore\", Todo)\n  .props({\n    todoQuery: TodoQuery,\n    todoUpdateMutation: TodoUpdateMutation\n  })\n  .volatile(self => ({\n    permssionError: '',\n    updateResult: null\n  }))\n  .actions(self => ({\n    updateTodo: flow(function* (request) {\n      const result = yield todoApi.checkPermissions(request.id);\n      if (!result.ok) {\n        self.permissionError = 'You are not allowed to edit this resource';\n        return;\n      }\n\n      const { error, result: updateResult } = yield self.todoUpdateMutation.mutate({ request });\n      if (error) {\n        logApi.sendLog(error.message);\n      }\n\n      self.updateResult = updateResult;\n    });\n  }));\n\nconst TodoLoader = async (id) => {\n  // Manual fetch in a route loader. This is also how you prefetch data.\n  const todo = await store.todoQuery.query({ request: { id } });\n  return <TodoContainer todo={todo} store={store}  />;\n};\n\nconst TodoContainer = observer((props) => {    \n  const { todo, store } = props;\n  return (\n    <Todo \n      todo={todo} \n      onUpdate={store.updateTodo} \n      permissionError={store.permissionError} \n    />\n  );\n});\n```\n\nThe imperative API supports most of the features in mst-query. However, automatically refetching a query when it's stale—either by passing `staleTime` or calling `invalidate`—is currently not supported.\n\n## Optimistic update\n\nOptimistic updates are important for a UI to feel responsive. You achieve this in mst-query by passing your update to the `optimisticUpdate` option in `mutate`. When the mutate call resolves, whether successfully or not, the optimistic update is automatically rolled back.\n\n```ts\nconst serverTodo = yield self.todoAddMutation.mutate({\n  request: data,\n  optimisticUpdate() {\n    // createModelStore provides a merge action that you can use to manually create models\n    const clientTodo = todoStore.merge({\n      id: `${Math.random()}`,\n      title: data.title,\n      done: data.done,\n      createdBy: loggedInUserId\n    });\n\n    todoStore.todoListQuery.push(clientTodo);\n  }\n});\n\ntodoStore.todoListQuery.push(serverTodo);\n```\n\n## Garbage collection\n\nConsider a scenario where an MST application fetches a list of items from an API. Over time, items may be added, updated, or removed. In regular MST, every item fetched remains in memory unless you manually remove them. If the list is paginated, the problem is even larger.\n\nSince mst-query tracks all models via queries, it can safely remove unused models. You do this by calling runGc on the rootStore:\n\n`rootStore.runGc()`"
  },
  {
    "path": "docs/recipes/pre-built-form-types-with-mst-form-type.md",
    "content": "---\nid: pre-built-form-types-with-mst-form-type\ntitle: Pre-built Form Types with MST Form Type\n---\n\nFind the `mst-form-type` library on npm: https://www.npmjs.com/package/mst-form-type.\n\n## Introduction\n\nLibraries like [Ant Design](https://ant.design/) have a built-in `Form` component that holds field status and validation rules. Integrating a `Form` component with MobX State Tree models can pose significant challenges as business logic become more complex.\n\nThat's where a solution like `mst-form-type` library comes into play. It models the Ant design field management like a conventional MobX-State-Tree type definition. Users can still use the UI of a component library and keep logic inside MobX-State-Tree, instead of syncing status changes between `Form` component and model.\n\nPlease note: The `mst-form-type` library primarily provides model types for the form structure. It does not encompass business logic related to field interactions.\n\n## Setup\n\nSetting up and utilizing the `mst-form-type` library is straightforward:\n\n1. Install the `mst-form-type` library via npm:\n\n```sh\nnpm install mst-form-type\n```\n\n2. Ensure you have `mobx-state-tree ^5.0.0` installed:\n\n```sh\nnpm install mobx-state-tree@^5.0.0\n```\n\nNow that the general installation is complete, let's explore how to use it through a simple example. Although mobx-state-tree is designed to handle complex web application states, you might find the example a bit over-designed. However, the aim is to illustrate the idea of why using mst-form-type is helpful, so I've kept the logic as simple as possible.\n\n## Example\n\nTo demonstrate the difference between mst-form-type and the Form component, I've created a comparative example. The form is straightforward, featuring two static fields and a dynamic field group with two fields inside. Certain fields have validation rules to illustrate how validation functions. Additionally, the value of a static field changes when the number of dynamic fields reaches a certain threshold.\n\n### Ant Design `Form` component\n\nI've utilized the [Ant Design](https://ant.design/) `Form` component for the example, although other libraries should have very similar implementations. Let's take a look at the Ant Design version first.\n\n```tsx\nimport React, { useState, useEffect } from 'react'\nimport { Form, Input, Select, Button, InputNumber } from 'antd'\n\nconst { Option } = Select\n\nconst MyForm = () => {\n  const [form] = Form.useForm()\n  // extra state to handle field interaction logic\n  const [members, setMembers] = useState([{ name: '', age: '' }])\n  const [planValue, setPlanValue] = useState('')\n\n  useEffect(() => {\n    // handle field interaction in effect\n    if (members.length > 1 && planValue === 'A') {\n      form.setFieldsValue({ plan: 'B' })\n    }\n\n    if (members.length > 3 && planValue !== 'C') {\n      form.setFieldsValue({ plan: 'C' })\n    }\n  }, [members, planValue, form])\n\n  const onFinish = values => {\n    console.log('Received values:', values)\n  }\n\n  // handle field interaction by another set of onChange events\n  const handleAddMember = cb => {\n    if (members.length <= 5) {\n      setMembers([...members, { name: '', age: '' }])\n      cb()\n    }\n  }\n\n  const handleRemoveMember = (index, cb) => {\n    const updatedMembers = [...members]\n    updatedMembers.splice(index, 1)\n    setMembers(updatedMembers)\n    cb(index)\n  }\n\n  const handlePlanChange = value => {\n    setPlanValue(value)\n  }\n\n  return (\n    {/* useForm hook provides a form instance to hold field status */}\n    <Form form={form} onFinish={onFinish}>\n      {/* field need name props for form instance */}\n      <Form.Item\n        name=\"name\"\n        label=\"Name\"\n        rules={[{ required: true, message: 'Please input your name!' }]}\n      >\n        <Input />\n      </Form.Item>\n      <Form.Item\n        name=\"plan\"\n        label=\"Plan\"\n        rules={[{ required: true, message: 'Please select a plan!' }]}\n      >\n        {/* extra event handler for field logic */}\n        <Select onChange={handlePlanChange}>\n          <Option value=\"A\" disabled={members.length > 1}>\n            A\n          </Option>\n          <Option value=\"B\" disabled={members.length >= 3}>\n            B\n          </Option>\n          <Option value=\"C\">C</Option>\n        </Select>\n      </Form.Item>\n      <Form.List name=\"members\">\n        {(fields, { add, remove }) => (\n          <>\n            {fields.map(({ key, name, ...restField }, index) => (\n              <div key={key}>\n                <Form.Item\n                  {...restField}\n                  name={[name, 'name']}\n                  label=\"Member Name\"\n                  rules={[{ required: true, message: 'Please input member name!' }]}\n                >\n                  <Input />\n                </Form.Item>\n                <Form.Item {...restField} name={[name, 'age']} label=\"Member Age\">\n                  <InputNumber />\n                </Form.Item>\n                {/* extra event handler for field logic */}\n                <Button type=\"link\" onClick={() => handleRemoveMember(index, remove)}>\n                  Remove\n                </Button>\n              </div>\n            ))}\n            <Form.Item>\n              <Button\n                type=\"dashed\"\n                onClick={() => {\n                  // pass form instance method as callback\n                  handleAddMember(add)\n                }}\n                style={{ width: '100%' }}\n              >\n                Add Member\n              </Button>\n            </Form.Item>\n          </>\n        )}\n      </Form.List>\n      <Form.Item>\n        <Button type=\"primary\" htmlType=\"submit\">\n          Submit\n        </Button>\n      </Form.Item>\n    </Form>\n  )\n}\n\nexport default MyForm\n```\n\nThe code reveals that to manage field logic, we need to maintain a second copy of the field value in React state and attach additional event handlers to Input or other components. Subsequently, we handle the logic within useEffect hooks using form instance APIs.\n\nHowever, this approach can quickly become unmaintainable as the logic complexity increases. Furthermore, it is challenging to integrate with other application states in MST. This is because MST does not inherently manage field statuses; instead, the form instance does so.\n\n### mst-form-type\n\nNow, let's examine how the same example looks when using mst-form-type. The code can be split into two files: one for the UI and the other for the model. Let's start by reviewing the model file.\n\n```typescript\n// model.ts\nimport { types } from \"mobx-state-tree\"\nimport createForm from \"mst-form-type\"\n\n// define form in schema\nexport const FormSchema = {\n  static: [\n    {\n      id: \"name\",\n      default: \"\",\n      validator: \"required\"\n    },\n    {\n      id: \"plan\",\n      default: \"A\"\n    }\n  ],\n  dynamic: [\n    {\n      id: \"member\",\n      limit: 5,\n      schema: [\n        {\n          id: \"name\",\n          default: \"\",\n          validator: \"required\"\n        },\n        {\n          id: \"age\",\n          default: \"\",\n          validator: \"required\"\n        }\n      ],\n      default: [{ name: \"John\", age: 20 }],\n      onAdd: (i) => {\n        // hooks run when add dynamic fields\n        console.log(\"add\", i)\n      },\n      onRemove: (i) => {\n        // hooks run when remove dynamic fields\n        console.log(\"remove\", i)\n      },\n      onEdit: (i) => {\n        // hooks run when edit dynamic fields, only be called when edit field by form action\n        console.log(\"edit\", i)\n      }\n    }\n  ]\n}\n\n// App model\nexport const Example = types\n  .model(\"FormExample\")\n  .props({\n    form: createForm(FormSchema) // form as a model type\n  })\n  .views((self) => ({\n    get disableA() {\n      return self.form.member.size > 1\n    },\n    get disableB() {\n      return self.form.member.size > 3\n    }\n  }))\n  .actions((self) => ({\n    onAddFields() {\n      // field logic\n      if (self.form.member.size > 1 && self.form.plan.value === \"A\") {\n        self.form.plan.setValue(\"B\")\n      } else if (self.form.member.size > 3 && self.form.plan.value !== \"C\") {\n        self.form.plan.setValue(\"C\")\n      }\n    }\n  }))\n```\n\nIn the model file, we declare a form schema and create the form model. Subsequently, all relevant field models are created within the form model. An action is added to the model to handle field logic, illustrating that mst-form-type doesn't manage business logic but solely holds form and field statuses.\n\nNow, let's examine how the model is utilized by the UI, which is still built using Ant Design.\n\n```tsx\n// model.ts\nimport React, { useContext } from 'react'\nimport { Form, Input, Select, Button, InputNumber } from 'antd'\nimport { model, ModelContext } from '~/models'\nimport { observer } from 'mobx-react-lite'\n\nconst { Option } = Select\n\nconst MyForm = () => {\n  // get model instance via context\n  const model = useContext(ModelContext)\n\n  const onFinish = values => {\n    // 在这里计算价格\n    console.log('Received values:', model.form.submit())\n  }\n\n  return (\n    {/* no form instance need */}\n    <Form onFinish={onFinish}>\n      {/* get field status from MST */}\n      <Form.Item\n        label=\"Name\"\n        validateStatus={model.form.name.invalid && 'error'}\n        help={model.form.name.invalid && model.form.name.msg}\n      >\n        {/* controlled component using MST props and action */}\n        <Input\n          value={model.form.name.value}\n          onChange={e => model.form.name.setValue(e.target.value)}\n        />\n      </Form.Item>\n      <Form.Item label=\"Plan\">\n        <Select value={model.form.plan.value} onChange={value => model.form.plan.setValue(value)}>\n        {/* computed value */}\n          <Option value=\"A\" disabled={model.disableA}>\n            A\n          </Option>\n          <Option value=\"B\" disabled={model.disableB}>\n            B\n          </Option>\n          <Option value=\"C\">C</Option>\n        </Select>\n      </Form.Item>\n      <>\n        {/* render dynamic fields */}\n        {model.form.member.fields.map(({ id, ...field }) => {\n          return (\n            <div key={id}>\n              {/* access individual dynamic fields */}\n              <Form.Item label=\"Member Name\">\n                <Input\n                  value={field.name.value}\n                  onChange={e => {\n                    field.name.setValue(e.target.value)\n                  }}\n                />\n              </Form.Item>\n              <Form.Item label=\"Member Age\">\n                <InputNumber\n                  value={field.age.value}\n                  onChange={e => field.age.setValue(e.target.value)}\n                />\n              </Form.Item>\n              <Button\n                type=\"link\"\n                onClick={() => {\n                  // dynamic fields built-in action\n                  model.form.member.removeFields(id)\n                }}\n              >\n                Remove\n              </Button>\n            </div>\n          )\n        })}\n        <Form.Item>\n          <Button\n            type=\"dashed\"\n            onClick={() => {\n              model.form.member.addFields({ name: '', age: 20 })\n              // handle business logic\n              model.onAddFields()\n            }}\n          >\n            Add Member\n          </Button>\n        </Form.Item>\n      </>\n      <Form.Item>\n        <Button type=\"primary\" htmlType=\"submit\">\n          Submit\n        </Button>\n      </Form.Item>\n    </Form>\n  )\n}\n// wrap the component by mobx observer\nexport default observer(MyForm)\n```\n\nEssentially, `mst-form-type` eliminates the need for a form instance, allowing us to have only one copy of field status, which resides within the MST. Components no longer need to manage form-related states; instead, they simply read state from the MST and render accordingly.\n\nThere are two methods to retrieve or set field values. One involves using actions on each field instance, as demonstrated in the example code. The other method utilizes actions on the form type instance, specifying the field id defined in the form schema. Refer to the \"APIs\" section below for more details.\n\n## How It Works\n\nThe `mst-form-type` library exports a function which creates a form model (based on the base form model under the hood) using the provided schema and an optional name. Each form type model create its own type, and then instance by MST, which makes every attribute and method independent. So that you can have multiple form type `props` in a single MST model. User can conveniently interact with the form field via form props, or apply the form model actions to directly get or set field values. Let's see the architecture of `mst-form-type` first, and then we will go through most of the useful APIs\n\n### Architecture\n\nThe library defines three model types under the hood:\n\n- **Field Model**: Contains props and actions of a form field, such as `value`, `default`, `id`, `valid()`, etc.\n\n- **Group Model**: Built for handling a collection of field models as dynamic fields. Each dynamic field group should maintain uniformity in structure. The model also provides actions as life cycle hooks for adding, editing, and removing dynamic fields.\n\n- **Base Form Model**: Encapsulates all field and group models and the associated form methods. The exported function will build a form model with fields defined in schema on top of the base form model.\n\n### APIs\n\n**default export**\n\nThe default exported method will generate a new custom types.model with all the fields in the schema as props, based on a base model type. The newly created form type will automatically initialize with the schema upon creation. Optionally, a name can be passed for tracking purposes; otherwise, it will default to the base model name.\n\n```typescript\ntype TValidator = \"required\" | ((...args: any[]) => boolean) | RegExp | undefined | null\n\ntype TValue = string | boolean | number | Record<string, string> | Array<any>\n\ninterface FieldSchema {\n  id: string\n  type?: \"string\" | \"number\" | \"boolean\" | \"object\" | \"array\"\n  default: TValue\n  validator?: TValidator\n  msg?: string\n}\n```\n\n#### Field\n\n##### schema\n\n```typescript\ntype TValidator = \"required\" | ((...args: any[]) => boolean) | RegExp | undefined | null\n\ntype TValue = string | boolean | number | Record<string, string> | Array<any>\n\ninterface FieldSchema {\n  id: string\n  type?: \"string\" | \"number\" | \"boolean\" | \"object\" | \"array\"\n  default: TValue\n  validator?: TValidator\n  msg?: string\n}\n```\n\n##### props\n\n`id`\n\n`types.identifier`. The key of each field in a form, and will become the form type props key. It should be unique and will be used to access field value and in `setValue()` form action.\n\n`value`\n\nThe props hold the field value. The value type can be `string`, `boolean`, `number`, `object`, `array`. `object` and `array` will be tranform to `types.frozen` as a MST leaf.\n\n`default`\n\nThe default value of a field. The Mobx State Tree will decide `prop` type based on the type of this value.\n\n`validator`\n\nOptional & Private. All validators will be called in `valid()` before `submit()`.\n\n`'required'` means this field cannot be falsy values, like `0`, `''`, or `undefined`.\n\n`((...args: any[]) => boolean)` means a function return a boolean value. If returned `true`, the validation will be treated passed.\n\n`RegExp` means the value will be used in `RegExp.test()`. If returned `true`, the validation will be treated passed.\n\n`undefined | null` will not be processed.\n\n`msg`\n\nOptional. Message shows when field is invalid. The default message is `'The input is invalid'`\n\n`invalid`\n\nCompute value. Return revert value of `invalid()` result\n\n##### actions\n\n**`setValue(value)`**\n\nUpdate field value.\n\n**`valid()`**\n\nRun field validator if it has.\n\n**`reset()`**\n\nReset field value to default value.\n\n`setValidator(rawValidator: TValidator)`\n\nChange field validator after initialization\n\n`init(field: IField)`\n\nRerun field initialization\n\n`setErrorMsg(msg: string)`\n\nChange invalid message\n\n`clear()`\n\nSet field value to `null`\n\n##### code example\n\n```typescript\nform[id].value\nform[id].invalid\nform[id].setValue(\"new-value\")\nform[id].reset()\nform[id].valid()\n```\n\n#### Dynamic Field Group\n\n##### schema\n\n```typescript\ninterface DynamicFields {\n  id: string\n  limit: number\n  schema: FieldSchema | FieldSchema[]\n  default?: Array<Record<string, TValue>>\n  onAdd?: (field) => any\n  onRemove?: (field) => any\n  onEdit?: (field) => void\n}\n```\n\n##### props\n\n`id`\n\n`types.identifier`. The key of each dynamic field group in a form, and will become the form type props key. It should be unique and will be used to access field value and in `setDynamicValue()` form action.\n\n`fields`\n\nAn array holds all dynamic field models. `schema` in the interface is to define field schema here. Object in `default` array will be used to create dynamic fields when form initializing.\n\n`limit`\n\nOptional. Maxium dynamic field allowed. default is `-1`, means unlimited.\n\n##### actions\n\n**`addFields(i, isInit = false)`**\n\nAdd new dynamic field `i`. You don't need to pass isInit flag when calling the action. It is used for not calling `onAdd` hooks in schema when initialization.\n\n**`removeFields(id: string)`**\n\nRemove the field with specific `id`. This action will call the `onRemove` hook if passed.\n\n**`editField(id: string, fieldKey: string, value)`**\n\nEdit `fieldKey` field with `id` to `value`. This action will call the `onEdit` hook if passed.\n\n`getValues()`\n\nGet all dynamic field values/\n\n`valid()`\n\nValid all dynamic field.\n\n`reset()`\n\nReset all dynamic fields.\n\n##### code example\n\n```typescript\nform[id].fields.map(field => { ... })\nform[id].addFields(field)\nform[id].removeFields('id')\nform[id].editField('id', 'key', 'value')\nform[id].getValues() // get all dynamic field values, rarely used\nform[id].valid() // valid all dynamic field, rarely used\nform[id].reset() // reset all dynamic field, rarely used\n```\n\n#### Form\n\n##### schema\n\n```typescript\ninterface FormSchema {\n  static: FieldSchema[]\n  dynamic?: DynamicFields[]\n}\n```\n\n##### props\n\n**fields**\n\nEvery field in `schema` will become a field `prop` of form type. The type of each field will be based on `default` value.\n\n**`submission`**\n\nA snapshot of last success submitted form values, in Key-Value object format.\n\n`error`\n\nAn object in Key-Value format contains validation error of each field. This will be cleared on every `valid()` call.\n\n`_internalStatus`\n\nIndicate the form status, has 4 values: `'init', 'pending', 'success', 'error'`. Usually you don't need it, it will change according to form status.\n\n`loading`\n\nCompute value. Return `true` when form status is 'pending'. Designed for avoiding duplicated submission.\n\n##### actions\n\n**`setValue({ key, value })`**\n\nSet **static** field value in a form type. `_internalStatus` is reserved.\n\n**`setDynamicValue({ groupId, id, key, value })`**\n\nSet **dynamic** field value in a form type. Each dynamic field has a field group id and a field id. Or you can use the `setValue` action on instance to do the same job.\n\n**`submit()`**\n\nIt will return all field values if passed validation in Key-Value format object. The last valid submission will be hold in `submission` props.\n\n**The method will not submit the form in any form of action. It only output the form current values. You need to handle to real submission action yourself.**\n\n`init()`\n\nIt will be called after new custom type created with schema. It will process the schema to get default values and validators.\n\n`valid()`\n\nIt will run all validators in schema with current field values. It will be called in `submit()`, and produce `error` if any error happens.\n\n`reset()`\n\nIt will set the form type to `init` status, clearing submissions and errors. All fields will be set to default values passed by schema.\n\n##### code example\n\n```typescript\nform.setValue({ key, value })\nform.setDynamicValue({ groupId, id, key, value })\nform.submit()\nform.rest()\nform.onAdd(id, field)\nform.onRemove(groupId, fieldId)\nform.onEdit(groupId, fieldId, key, value)\nform.clear(groupId)\n```\n"
  },
  {
    "path": "docs/tips/circular-deps.md",
    "content": "---\nid: circular-deps\nsidebar_label: Circular dependencies\ntitle: Handle circular dependencies between files and types using `late`\n---\n\n<div id=\"codefund\"></div>\n\n\nIn the exporting file:\n\n```javascript\nexport function LateStore() {\n    return types.model({\n        title: types.string\n    })\n}\n```\n\nIn the importing file:\n\n```javascript\nimport { LateStore } from \"./circular-dep\"\n\nconst Store = types.late(() => LateStore)\n```\n\nThanks to function hoisting in combination with `types.late`, this lets you have circular dependencies between types, across files.\n\nIf you are using TypeScript and you get errors about circular or self-referencing types then you can partially fix it by doing:\n\n```ts\nconst Node = types.model({\n    x: 5, // as an example\n    me: types.maybe(types.late((): IAnyModelType => Node))\n})\n```\n\nIn this case, while \"me\" will become any, any other properties (such as x) will be strongly typed, so you can typecast the self referencing properties (me in this case) once more to get typings. For example:\n\n```ts\nnode.((me) as Instance<typeof Node>).x // x here will be number\n```\n"
  },
  {
    "path": "docs/tips/faq.md",
    "content": "---\nid: faq\ntitle: Frequently Asked Questions\n---\n\n<div id=\"codefund\"></div>\n\n### Should all state of my app be stored in `mobx-state-tree`?\n\nNo, or not necessarily. An application can use both state trees and vanilla MobX observables at the same time.\nState trees are primarily designed to store your domain data as this kind of state is often distributed and not very local.\nFor local component state, for example, vanilla MobX observables might often be simpler to use.\n\n### When not to use MST?\n\nMST provides access to snapshots, patches and interceptable actions. Also, it fixes the `this` problem.\nAll these features have a downside as they incur a little runtime overhead.\nAlthough in many places the MST core can still be optimized significantly, there will always be a constant overhead.\nIf you have a performance critical application that handles a huge amount of mutable data, you will probably be better\noff by using 'raw' MobX, which has a predictable and well-known performance and much less overhead.\n\nLikewise, if your application mainly processes stateless information (such as a logging system), MST won't add much value.\n\n### Can I use Hot Module Reloading?\n\n<details>\n    <summary style=\"color: white; background:#ff7000;padding:5px;margin:5px;border-radius:2px\">egghead.io lesson 10: Restore the Model Tree State using Hot Module Reloading when Model Definitions Change</summary>\n    <br>\n    <div style=\"padding:5px;\">\n        <iframe style=\"border: none;\" width=760 height=427  src=\"https://egghead.io/lessons/react-restore-the-model-tree-state-using-hot-module-reloading-when-model-definitions-change/embed\" ></iframe>\n    </div>\n    <a style=\"font-style:italic;padding:5px;margin:5px;\"  href=\"https://egghead.io/lessons/react-restore-the-model-tree-state-using-hot-module-reloading-when-model-definitions-change\">Hosted on egghead.io</a>\n</details>\n\nYes, with MST it is pretty straight forward to setup hot reloading for your store definitions while preserving state. See the [todomvc example](https://github.com/coolsoftwaretyler/mst-example-todomvc/blob/main/src/index.js#L59C1-L64C1).\n\n### How does MST compare to Redux\n\nSo far this might look a lot like an immutable state tree as found for example in Redux apps, but there're are only so many reasons to use Redux as per [article linked at the very top of Redux guide](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367) that MST covers too, meanwhile:\n\n-   Like Redux, and unlike MobX, MST prescribes a very specific state architecture.\n-   mobx-state-tree allows direct modification of any value in the tree. It is not necessary to construct a new tree in your actions.\n-   mobx-state-tree allows for fine-grained and efficient observation of any point in the state tree.\n-   mobx-state-tree generates JSON patches for any modification that is made.\n-   mobx-state-tree provides utilities to turn any MST tree into a valid Redux store.\n-   Having multiple MSTs in a single application is perfectly fine.\n\n### Where is the `any` type?\n\nMST doesn't offer an any type because it can't reason about it. For example, given a snapshot and a field with `any`, how should MST know how to deserialize it or apply patches to it, etc.? If you need `any`, there are following options:\n\n1.  Use `types.frozen()`. Frozen values need to be immutable and serializable (so MST can treat them verbatim)\n2.  Use volatile state. Volatile state can store anything, but won't appear in snapshots, patches etc.\n3.  If your type is regular, and you just are too lazy to type the model, you could also consider generating the type at runtime once (after all, MST types are just JS...). However, you will lose static typing, and any confusion it causes is up to you to handle :-).\n"
  },
  {
    "path": "docs/tips/inheritance.md",
    "content": "---\nid: inheritance\nsidebar_label: Simulating inheritance\ntitle: Simulate inheritance by using type composition\n---\n\n<div id=\"codefund\"></div>\n\nThere is no notion of inheritance in MST. The recommended approach is to keep references to the original configuration of a model in order to compose it into a new one, for example by using `types.compose` (which combines two types) or producing fresh types using `.props|.views|.actions`. An example of classical inheritance could be expressed using composition as follows:\n\n```javascript\nconst Square = types\n    .model(\n        \"Square\",\n        {\n            width: types.number\n        }\n    )\n    .views(self => ({\n        // note: this is not a getter! this is just a function that is evaluated\n        surface() {\n            return self.width * self.width\n        }\n    }))\n\n// create a new type, based on Square\nconst Box = Square\n    .named(\"Box\")\n    .views(self => {\n        // save the base implementation of surface, again, this is a function.\n        // if it was a getter, the getter would be evaluated only once here\n        // instead of being able to evaluate dynamically at time-of-use\n        const superSurface = self.surface\n\n        return {\n            // super contrived override example!\n            surface() {\n                return superSurface() * 1\n            },\n            volume() {\n                return self.surface() * self.width\n            }\n        }\n    }))\n\n// no inheritance, but, union types and code reuse\nconst Shape = types.union(Box, Square)\n\nconst instance = Shape.create({type:\"Box\",width:4})\nconsole.log(instance.width)\nconsole.log(instance.surface()) // calls Box.surface()\nconsole.log(instance.volume()) // calls Box.volume()\n```\n\nSimilarly, compose can be used to simply mix in types:\n\n```javascript\nconst CreationLogger = types.model().actions((self) => ({\n    afterCreate() {\n        console.log(\"Instantiated \" + getType(self).name)\n    }\n}))\n\nconst BaseSquare = types\n    .model({\n        width: types.number\n    })\n    .views((self) => ({\n        surface() {\n            return self.width * self.width\n        }\n    }))\n\nexport const LoggingSquare = types\n    .compose(\n        // combine a simple square model...\n        BaseSquare,\n        // ... with the logger type\n        CreationLogger\n    )\n    // ..and give it a nice name\n    .named(\"LoggingSquare\")\n```\n"
  },
  {
    "path": "docs/tips/more-tips.md",
    "content": "---\nid: more-tips\ntitle: Miscellaneous Tips\n---\n\n<div id=\"codefund\"></div>\n\n### Generate MST models from JSON\n\nThe following service can generate MST models based on JSON: https://transform.now.sh/json-to-mobx-state-tree\n\n### `optionals` and default value functions\n\n`types.optional` can take an optional function parameter which will be invoked each time a default value is needed. This is useful to generate timestamps, identifiers or even complex objects, for example:\n\n`createdDate: types.optional(types.Date, () => new Date())`\n\n### `toJSON()` for debugging\n\nFor debugging you might want to use `getSnapshot(model, applyPostProcess)` to print the state of a model. If you didn't import `getSnapshot` while debugging in some debugger, don't worry, `model.toJSON()` will produce the same snapshot. (For API consistency, this feature is not part of the typed API).\n\n#### Optional/empty maps/arrays\n\nSince v3, maps and arrays are optional by default, this is:\n\n```javascript\ntypes.map(OtherType)\n// is the same as\ntypes.optional(types.map(OtherType), {})\n\ntypes.array(OtherType)\n// is the same as\ntypes.optional(types.array(OtherType), [])\n```\n\n#### Complex union types\n\nUnion types works well when the types are primitives. Unions might cause bugs when complex types are involved. For Example\n\n```javascript\nconst Foo = types.model({ foo: types.array(types.string) })\nconst Bar = types.model({ bar: types.array(types.number) })\nconst FooBar = types.union(Foo, Bar)\n\nconst test_foo = { foo: [\"test\"] }\nconst test_bar = { bar: [200] }\n\nconst unionStore = Store.create({\n    foobars: [test_foo, test_bar]\n})\n\nconst foo = unionStore.foobars[0]\nconst bar = unionStore.foobars[1]\nconsole.log(foo, bar)\n\n// Expected: { foo: [\"test\"], bar: [200] }\n// Actual: { foo: [\"test\"], foo: [] }\n```\n\nThis can be solved in two ways\n\n**Using Dispatcher:**\n\nYou can provide first arg with a _dispatcher_ that provides _snapshot_ which can be used to explicitly return the `type` of the model\n\n```javascript\nconst FooBar = types.union(\n    {\n        dispatcher: (snapshot) => {\n            console.log({ snapshot })\n            if (snapshot.foo) {\n                return Foo\n            }\n            return Bar\n        }\n    },\n    Foo,\n    Bar\n)\n```\n\n**Using Literals:**\n\nAdding type literal to the Base Models to identify the type\n\n```javascript\nconst Foo = types.model({\n    foo: types.array(types.string),\n    type: types.literal(\"foo\")\n})\n\nconst Bar = types.model({\n    bar: types.array(types.number),\n    type: types.literal(\"bar\")\n})\n```\n\n### Building with production environment\n\nMobX-state-tree provides a lot of dev-only checks. They check the correctness of function calls and perform runtime type-checks over your models. It is recommended to disable them in production builds. To do so, you should use webpack's DefinePlugin to set environment as production and remove them. More information could be found in the [official webpack guides](https://webpack.js.org/plugins/environment-plugin/#usage).\n"
  },
  {
    "path": "docs/tips/resources.md",
    "content": "---\nid: resources\ntitle: Talks & Blogs\n---\n\n<div id=\"codefund\"></div>\n\n## Official resources\n\n-   Introduction blog post [The curious case of MobX state tree](https://medium.com/@mweststrate/the-curious-case-of-mobx-state-tree-7b4e22d461f)\n-   Free [egghead.io](https://egghead.io/courses/manage-application-state-with-mobx-state-tree) MST course\n-   [Introduction tutorial](../intro/getting-started)\n\n## Talks\n\n-   Talk React Europe 2017: [Next generation state management](https://www.youtube.com/watch?v=rwqwwn_46kA)\n-   Talk ReactNext 2017: [React, but for Data](https://www.youtube.com/watch?v=xfC_xEA8Z1M&index=6&list=PLMYVq3z1QxSqq6D7jxVdqttOX7H_Brq8Z) ([slides](http://react-next-2017-slides.surge.sh/#1), [demo](https://codesandbox.io/s/8y4p23j32j))\n-   Talk ReactJSDay Verona 2017: [Immutable or immutable? Both!](https://www.youtube.com/watch?v=zdtwaa5Rmb8&index=9&list=PLWK9j6ps_unl293VhhN4RYMCISxye3xH9) ([slides](https://mweststrate.github.io/reactjsday2017-presentation/index.html#1), [demo](https://github.com/mweststrate/reatjsday2017-demo))\n-   Talk React Alicante 2017: [Mutable or Immutable? Let's do both!](https://www.youtube.com/watch?v=DgnL3uij9ec&list=PLd7nkr8mN0sWvBH_s0foCE6eZTX8BmLUM&index=9) ([slides](https://mattiamanzati.github.io/slides-react-alicante-2017/#2))\n-   Talk ReactiveConf 2016: [Immer-mutable state management](https://www.youtube.com/watch?v=Ql8KUUUOHNc&list=PLa2ZZ09WYepMCRRGCRPhTYuTCat4TiDlX&index=30)\n-   Talk FrontendLove 2018: [MobX State Tree + React: pure reactivity served](https://www.youtube.com/watch?v=HS9revHrNRI) by [Luca Mezzalira](https://lucamezzalira.com/) ([slides](https://docs.google.com/presentation/d/1f18RhN9hz1GPAdY4binWVNZDKm3k7EfNvV48lWnzdjQ/edit#slide=id.g35f391192_00)).\n-   Talk React Native EU 2019: [Resolving the Great State Debate with Hooks, Context, and MobX-State-Tree](https://www.youtube.com/watch?v=Wx9slbOTD6Q) by [Jamon Holmgren](https://jamonholmgren.com/) ([slides](https://infinite-red.slides.com/infinitered/resolving-the-great-state-debate))\n-   International JavaScript Conference 2019, Londen [You don’t know MobX State Tree](https://www.youtube.com/watch?v=LKyCJB27oNM), by [Max Gallo](https://twitter.com/_maxgallo)\n-   React Europe 2019: [Combining GraphQL + mobx-state-tree](https://www.youtube.com/watch?v=Sq2M00vghqY)\n\n## Blogs / Vlogs\n\n-   [Introduction to MobX State Tree](https://www.youtube.com/watch?v=pPgOrecfcg4) by [Leigh Halliday](https://twitter.com/leighchalliday)\n-   [Creating a Trivia App with Ignite Bowser](https://shift.infinite.red/creating-a-trivia-app-with-ignite-bowser-part-1-1987cc6e93a1) by [Robin Heinze](https://twitter.com/robin_heinze)\n-   <img src=\"https://raw.githubusercontent.com/mobxjs/mobx/master/docs/assets/book.jpg\" height=\"80px\"/> [The MobX book](https://books.google.nl/books?id=ALFmDwAAQBAJ&pg=PP1&lpg=PP1&dq=michel+weststrate+mobx+quick+start+guide:+supercharge+the+client+state+in+your+react+apps+with+mobx&source=bl&ots=D460fxti0F&sig=ivDGTxsPNwlOjLHrpKF1nweZFl8&hl=nl&sa=X&ved=2ahUKEwiwl8XO--ncAhWPmbQKHWOYBqIQ6AEwAnoECAkQAQ#v=onepage&q=michel%20weststrate%20mobx%20quick%20start%20guide%3A%20supercharge%20the%20client%20state%20in%20your%20react%20apps%20with%20mobx&f=false) by Pavan Podila and Michel Weststrate, discusses MobX-State-Tree as well.\n-   [How to: mobx-state-tree + react + typescript](https://dev.to/margaretkrutikova/how-to-mobx-state-tree-react-typescript-3d5j) by [Margarita Krutikova](https://twitter.com/rita_krutikova)\n-   [MobX-state-tree: A step by step guide for React Apps](https://medium.com/mr-frontend-community/mobx-state-tree-a-step-by-step-guide-for-react-apps-e65716a219d2) by [Faris Tangastani](https://medium.com/@ftangastani)\n\n## Supported devtools:\n\n-   [Reactotron](https://github.com/infinitered/reactotron)\n-   [MobX DevTools](https://chrome.google.com/webstore/detail/mobx-developer-tools/pfgnfdagidkfgccljigdamigbcnndkod)\n-   The Redux can be connected as well as demonstrated [here](https://github.com/coolsoftwaretyler/mst-example-redux-todomvc/blob/main/src/index.js#L6)\n\n## Addons, libraries and tools\n\n-   [mst-query](https://github.com/ConrabOpto/mst-query/) - Query library for MST with support for auto normalization, garbage collection, infinite scroll & pagination, and more\n"
  },
  {
    "path": "docs/tips/snapshots-as-values.md",
    "content": "---\nid: snapshots-as-values\ntitle: Using snapshots as values\n---\n\n<div id=\"codefund\"></div>\n\nEverywhere where you can modify your state tree and assign a model instance, you can also\njust assign a snapshot, and MST will convert it to a model instance for you.\nHowever, that is simply not expressible in static type systems atm (as the type written to a value differs to the type read from it).\nAs a workaround MST offers a `cast` function, which will try to fool the typesystem into thinking that an snapshot type (and instance as well)\nis of the related instance type.\n\n```typescript\nconst Task = types.model({\n    done: false\n})\nconst Store = types.model({\n    tasks: types.array(Task),\n    selection: types.maybe(Task)\n})\n\nconst s = Store.create({ tasks: [] })\n// `{}` is a valid snapshot of Task, and hence a valid task, MST allows this, but TS doesn't, so we need to use 'cast'\ns.tasks.push(cast({}))\ns.selection = cast({})\n```\n\nAdditionally, for function parameters, MST offers a `SnapshotOrInstance<T>` type, where T can either be a `typeof TYPE` or a\n`typeof VARIABLE`. In both cases it will resolve to the union of the input (creation) snapshot and instance type of that TYPE or VARIABLE.\n\nUsing both at the same time we can express property assignation of complex properties in this form:\n\n```typescript\nconst Task = types.model({\n    done: false\n})\nconst Store = types\n    .model({\n        tasks: types.array(Task)\n    })\n    .actions(self => ({\n        addTask(task: SnapshotOrInstance<typeof Task>) {\n            self.tasks.push(cast(task))\n        },\n        replaceTasks(tasks: SnapshotOrInstance<typeof self.tasks>) {\n            self.tasks = cast(tasks)\n        }\n    }))\n\nconst s = Store.create({ tasks: [] })\n\ns.addTask({})\n// or\ns.addTask(Task.create({}))\n\ns.replaceTasks([{ done: true }])\n// or\ns.replaceTasks(types.array(Task).create([{ done: true }]))\n```\n\nAdditionally, the `castToSnapshot` function can be also used in the inverse case, this is when you want to use an instance inside an snapshot.\nIn this case MST will internally convert the instance to a snapshot before using it, but we need once more to fool TypeScript into\nthinking that this instance is actually a snapshot.\n\n```typescript\nconst task = Task.create({ done: true })\nconst Store = types.model({\n    tasks: types.array(Task)\n})\n\n// we cast the task instance to a snapshot so it can be used as part of another snapshot without typing errors\nconst s = Store.create({ tasks: [castToSnapshot(task)] })\n```\n\nFinally, the `castToReferenceSnapshot` can be used when we want to use an instance to actually use a reference snapshot (a string or number).\nIn this case MST will internally convert the instance to a reference snapshot before using it, but we need once more to fool TypeScript into\nthinking that this instance is actually a snapshot of a reference.\n\n```typescript\nconst task = Task.create({ id: types.identifier, done: true })\nconst Store = types.model({\n    tasks: types.array(types.reference(Task))\n})\n\n// we cast the task instance to a reference snapshot so it can be used as part of another snapshot without typing errors\nconst s = Store.create({ tasks: [castToReferenceSnapshot(task)] })\n```\n\n"
  },
  {
    "path": "docs/tips/typescript.md",
    "content": "---\nid: typescript\ntitle: TypeScript and MST\n---\n\n<div id=\"codefund\"></div>\n\nTypeScript support is best-effort as not all patterns can be expressed in TypeScript. Except for assigning snapshots to properties we get pretty close! As MST uses the latest fancy TypeScript features it is required to use TypeScript 5.3.3 or later with `noImplicitThis` and `strictNullChecks` enabled.\n\nThe more strict options that are enabled, the better the type system will behave.\n\n#### Recommend compiler flags\n\nThe recommended compiler flags (against which all our tests are written) are:\n\n```json\n{\n  \"strictNullChecks\": true,\n  \"strictFunctionTypes\": true,\n  \"noImplicitAny\": true,\n  \"noImplicitReturns\": true,\n  \"noImplicitThis\": true\n}\n```\n\nOr shorter by leveraging `strict`:\n\n```json\n{\n  \"strict\": true,\n  \"noImplicitReturns\": true\n}\n```\n\nFlow is not supported.\n\n#### Using a MST type at design time\n\nWhen using models, you write an interface, along with its property types, that will be used to perform type checks at runtime.\nWhat about compile time? You can use TypeScript interfaces to perform those checks, but that would require writing again all the properties and their actions!\n\nGood news! You don't need to write it twice!\n\nThere are four kinds of types available, plus one helper type:\n\n- `Instance<typeof TYPE>` or `Instance<typeof VARIABLE>` is the node instance type. (Legacy form is `typeof MODEL.Type`).\n- `SnapshotIn<typeof TYPE>` or `SnapshotIn<typeof VARIABLE>` is the input (creation) snapshot type. (Legacy form is `typeof MODEL.CreationType`).\n- `SnapshotOut<typeof TYPE>` or `SnapshotOut<typeof VARIABLE>` is the output (creation) snapshot type. (Legacy form is `typeof MODEL.SnapshotType`).\n- `SnapshotOrInstance<typeof TYPE>` or `SnapshotOrInstance<typeof VARIABLE>` is `SnapshotIn<T> | Instance<T>`. This type is useful when you want to declare an input parameter that is able consume both types.\n- `TypeOfValue<typeof VARIABLE>` gets the original type for the given instance. Note that this only works for complex values (models, arrays, maps...) but not for simple values (number, string, boolean, string, undefined).\n\n```typescript\nimport { types, Instance, SnapshotIn, SnapshotOut } from \"mobx-state-tree\"\n\nconst Todo = types\n  .model({\n    title: \"hello\"\n  })\n  .actions((self) => ({\n    setTitle(v: string) {\n      self.title = v\n    }\n  }))\n\ninterface ITodo extends Instance<typeof Todo> {} // => { title: string; setTitle: (v: string) => void }\ninterface ITodoSnapshotIn extends SnapshotIn<typeof Todo> {} // => { title?: string }\ninterface ITodoSnapshotOut extends SnapshotOut<typeof Todo> {} // => { title: string }\n```\n\nNote, it is important to use `interface` and not `type` when constructing those types! Although `type`s will work exactly the same, due to their nature they will be [much more expensive for the compiler to typecheck](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections).\n\nFor further performance tips, read the [official TypeScript performance wiki](https://github.com/microsoft/TypeScript/wiki/Performance).\n\n#### Typing `self` in actions and views\n\nThe type of `self` is what `self` was **before the action or views blocks starts**, and only after that part finishes, the actions will be added to the type of `self`.\n\nSometimes you'll need to take into account where your typings are available and where they aren't. The code below will not compile: TypeScript will complain that `self.upperProp` is not a known property. Computed properties are only available after `.views` is evaluated.\n\nFor example:\n\n```typescript\nconst Example = types\n  .model(\"Example\", {\n    prop: types.string\n  })\n  .views((self) => ({\n    get upperProp(): string {\n      return self.prop.toUpperCase()\n    },\n    get twiceUpperProp(): string {\n      return self.upperProp + self.upperProp // Compile error: `self.upperProp` is not yet defined\n    }\n  }))\n```\n\nYou can circumvent this situation by using `this` whenever you intend to use the newly declared computed values that are local to the current object:\n\n```typescript\nconst Example = types.model(\"Example\", { prop: types.string }).views((self) => ({\n  get upperProp(): string {\n    return self.prop.toUpperCase()\n  },\n  get twiceUpperProp(): string {\n    return this.upperProp + this.upperProp\n  }\n}))\n```\n\nAlternatively you can also declare multiple `.views` block, in which case the `self` parameter gets extended after each block.\n\n```typescript\nconst Example = types\n  .model('Example', { prop: types.string })\n  .views(self => {\n    get upperProp(): string {\n      return self.prop.toUpperCase();\n    },\n  }))\n  .views(self => ({\n    get twiceUpperProp(): string {\n      return self.upperProp + self.upperProp;\n    },\n  }));\n```\n\nAs a last resort, although not recommended due to the performance penalty (see the note below), you may declare the views in two steps:\n\n```typescript\nconst Example = types\n  .model('Example', { prop: types.string })\n  .views(self => {\n      const views = {\n        get upperProp(): string {\n            return self.prop.toUpperCase();\n        },\n        get twiceUpperProp(): string {\n            return views.upperProp + views.upperProp;\n        }\n      }\n      return views\n  }))\n```\n\n_**NOTE: the last approach will incur runtime performance penalty as accessing such computed values (e.g. inside `render()` method of an observed component) always leads to full recompute (see [this issue](https://github.com/mobxjs/mobx-state-tree/issues/818#issue-323164363) for details). For a heavily used computed properties it's recommended to use one of above approaches.**_\n\nSimilarly, when writing actions or views one can use helper functions:\n\n```typescript\nimport { types, flow } from \"mobx-state-tree\"\n\nconst Example = types.model(\"Example\", { prop: types.string }).actions((self) => {\n  // Don't forget that async operations HAVE\n  // to use `flow( ... )`.\n  const fetchData = flow(function* fetchData() {\n    yield doSomething()\n  })\n\n  return {\n    fetchData,\n    afterCreate() {\n      // Notice that we call the function directly\n      // instead of using `self.fetchData()`. This is\n      // because Typescript doesn't know yet about `fetchData()`\n      // being part of `self` in this context.\n      fetchData()\n    }\n  }\n})\n```\n\n#### Using cast\n\nWe provide a `cast` utility to [cast a node snapshot to an instance type for assignment](https://mobx-state-tree.js.org/API/#cast). [Check out this blog post for details and examples on when to use it](https://coolsoftware.dev/blog/type-casting-in-mobx-state-tree/).\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  displayName: \"test\",\n  testEnvironment: \"node\",\n  transform: {\n    \"^.+\\\\.tsx?$\": \"ts-jest\"\n  },\n  testRegex: \".*\\\\.test\\\\.tsx?$\",\n  moduleFileExtensions: [\"ts\", \"tsx\", \"js\"],\n  globals: {\n    \"ts-jest\": {\n      tsConfig: \"__tests__/tsconfig.json\"\n    }\n  },\n  reporters: [\"default\", \"jest-junit\"]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"mobx-state-tree\",\n    \"version\": \"7.0.2\",\n    \"description\": \"Opinionated, transactional, MobX powered state container\",\n    \"main\": \"dist/mobx-state-tree.js\",\n    \"umd:main\": \"dist/mobx-state-tree.umd.js\",\n    \"module\": \"dist/mobx-state-tree.module.js\",\n    \"browser\": {\n        \"./dist/mobx-state-tree.js\": \"./dist/mobx-state-tree.js\",\n        \"./dist/mobx-state-tree.module.js\": \"./dist/mobx-state-tree.module.js\"\n    },\n    \"unpkg\": \"dist/mobx-state-tree.umd.min.js\",\n    \"jsnext:main\": \"dist/mobx-state-tree.module.js\",\n    \"react-native\": \"dist/mobx-state-tree.module.js\",\n    \"typings\": \"dist/index.d.ts\",\n    \"sideEffects\": false,\n    \"scripts\": {\n        \"clean\": \"shx rm -rf dist lib\",\n        \"build\": \"bun run clean && tsc && cpr lib/src dist --filter=\\\\.js$ && rollup -c\",\n        \"test:dev\": \"NODE_ENV=development bun test\",\n        \"test:prod\": \"MST_TESTING=1 NODE_ENV=production bun test\",\n        \"test:all\": \"bun run test:dev && bun run test:prod && bun run size\",\n        \"test:perf\": \"bun test ./__tests__/perf/\",\n        \"test:perf:compiled\": \"bun run build && bun run jest:perf && TS_NODE_COMPILER_OPTIONS='{\\\"module\\\": \\\"commonjs\\\"}' /usr/bin/time node --expose-gc --require ts-node/register __tests__/perf/report.ts\",\n        \"typecheck\": \"tsc --noEmit\",\n        \"size\": \"size-limit\",\n        \"tag-new-version\": \"yarn version && git push --tags\",\n        \"build-docs\": \"bun run fix-typedoc && shx rm -rf ./docs/API && typedoc --options ./typedocconfig.js && node scripts/fix-docs-source-links.js\",\n        \"publish-docs\": \"bun run build-docs && cd ./website && GIT_USER=jamonholmgren USE_SSH=true bun run publish-gh-pages\",\n        \"start\": \"cd website && bun run start\",\n        \"prepare\": \"husky install\",\n        \"lint\": \"tslint -c ./tslint.json 'src/**/*.ts'\",\n        \"fix-typedoc\": \"shx rm -rf ./node_modules/typedoc/node_modules/typescript\",\n        \"prettier:list\": \"prettier --list-different \\\"src/**/*.ts\\\" \\\"__tests__/**/*.ts\\\"\",\n        \"prettier:check\": \"prettier --check \\\"src/**/*.ts\\\" \\\"__tests__/**/*.ts\\\"\",\n        \"prettier:write\": \"prettier --write \\\"src/**/*.ts\\\" \\\"__tests__/**/*.ts\\\"\"\n    },\n    \"lint-staged\": {\n        \"*.{ts,tsx,js,jsx}\": [\n            \"prettier --write\"\n        ]\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/mobxjs/mobx-state-tree.git\"\n    },\n    \"author\": \"Michel Weststrate\",\n    \"license\": \"MIT\",\n    \"bugs\": {\n        \"url\": \"https://github.com/mobxjs/mobx-state-tree/issues\"\n    },\n    \"files\": [\n        \"dist/\"\n    ],\n    \"dependencies\": {\n        \"ts-essentials\": \"^9.4.1\"\n    },\n    \"devDependencies\": {\n        \"@size-limit/preset-big-lib\": \"^5.0.3\",\n        \"@types/bun\": \"^1.0.6\",\n        \"@types/node\": \"^12.0.2\",\n        \"concat\": \"^1.0.3\",\n        \"coveralls\": \"^3.1.0\",\n        \"cpr\": \"^3.0.1\",\n        \"husky\": \"^7.0.0\",\n        \"lint-staged\": \"^11.1.2\",\n        \"mobx\": \"^6.13.1\",\n        \"prettier\": \"3.4.1\",\n        \"rollup\": \"^2.18.1\",\n        \"rollup-plugin-commonjs\": \"^10.0.0\",\n        \"rollup-plugin-filesize\": \"^9.0.1\",\n        \"rollup-plugin-node-resolve\": \"^5.0.0\",\n        \"rollup-plugin-replace\": \"^2.1.0\",\n        \"rollup-plugin-terser\": \"^6.1.0\",\n        \"shx\": \"^0.3.2\",\n        \"size-limit\": \"^5.0.3\",\n        \"ts-node\": \"^8.10.2\",\n        \"tslib\": \"^2.0.0\",\n        \"tslint\": \"^6.1.3\",\n        \"tslint-config-prettier\": \"^1.18.0\",\n        \"typedoc\": \"0.15.8\",\n        \"typedoc-plugin-markdown\": \"2.2.11\",\n        \"typescript\": \"^5.3.3\",\n        \"yarn-deduplicate\": \"^3.1.0\"\n    },\n    \"peerDependencies\": {\n        \"mobx\": \"^6.3.0\"\n    },\n    \"keywords\": [\n        \"mobx\",\n        \"mobx-state-tree\",\n        \"promise\",\n        \"reactive\",\n        \"frp\",\n        \"functional-reactive-programming\",\n        \"state management\"\n    ],\n    \"gitHead\": \"27ec7ac0b0743a367fb01a7f40192f3042bd91f2\",\n    \"prettier\": {\n        \"printWidth\": 100,\n        \"semi\": false,\n        \"tabWidth\": 4,\n        \"singleQuote\": false,\n        \"trailingComma\": \"none\",\n        \"arrowParens\": \"avoid\",\n        \"useTabs\": false\n    },\n    \"size-limit\": [\n        {\n            \"path\": \"./dist/mobx-state-tree.min.js\",\n            \"limit\": \"25 KB\",\n            \"webpack\": false,\n            \"running\": false\n        }\n    ]\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import * as path from \"path\"\nimport filesize from \"rollup-plugin-filesize\"\nimport resolve from \"rollup-plugin-node-resolve\"\nimport { terser } from \"rollup-plugin-terser\"\nimport replace from \"rollup-plugin-replace\"\n\nconst devPlugins = () => [resolve(), filesize()]\n\n// For umd builds, set process.env.NODE_ENV to \"development\" since 'process' is not available in the browser\nconst devPluginsUmd = () => [\n  resolve(),\n  replace({ \"process.env.NODE_ENV\": JSON.stringify(\"development\") }),\n  filesize()\n]\n\nconst prodPlugins = () => [\n  resolve(),\n  replace({ \"process.env.NODE_ENV\": JSON.stringify(\"production\") }),\n  terser(),\n  filesize()\n]\n\nconst config = (outFile, format, mode) => ({\n  input: \"./lib/src/index.js\",\n  output: {\n    file: path.join(\"./dist\", outFile),\n    format: format,\n    globals: {\n      mobx: \"mobx\"\n    },\n    name: format === \"umd\" ? \"mobxStateTree\" : undefined\n  },\n  external: [\"mobx\"],\n  plugins: mode === \"production\" ? prodPlugins() : format === \"umd\" ? devPluginsUmd() : devPlugins()\n})\n\nexport default [\n  config(\"mobx-state-tree.js\", \"cjs\", \"development\"),\n  config(\"mobx-state-tree.min.js\", \"cjs\", \"production\"),\n  config(\"mobx-state-tree.umd.js\", \"umd\", \"development\"),\n  config(\"mobx-state-tree.umd.min.js\", \"umd\", \"production\"),\n  config(\"mobx-state-tree.module.js\", \"es\", \"development\")\n]\n"
  },
  {
    "path": "scripts/fix-docs-source-links.js",
    "content": "/**\n * Rewrites GitHub source links in generated API docs:\n * 1. Point to the canonical mobxjs/mobx-state-tree repo (so forks produce main-repo links).\n * 2. Use the last commit hash that changed each linked file (from git),\n * so links stay valid and minimize the changes upon subsequent doc builds.\n */\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst { execSync } = require(\"child_process\")\n\nconst DOCS_API = path.join(__dirname, \"..\", \"docs\", \"API\")\nconst REPO_ROOT = path.join(__dirname, \"..\")\nconst CANONICAL_BASE = \"https://github.com/mobxjs/mobx-state-tree/blob\"\n\n// Match \"Defined in [path:line](https://github.com/owner/mobx-state-tree/blob/hash/path#Lline)\"\nconst LINK_REGEX = /https:\\/\\/github\\.com\\/[^/]+\\/mobx-state-tree\\/blob\\/[a-f0-9]+\\/([^#]+)#L(\\d+)/g\n\nfunction getAllMdContent(dir) {\n    let out = \"\"\n    const entries = fs.readdirSync(dir, { withFileTypes: true })\n    for (const ent of entries) {\n        const full = path.join(dir, ent.name)\n        if (ent.isDirectory()) {\n            out += getAllMdContent(full)\n        } else if (ent.name.endsWith(\".md\")) {\n            out += fs.readFileSync(full, \"utf8\")\n        }\n    }\n    return out\n}\n\nfunction getUniqueFilePathsFromLinks(content) {\n    const paths = new Set()\n    let m\n    while ((m = LINK_REGEX.exec(content)) !== null) {\n        paths.add(m[1]) // file path (e.g. src/utils.ts)\n    }\n    return [...paths]\n}\n\nfunction buildFileToHashMap(filePaths) {\n    const map = {}\n    for (const filePath of filePaths) {\n        try {\n            const hash = execSync(\"git log -1 --format=%h -- \" + JSON.stringify(filePath), {\n                cwd: REPO_ROOT,\n                encoding: \"utf8\"\n            }).trim()\n            if (hash) map[filePath] = hash\n        } catch (_) {\n            // file not in git or not found, skip\n        }\n    }\n    return map\n}\n\nfunction fixFile(filePath, fileToHash) {\n    let content = fs.readFileSync(filePath, \"utf8\")\n    const fixed = content.replace(LINK_REGEX, (match, pathInRepo, line) => {\n        const hash = fileToHash[pathInRepo]\n        if (!hash) return match\n        return `${CANONICAL_BASE}/${hash}/${pathInRepo}#L${line}`\n    })\n    if (fixed !== content) {\n        fs.writeFileSync(filePath, fixed, \"utf8\")\n    }\n}\n\nfunction walk(dir, fileToHash) {\n    const entries = fs.readdirSync(dir, { withFileTypes: true })\n    for (const ent of entries) {\n        const full = path.join(dir, ent.name)\n        if (ent.isDirectory()) {\n            walk(full, fileToHash)\n        } else if (ent.name.endsWith(\".md\")) {\n            fixFile(full, fileToHash)\n        }\n    }\n}\n\nif (!fs.existsSync(DOCS_API)) {\n    console.warn(\"fix-docs-source-links: docs/API not found, skipping\")\n    process.exit(0)\n}\n\nconst allContent = getAllMdContent(DOCS_API)\nconst uniquePaths = getUniqueFilePathsFromLinks(allContent)\nconst fileToHash = buildFileToHashMap(uniquePaths)\nwalk(DOCS_API, fileToHash)\n"
  },
  {
    "path": "scripts/generate-compose-type.js",
    "content": "const { getDeclaration } = require(\"./generate-shared\")\n\nlet str = `// generated with ${__filename}\\n`\n\nconst minArgs = 2\nconst maxArgs = 10\nconst preParam = \"name: string, \"\n\nconst returnTypeTransform = (rt) => {\n  // [['PA', 'PB', 'PC'], ['OA', 'OB', 'OC'], ['FCA', 'FCB', 'FCC'], ['FSA', 'FSB', 'FSC']]\n  // ->\n  // [['PA', 'PB', 'PC'], no change\n  //  ['OA', 'OB', 'OC'], no change\n  //  ['_CustomJoin<FCA, _CustomJoin<FCB, FCC>>']\n  //  ['_CustomJoin<FSA, _CustomJoin<FSB, FSC>>']]\n\n  const [props, others, fixedC, fixedS] = rt\n\n  function customJoin(left) {\n    if (left.length === 1) {\n      return left[0]\n    }\n    const [a, ...rest] = left\n    return `_CustomJoin<${a}, ${customJoin(rest)}>`\n  }\n\n  return [props, others, [customJoin(fixedC)], [customJoin(fixedS)]]\n}\n\nfor (let i = minArgs; i < maxArgs; i++) {\n  str += getDeclaration(\n    \"compose\",\n    \"IModelType\",\n    [\"P\", \"O\", \"FC\", \"FS\"],\n    i,\n    preParam,\n    \"&\",\n    \"IModelType\",\n    returnTypeTransform\n  )\n  str += getDeclaration(\n    \"compose\",\n    \"IModelType\",\n    [\"P\", \"O\", \"FC\", \"FS\"],\n    i,\n    null,\n    \"&\",\n    \"IModelType\",\n    returnTypeTransform\n  )\n}\n\nconsole.log(str)\n"
  },
  {
    "path": "scripts/generate-shared.js",
    "content": "const alfa = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\nfunction getTemplateVar(templateVar, argNumber) {\n  return `${templateVar}${alfa[argNumber]}`\n}\n\nfunction getTemplateVars(templateVars, argNumber) {\n  return templateVars.map((tv) => getTemplateVar(tv, argNumber))\n}\n\nexports.getDeclaration = function getDeclaration(\n  funcName,\n  type,\n  templateVars,\n  args,\n  preParam,\n  operationChar,\n  outType = type,\n  allReturnTypesTransform = (x) => x\n) {\n  let str = \"// prettier-ignore\\n\"\n\n  let allTemplateVars = []\n  for (let i = 0; i < args; i++) {\n    allTemplateVars = allTemplateVars.concat(getTemplateVars(templateVars, i))\n  }\n  allTemplateVars = allTemplateVars.map((tv) =>\n    tv.startsWith(\"P\") ? `${tv} extends ModelProperties` : tv\n  )\n  str += `export function ${funcName}<${allTemplateVars.join(\", \")}>(`\n\n  if (preParam) {\n    str += preParam\n  }\n\n  const allParams = []\n  for (let i = 0; i < args; i++) {\n    allParams.push(`${alfa[i]}: ${type}<${getTemplateVars(templateVars, i).join(\", \")}>`)\n  }\n  str += `${allParams.join(\", \")}`\n  str += \")\"\n\n  let allReturnTypes = []\n  for (const templateVar of templateVars) {\n    let union = []\n    for (let i = 0; i < args; i++) {\n      union.push(getTemplateVar(templateVar, i))\n    }\n    allReturnTypes.push(union)\n  }\n  allReturnTypes = allReturnTypesTransform(allReturnTypes)\n  str += `: ${outType}<${allReturnTypes.map((u) => u.join(` ${operationChar} `)).join(\", \")}>`\n\n  return str + \"\\n\"\n}\n"
  },
  {
    "path": "src/core/action.ts",
    "content": "import { action as mobxAction } from \"mobx\"\nimport {\n    getStateTreeNode,\n    MstError,\n    argsToArray,\n    IDisposer,\n    getRoot,\n    Hook,\n    IAnyStateTreeNode,\n    warnError,\n    AnyObjectNode,\n    devMode,\n    IActionContext\n} from \"../internal\"\n\nexport type IMiddlewareEventType =\n    | \"action\"\n    | \"flow_spawn\"\n    | \"flow_resume\"\n    | \"flow_resume_error\"\n    | \"flow_return\"\n    | \"flow_throw\"\n// | \"task_spawn TODO, see #273\"\n\nexport interface IMiddlewareEvent extends IActionContext {\n    /** Event type */\n    readonly type: IMiddlewareEventType\n\n    /** Parent event unique id */\n    readonly parentId: number\n    /** Parent event object */\n    readonly parentEvent: IMiddlewareEvent | undefined\n\n    /** Root event unique id */\n    readonly rootId: number\n    /** Id of all events, from root until current (excluding current) */\n    readonly allParentIds: number[]\n}\n\nexport interface FunctionWithFlag extends Function {\n    _isMSTAction?: boolean\n    _isFlowAction?: boolean\n}\n\n/**\n * @internal\n * @hidden\n */\nexport type IMiddleware = {\n    handler: IMiddlewareHandler\n    includeHooks: boolean\n}\n\nexport type IMiddlewareHandler = (\n    actionCall: IMiddlewareEvent,\n    next: (actionCall: IMiddlewareEvent, callback?: (value: any) => any) => void,\n    abort: (value: any) => void\n) => any\n\nlet nextActionId = 1\nlet currentActionContext: IMiddlewareEvent | undefined\n\n/**\n * @internal\n * @hidden\n */\nexport function getCurrentActionContext() {\n    return currentActionContext\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function getNextActionId() {\n    return nextActionId++\n}\n\n/**\n * @internal\n * @hidden should only ever be used for testing within MST itself\n */\nexport function resetNextActionId() {\n    if (!devMode() && !process.env.MST_TESTING) {\n        throw new Error(\"resetNextActionId should only be used when testing MST\")\n    }\n    nextActionId = 1\n}\n\n// TODO: optimize away entire action context if there is no middleware in tree?\n/**\n * @internal\n * @hidden\n */\nexport function runWithActionContext(context: IMiddlewareEvent, fn: Function) {\n    const node = getStateTreeNode(context.context)\n\n    if (context.type === \"action\") {\n        node.assertAlive({\n            actionContext: context\n        })\n    }\n\n    const baseIsRunningAction = node._isRunningAction\n    node._isRunningAction = true\n    const previousContext = currentActionContext\n    currentActionContext = context\n    try {\n        return runMiddleWares(node, context, fn)\n    } finally {\n        currentActionContext = previousContext\n        node._isRunningAction = baseIsRunningAction\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function getParentActionContext(parentContext: IMiddlewareEvent | undefined) {\n    if (!parentContext) return undefined\n    if (parentContext.type === \"action\") return parentContext\n    return parentContext.parentActionEvent\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function createActionInvoker<T extends FunctionWithFlag>(\n    target: IAnyStateTreeNode,\n    name: string,\n    fn: T\n) {\n    const res = function () {\n        const id = getNextActionId()\n        const parentContext = currentActionContext\n        const parentActionContext = getParentActionContext(parentContext)\n\n        return runWithActionContext(\n            {\n                type: \"action\",\n                name,\n                id,\n                args: argsToArray(arguments),\n                context: target,\n                tree: getRoot(target),\n                rootId: parentContext ? parentContext.rootId : id,\n                parentId: parentContext ? parentContext.id : 0,\n                allParentIds: parentContext\n                    ? [...parentContext.allParentIds, parentContext.id]\n                    : [],\n                parentEvent: parentContext,\n                parentActionEvent: parentActionContext\n            },\n            fn\n        )\n    }\n    ;(res as FunctionWithFlag)._isMSTAction = true\n    ;(res as FunctionWithFlag)._isFlowAction = fn._isFlowAction\n    return res\n}\n\n/**\n * Middleware can be used to intercept any action is invoked on the subtree where it is attached.\n * If a tree is protected (by default), this means that any mutation of the tree will pass through your middleware.\n *\n * For more details, see the [middleware docs](concepts/middleware.md)\n *\n * @param target Node to apply the middleware to.\n * @param middleware Middleware to apply.\n * @returns A callable function to dispose the middleware.\n */\nexport function addMiddleware(\n    target: IAnyStateTreeNode,\n    handler: IMiddlewareHandler,\n    includeHooks: boolean = true\n): IDisposer {\n    const node = getStateTreeNode(target)\n    if (devMode()) {\n        if (!node.isProtectionEnabled) {\n            warnError(\n                \"It is recommended to protect the state tree before attaching action middleware, as otherwise it cannot be guaranteed that all changes are passed through middleware. See `protect`\"\n            )\n        }\n    }\n    return node.addMiddleWare(handler, includeHooks)\n}\n\n/**\n * Binds middleware to a specific action.\n *\n * Example:\n * ```ts\n * type.actions(self => {\n *   function takeA____() {\n *       self.toilet.donate()\n *       self.wipe()\n *       self.wipe()\n *       self.toilet.flush()\n *   }\n *   return {\n *     takeA____: decorate(atomic, takeA____)\n *   }\n * })\n * ```\n *\n * @param handler\n * @param fn\n * @param includeHooks\n * @returns The original function\n */\nexport function decorate<T extends Function>(\n    handler: IMiddlewareHandler,\n    fn: T,\n    includeHooks = true\n): T {\n    const middleware: IMiddleware = { handler, includeHooks }\n    ;(fn as any).$mst_middleware = (fn as any).$mst_middleware || []\n    ;(fn as any).$mst_middleware.push(middleware)\n    return fn\n}\n\nclass CollectedMiddlewares {\n    private arrayIndex = 0\n    private inArrayIndex = 0\n    private middlewares: IMiddleware[][] = []\n\n    constructor(node: AnyObjectNode, fn: Function) {\n        // we just push middleware arrays into an array of arrays to avoid making copies\n        if ((fn as any).$mst_middleware) {\n            this.middlewares.push((fn as any).$mst_middleware)\n        }\n        let n: AnyObjectNode | null = node\n        // Find all middlewares. Optimization: cache this?\n        while (n) {\n            if (n.middlewares) this.middlewares.push(n.middlewares)\n            n = n.parent\n        }\n    }\n\n    get isEmpty() {\n        return this.middlewares.length <= 0\n    }\n\n    getNextMiddleware(): IMiddleware | undefined {\n        const array = this.middlewares[this.arrayIndex]\n        if (!array) return undefined\n        const item = array[this.inArrayIndex++]\n        if (!item) {\n            this.arrayIndex++\n            this.inArrayIndex = 0\n            return this.getNextMiddleware()\n        }\n        return item\n    }\n}\n\nfunction runMiddleWares(\n    node: AnyObjectNode,\n    baseCall: IMiddlewareEvent,\n    originalFn: Function\n): any {\n    const middlewares = new CollectedMiddlewares(node, originalFn)\n    // Short circuit\n    if (middlewares.isEmpty) return mobxAction(originalFn).apply(null, baseCall.args)\n\n    let result: any = null\n\n    function runNextMiddleware(call: IMiddlewareEvent): any {\n        const middleware = middlewares.getNextMiddleware()\n        const handler = middleware && middleware.handler\n\n        if (!handler) {\n            return mobxAction(originalFn).apply(null, call.args)\n        }\n\n        // skip hooks if asked to\n        if (!middleware!.includeHooks && (Hook as any)[call.name]) {\n            return runNextMiddleware(call)\n        }\n\n        let nextInvoked = false\n        function next(call2: IMiddlewareEvent, callback?: (value: any) => any): void {\n            nextInvoked = true\n            // the result can contain\n            // - the non manipulated return value from an action\n            // - the non manipulated abort value\n            // - one of the above but manipulated through the callback function\n            result = runNextMiddleware(call2)\n            if (callback) {\n                result = callback(result)\n            }\n        }\n\n        let abortInvoked = false\n        function abort(value: any) {\n            abortInvoked = true\n            // overwrite the result\n            // can be manipulated through middlewares earlier in the queue using the callback fn\n            result = value\n        }\n\n        handler(call, next, abort)\n        if (devMode()) {\n            if (!nextInvoked && !abortInvoked) {\n                const node2 = getStateTreeNode(call.tree)\n                throw new MstError(\n                    `Neither the next() nor the abort() callback within the middleware ${handler.name} for the action: \"${call.name}\" on the node: ${node2.type.name} was invoked.`\n                )\n            } else if (nextInvoked && abortInvoked) {\n                const node2 = getStateTreeNode(call.tree)\n                throw new MstError(\n                    `The next() and abort() callback within the middleware ${handler.name} for the action: \"${call.name}\" on the node: ${node2.type.name} were invoked.`\n                )\n            }\n        }\n        return result\n    }\n    return runNextMiddleware(baseCall)\n}\n"
  },
  {
    "path": "src/core/actionContext.ts",
    "content": "import { IAnyStateTreeNode, IMiddlewareEvent } from \"../internal\"\nimport { getCurrentActionContext } from \"./action\"\n\nexport interface IActionContext {\n    /** Event name (action name for actions) */\n    readonly name: string\n\n    /** Event unique id */\n    readonly id: number\n\n    /** Parent action event object */\n    readonly parentActionEvent: IMiddlewareEvent | undefined\n\n    /** Event context (node where the action was invoked) */\n    readonly context: IAnyStateTreeNode\n    /** Event tree (root node of the node where the action was invoked) */\n    readonly tree: IAnyStateTreeNode\n\n    /** Event arguments in an array (action arguments for actions) */\n    readonly args: any[]\n}\n\n/**\n * Returns the currently executing MST action context, or undefined if none.\n */\nexport function getRunningActionContext(): IActionContext | undefined {\n    let current = getCurrentActionContext()\n    while (current && current.type !== \"action\") {\n        current = current.parentActionEvent\n    }\n    return current\n}\n\nfunction _isActionContextThisOrChildOf(\n    actionContext: IActionContext,\n    sameOrParent: number | IActionContext | IMiddlewareEvent,\n    includeSame: boolean\n) {\n    const parentId = typeof sameOrParent === \"number\" ? sameOrParent : sameOrParent.id\n\n    let current: IActionContext | IMiddlewareEvent | undefined = includeSame\n        ? actionContext\n        : actionContext.parentActionEvent\n    while (current) {\n        if (current.id === parentId) {\n            return true\n        }\n        current = current.parentActionEvent\n    }\n    return false\n}\n\n/**\n * Returns if the given action context is a parent of this action context.\n */\nexport function isActionContextChildOf(\n    actionContext: IActionContext,\n    parent: number | IActionContext | IMiddlewareEvent\n) {\n    return _isActionContextThisOrChildOf(actionContext, parent, false)\n}\n\n/**\n * Returns if the given action context is this or a parent of this action context.\n */\nexport function isActionContextThisOrChildOf(\n    actionContext: IActionContext,\n    parentOrThis: number | IActionContext | IMiddlewareEvent\n) {\n    return _isActionContextThisOrChildOf(actionContext, parentOrThis, true)\n}\n"
  },
  {
    "path": "src/core/flow.ts",
    "content": "import { argsToArray, MstError, setImmediateWithFallback } from \"../utils\"\nimport {\n    FunctionWithFlag,\n    getCurrentActionContext,\n    getNextActionId,\n    getParentActionContext,\n    IMiddlewareEventType,\n    runWithActionContext\n} from \"./action\"\n\n/**\n * @hidden\n */\nexport type FlowReturn<R> = R extends Promise<infer T> ? T : R\n\n/**\n * See [asynchronous actions](concepts/async-actions.md).\n *\n * @returns The flow as a promise.\n */\nexport function flow<R, Args extends any[]>(\n    generator: (...args: Args) => Generator<PromiseLike<any>, R, any>\n): (...args: Args) => Promise<FlowReturn<R>> {\n    return createFlowSpawner(generator.name, generator) as any\n}\n\n/**\n * @deprecated Not needed since TS3.6.\n * Used for TypeScript to make flows that return a promise return the actual promise result.\n *\n * @param val\n * @returns\n */\nexport function castFlowReturn<T>(val: T): T {\n    return val as any\n}\n\n/**\n * @experimental\n * experimental api - might change on minor/patch releases\n *\n * Convert a promise-returning function to a generator-returning one.\n * This is intended to allow for usage of `yield*` in async actions to\n * retain the promise return type.\n *\n * Example:\n * ```ts\n * function getDataAsync(input: string): Promise<number> { ... }\n * const getDataGen = toGeneratorFunction(getDataAsync);\n *\n * const someModel.actions(self => ({\n *   someAction: flow(function*() {\n *     // value is typed as number\n *     const value = yield* getDataGen(\"input value\");\n *     ...\n *   })\n * }))\n * ```\n */\nexport function toGeneratorFunction<R, Args extends any[]>(p: (...args: Args) => Promise<R>) {\n    return function* (...args: Args) {\n        return (yield p(...args)) as R\n    }\n}\n\n/**\n * @experimental\n * experimental api - might change on minor/patch releases\n *\n * Convert a promise to a generator yielding that promise\n * This is intended to allow for usage of `yield*` in async actions to\n * retain the promise return type.\n *\n * Example:\n * ```ts\n * function getDataAsync(input: string): Promise<number> { ... }\n *\n * const someModel.actions(self => ({\n *   someAction: flow(function*() {\n *     // value is typed as number\n *     const value = yield* toGenerator(getDataAsync(\"input value\"));\n *     ...\n *   })\n * }))\n * ```\n */\nexport function* toGenerator<R>(p: Promise<R>) {\n    return (yield p) as R\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function createFlowSpawner(name: string, generator: FunctionWithFlag) {\n    const spawner = function flowSpawner(this: any) {\n        // Implementation based on https://github.com/tj/co/blob/master/index.js\n        const runId = getNextActionId()\n        const parentContext = getCurrentActionContext()!\n        if (!parentContext) {\n            throw new MstError(\"a mst flow must always have a parent context\")\n        }\n        const parentActionContext = getParentActionContext(parentContext)\n        if (!parentActionContext) {\n            throw new MstError(\"a mst flow must always have a parent action context\")\n        }\n\n        const contextBase = {\n            name,\n            id: runId,\n            tree: parentContext.tree,\n            context: parentContext.context,\n            parentId: parentContext.id,\n            allParentIds: [...parentContext.allParentIds, parentContext.id],\n            rootId: parentContext.rootId,\n            parentEvent: parentContext,\n            parentActionEvent: parentActionContext\n        }\n\n        const args = arguments\n\n        function wrap(fn: any, type: IMiddlewareEventType, arg: any) {\n            fn.$mst_middleware = (spawner as any).$mst_middleware // pick up any middleware attached to the flow\n            return runWithActionContext(\n                {\n                    ...contextBase,\n                    type,\n                    args: [arg]\n                },\n                fn\n            )\n        }\n\n        return new Promise(function (resolve, reject) {\n            let gen: any\n            const init = function asyncActionInit() {\n                gen = generator.apply(null, arguments)\n                onFulfilled(undefined) // kick off the flow\n            }\n            ;(init as any).$mst_middleware = (spawner as any).$mst_middleware\n\n            runWithActionContext(\n                {\n                    ...contextBase,\n                    type: \"flow_spawn\",\n                    args: argsToArray(args)\n                },\n                init\n            )\n\n            function onFulfilled(res: any) {\n                let ret\n                try {\n                    // prettier-ignore\n                    const cancelError: any = wrap((r: any) => { ret = gen.next(r) }, \"flow_resume\", res)\n                    if (cancelError instanceof Error) {\n                        ret = gen.throw(cancelError)\n                    }\n                } catch (e) {\n                    // prettier-ignore\n                    setImmediateWithFallback(() => {\n                        wrap((r: any) => { reject(e) }, \"flow_throw\", e)\n                    })\n                    return\n                }\n                next(ret)\n                return\n            }\n\n            function onRejected(err: any) {\n                let ret\n                try {\n                    // prettier-ignore\n                    wrap((r: any) => { ret = gen.throw(r) }, \"flow_resume_error\", err) // or yieldError?\n                } catch (e) {\n                    // prettier-ignore\n                    setImmediateWithFallback(() => {\n                        wrap((r: any) => { reject(e) }, \"flow_throw\", e)\n                    })\n                    return\n                }\n                next(ret)\n            }\n\n            function next(ret: any) {\n                if (ret.done) {\n                    // prettier-ignore\n                    setImmediateWithFallback(() => {\n                        wrap((r: any) => { resolve(r) }, \"flow_return\", ret.value)\n                    })\n                    return\n                }\n                // TODO: support more type of values? See https://github.com/tj/co/blob/249bbdc72da24ae44076afd716349d2089b31c4c/index.js#L100\n                if (!ret.value || typeof ret.value.then !== \"function\") {\n                    // istanbul ignore next\n                    throw new MstError(\"Only promises can be yielded to `async`, got: \" + ret)\n                }\n                return ret.value.then(onFulfilled, onRejected)\n            }\n        })\n    }\n    ;(spawner as FunctionWithFlag)._isFlowAction = true\n    return spawner\n}\n"
  },
  {
    "path": "src/core/json-patch.ts",
    "content": "import { MstError, stringStartsWith } from \"../internal\"\n\n/**\n * https://tools.ietf.org/html/rfc6902\n * http://jsonpatch.com/\n */\nexport interface IJsonPatch {\n    readonly op: \"replace\" | \"add\" | \"remove\"\n    readonly path: string\n    readonly value?: any\n}\n\nexport interface IReversibleJsonPatch extends IJsonPatch {\n    readonly oldValue: any // This goes beyond JSON-patch, but makes sure each patch can be inverse applied\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function splitPatch(patch: IReversibleJsonPatch): [IJsonPatch, IJsonPatch] {\n    if (!(\"oldValue\" in patch))\n        throw new MstError(`Patches without \\`oldValue\\` field cannot be inversed`)\n    return [stripPatch(patch), invertPatch(patch)]\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function stripPatch(patch: IReversibleJsonPatch): IJsonPatch {\n    // strips `oldvalue` information from the patch, so that it becomes a patch conform the json-patch spec\n    // this removes the ability to undo the patch\n    switch (patch.op) {\n        case \"add\":\n            return { op: \"add\", path: patch.path, value: patch.value }\n        case \"remove\":\n            return { op: \"remove\", path: patch.path }\n        case \"replace\":\n            return { op: \"replace\", path: patch.path, value: patch.value }\n    }\n}\n\nfunction invertPatch(patch: IReversibleJsonPatch): IJsonPatch {\n    switch (patch.op) {\n        case \"add\":\n            return {\n                op: \"remove\",\n                path: patch.path\n            }\n        case \"remove\":\n            return {\n                op: \"add\",\n                path: patch.path,\n                value: patch.oldValue\n            }\n        case \"replace\":\n            return {\n                op: \"replace\",\n                path: patch.path,\n                value: patch.oldValue\n            }\n    }\n}\n\n/**\n * Simple simple check to check it is a number.\n */\nfunction isNumber(x: string): boolean {\n    return typeof x === \"number\"\n}\n\n/**\n * Escape slashes and backslashes.\n *\n * http://tools.ietf.org/html/rfc6901\n */\nexport function escapeJsonPath(path: string): string {\n    if (isNumber(path) === true) {\n        return \"\" + path\n    }\n    if (path.indexOf(\"/\") === -1 && path.indexOf(\"~\") === -1) return path\n    return path.replace(/~/g, \"~0\").replace(/\\//g, \"~1\")\n}\n\n/**\n * Unescape slashes and backslashes.\n */\nexport function unescapeJsonPath(path: string): string {\n    return path.replace(/~1/g, \"/\").replace(/~0/g, \"~\")\n}\n\n/**\n * Generates a json-path compliant json path from path parts.\n *\n * @param path\n * @returns\n */\nexport function joinJsonPath(path: string[]): string {\n    // `/` refers to property with an empty name, while `` refers to root itself!\n    if (path.length === 0) return \"\"\n\n    const getPathStr = (p: string[]) => p.map(escapeJsonPath).join(\"/\")\n    if (path[0] === \".\" || path[0] === \"..\") {\n        // relative\n        return getPathStr(path)\n    } else {\n        // absolute\n        return \"/\" + getPathStr(path)\n    }\n}\n\n/**\n * Splits and decodes a json path into several parts.\n *\n * @param path\n * @returns\n */\nexport function splitJsonPath(path: string): string[] {\n    // `/` refers to property with an empty name, while `` refers to root itself!\n    const parts = path.split(\"/\").map(unescapeJsonPath)\n\n    const valid =\n        path === \"\" ||\n        path === \".\" ||\n        path === \"..\" ||\n        stringStartsWith(path, \"/\") ||\n        stringStartsWith(path, \"./\") ||\n        stringStartsWith(path, \"../\")\n    if (!valid) {\n        throw new MstError(\n            `a json path must be either rooted, empty or relative, but got '${path}'`\n        )\n    }\n\n    // '/a/b/c' -> [\"a\", \"b\", \"c\"]\n    // '../../b/c' -> [\"..\", \"..\", \"b\", \"c\"]\n    // '' -> []\n    // '/' -> ['']\n    // './a' -> [\".\", \"a\"]\n    // /./a' -> [\".\", \"a\"] equivalent to './a'\n\n    if (parts[0] === \"\") {\n        parts.shift()\n    }\n    return parts\n}\n"
  },
  {
    "path": "src/core/mst-operations.ts",
    "content": "import { isComputedProp, isObservableProp } from \"mobx\"\nimport {\n    IAnyStateTreeNode,\n    IType,\n    IAnyModelType,\n    getStateTreeNode,\n    IStateTreeNode,\n    isStateTreeNode,\n    IJsonPatch,\n    splitJsonPath,\n    asArray,\n    MstError,\n    IDisposer,\n    resolveNodeByPath,\n    getRelativePathBetweenNodes,\n    freeze,\n    IAnyType,\n    isModelType,\n    InvalidReferenceError,\n    normalizeIdentifier,\n    ReferenceIdentifier,\n    AnyObjectNode,\n    assertIsType,\n    assertIsStateTreeNode,\n    TypeOfValue,\n    assertIsFunction,\n    assertIsNumber,\n    assertIsString,\n    assertArg,\n    assertIsValidIdentifier,\n    IActionContext,\n    getRunningActionContext,\n    IAnyComplexType\n} from \"../internal\"\n\n/** @hidden */\nexport type TypeOrStateTreeNodeToStateTreeNode<T extends IAnyType | IAnyStateTreeNode> =\n    T extends IType<any, any, infer TT> ? TT & IStateTreeNode<T> : T\n\n/**\n * Returns the _actual_ type of the given tree node. (Or throws)\n *\n * @param object\n * @returns\n */\nexport function getType(object: IAnyStateTreeNode): IAnyComplexType {\n    assertIsStateTreeNode(object, 1)\n\n    return getStateTreeNode(object).type\n}\n\n/**\n * Returns the _declared_ type of the given sub property of an object, array or map.\n * In the case of arrays and maps the property name is optional and will be ignored.\n *\n * Example:\n * ```ts\n * const Box = types.model({ x: 0, y: 0 })\n * const box = Box.create()\n *\n * console.log(getChildType(box, \"x\").name) // 'number'\n * ```\n *\n * @param object\n * @param propertyName\n * @returns\n */\nexport function getChildType(object: IAnyStateTreeNode, propertyName?: string): IAnyType {\n    assertIsStateTreeNode(object, 1)\n\n    return getStateTreeNode(object).getChildType(propertyName)\n}\n\n/**\n * Registers a function that will be invoked for each mutation that is applied to the provided model instance, or to any of its children.\n * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details. onPatch events are emitted immediately and will not await the end of a transaction.\n * Patches can be used to deeply observe a model tree.\n *\n * @param target the model instance from which to receive patches\n * @param callback the callback that is invoked for each patch. The reversePatch is a patch that would actually undo the emitted patch\n * @returns function to remove the listener\n */\nexport function onPatch(\n    target: IAnyStateTreeNode,\n    callback: (patch: IJsonPatch, reversePatch: IJsonPatch) => void\n): IDisposer {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsFunction(callback, 2)\n\n    return getStateTreeNode(target).onPatch(callback)\n}\n\n/**\n * Registers a function that is invoked whenever a new snapshot for the given model instance is available.\n * The listener will only be fire at the end of the current MobX (trans)action.\n * See [snapshots](https://github.com/mobxjs/mobx-state-tree#snapshots) for more details.\n *\n * @param target\n * @param callback\n * @returns\n */\nexport function onSnapshot<S>(\n    target: IStateTreeNode<IType<any, S, any>>,\n    callback: (snapshot: S) => void\n): IDisposer {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsFunction(callback, 2)\n\n    return getStateTreeNode(target).onSnapshot(callback)\n}\n\n/**\n * Applies a JSON-patch to the given model instance or bails out if the patch couldn't be applied\n * See [patches](https://github.com/mobxjs/mobx-state-tree#patches) for more details.\n *\n * Can apply a single past, or an array of patches.\n *\n * @param target\n * @param patch\n * @returns\n */\nexport function applyPatch(\n    target: IAnyStateTreeNode,\n    patch: IJsonPatch | ReadonlyArray<IJsonPatch>\n): void {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertArg(patch, p => typeof p === \"object\", \"object or array\", 2)\n\n    getStateTreeNode(target).applyPatches(asArray(patch))\n}\n\nexport interface IPatchRecorder {\n    patches: ReadonlyArray<IJsonPatch>\n    inversePatches: ReadonlyArray<IJsonPatch>\n    reversedInversePatches: ReadonlyArray<IJsonPatch>\n    readonly recording: boolean\n    stop(): void\n    resume(): void\n    replay(target?: IAnyStateTreeNode): void\n    undo(target?: IAnyStateTreeNode): void\n}\n\n/**\n * Small abstraction around `onPatch` and `applyPatch`, attaches a patch listener to a tree and records all the patches.\n * Returns a recorder object with the following signature:\n *\n * Example:\n * ```ts\n * export interface IPatchRecorder {\n *      // the recorded patches\n *      patches: IJsonPatch[]\n *      // the inverse of the recorded patches\n *      inversePatches: IJsonPatch[]\n *      // true if currently recording\n *      recording: boolean\n *      // stop recording patches\n *      stop(): void\n *      // resume recording patches\n *      resume(): void\n *      // apply all the recorded patches on the given target (the original subject if omitted)\n *      replay(target?: IAnyStateTreeNode): void\n *      // reverse apply the recorded patches on the given target  (the original subject if omitted)\n *      // stops the recorder if not already stopped\n *      undo(): void\n * }\n * ```\n *\n * The optional filter function allows to skip recording certain patches.\n *\n * @param subject\n * @param filter\n * @returns\n */\nexport function recordPatches(\n    subject: IAnyStateTreeNode,\n    filter?: (\n        patch: IJsonPatch,\n        inversePatch: IJsonPatch,\n        actionContext: IActionContext | undefined\n    ) => boolean\n): IPatchRecorder {\n    // check all arguments\n    assertIsStateTreeNode(subject, 1)\n\n    interface IPatches {\n        patches: IJsonPatch[]\n        reversedInversePatches: IJsonPatch[]\n        inversePatches: IJsonPatch[]\n    }\n\n    const data: Pick<IPatches, \"patches\" | \"inversePatches\"> = {\n        patches: [],\n        inversePatches: []\n    }\n\n    // we will generate the immutable copy of patches on demand for public consumption\n    const publicData: Partial<IPatches> = {}\n\n    let disposer: IDisposer | undefined\n\n    const recorder: IPatchRecorder = {\n        get recording() {\n            return !!disposer\n        },\n        get patches() {\n            if (!publicData.patches) {\n                publicData.patches = data.patches.slice()\n            }\n            return publicData.patches\n        },\n        get reversedInversePatches() {\n            if (!publicData.reversedInversePatches) {\n                publicData.reversedInversePatches = data.inversePatches.slice().reverse()\n            }\n            return publicData.reversedInversePatches\n        },\n        get inversePatches() {\n            if (!publicData.inversePatches) {\n                publicData.inversePatches = data.inversePatches.slice()\n            }\n            return publicData.inversePatches\n        },\n        stop() {\n            if (disposer) {\n                disposer()\n                disposer = undefined\n            }\n        },\n        resume() {\n            if (disposer) return\n            disposer = onPatch(subject, (patch, inversePatch) => {\n                // skip patches that are asked to be filtered if there's a filter in place\n                if (filter && !filter(patch, inversePatch, getRunningActionContext())) {\n                    return\n                }\n                data.patches.push(patch)\n                data.inversePatches.push(inversePatch)\n\n                // mark immutable public patches as dirty\n                publicData.patches = undefined\n                publicData.inversePatches = undefined\n                publicData.reversedInversePatches = undefined\n            })\n        },\n        replay(target?: IAnyStateTreeNode) {\n            applyPatch(target || subject, data.patches)\n        },\n        undo(target?: IAnyStateTreeNode) {\n            applyPatch(target || subject, data.inversePatches.slice().reverse())\n        }\n    }\n\n    recorder.resume()\n    return recorder\n}\n\n/**\n * The inverse of `unprotect`.\n *\n * @param target\n */\nexport function protect(target: IAnyStateTreeNode): void {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    const node = getStateTreeNode(target)\n    if (!node.isRoot) throw new MstError(\"`protect` can only be invoked on root nodes\")\n    node.isProtectionEnabled = true\n}\n\n/**\n * By default it is not allowed to directly modify a model. Models can only be modified through actions.\n * However, in some cases you don't care about the advantages (like replayability, traceability, etc) this yields.\n * For example because you are building a PoC or don't have any middleware attached to your tree.\n *\n * In that case you can disable this protection by calling `unprotect` on the root of your tree.\n *\n * Example:\n * ```ts\n * const Todo = types.model({\n *     done: false\n * }).actions(self => ({\n *     toggle() {\n *         self.done = !self.done\n *     }\n * }))\n *\n * const todo = Todo.create()\n * todo.done = true // throws!\n * todo.toggle() // OK\n * unprotect(todo)\n * todo.done = false // OK\n * ```\n */\nexport function unprotect(target: IAnyStateTreeNode): void {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    const node = getStateTreeNode(target)\n    if (!node.isRoot) throw new MstError(\"`unprotect` can only be invoked on root nodes\")\n    node.isProtectionEnabled = false\n}\n\n/**\n * Returns true if the object is in protected mode, @see protect\n */\nexport function isProtected(target: IAnyStateTreeNode): boolean {\n    return getStateTreeNode(target).isProtected\n}\n\n/**\n * Applies a snapshot to a given model instances. Patch and snapshot listeners will be invoked as usual.\n *\n * @param target\n * @param snapshot\n * @returns\n */\nexport function applySnapshot<C>(target: IStateTreeNode<IType<C, any, any>>, snapshot: C) {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).applySnapshot(snapshot)\n}\n\n/**\n * Calculates a snapshot from the given model instance. The snapshot will always reflect the latest state but use\n * structural sharing where possible. Doesn't require MobX transactions to be completed.\n *\n * @param target\n * @param applyPostProcess If true (the default) then postProcessSnapshot gets applied.\n * @returns\n */\nexport function getSnapshot<S>(\n    target: IStateTreeNode<IType<any, S, any>>,\n    applyPostProcess = true\n): S {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    const node = getStateTreeNode(target)\n    if (applyPostProcess) return node.snapshot\n\n    return freeze(node.type.getSnapshot(node, false))\n}\n\n/**\n * Given a model instance, returns `true` if the object has a parent, that is, is part of another object, map or array.\n *\n * @param target\n * @param depth How far should we look upward? 1 by default.\n * @returns\n */\nexport function hasParent(target: IAnyStateTreeNode, depth: number = 1): boolean {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsNumber(depth, 2, 0)\n\n    let parent: AnyObjectNode | null = getStateTreeNode(target).parent\n    while (parent) {\n        if (--depth === 0) return true\n        parent = parent.parent\n    }\n    return false\n}\n\n/**\n * Returns the immediate parent of this object, or throws.\n *\n * Note that the immediate parent can be either an object, map or array, and\n * doesn't necessarily refer to the parent model.\n *\n * Please note that in child nodes access to the root is only possible\n * once the `afterAttach` hook has fired.\n *\n * @param target\n * @param depth How far should we look upward? 1 by default.\n * @returns\n */\nexport function getParent<IT extends IAnyStateTreeNode | IAnyComplexType>(\n    target: IAnyStateTreeNode,\n    depth = 1\n): TypeOrStateTreeNodeToStateTreeNode<IT> {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsNumber(depth, 2, 0)\n\n    let d = depth\n    let parent: AnyObjectNode | null = getStateTreeNode(target).parent\n    while (parent) {\n        if (--d === 0) return parent.storedValue as any\n        parent = parent.parent\n    }\n    throw new MstError(`Failed to find the parent of ${getStateTreeNode(target)} at depth ${depth}`)\n}\n\n/**\n * Given a model instance, returns `true` if the object has a parent of given type, that is, is part of another object, map or array\n *\n * @param target\n * @param type\n * @returns\n */\nexport function hasParentOfType(target: IAnyStateTreeNode, type: IAnyComplexType): boolean {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsType(type, 2)\n\n    let parent: AnyObjectNode | null = getStateTreeNode(target).parent\n    while (parent) {\n        if (type.is(parent.storedValue)) return true\n        parent = parent.parent\n    }\n    return false\n}\n\n/**\n * Returns the target's parent of a given type, or throws.\n *\n * @param target\n * @param type\n * @returns\n */\nexport function getParentOfType<IT extends IAnyComplexType>(\n    target: IAnyStateTreeNode,\n    type: IT\n): IT[\"Type\"] {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsType(type, 2)\n\n    let parent: AnyObjectNode | null = getStateTreeNode(target).parent\n    while (parent) {\n        if (type.is(parent.storedValue)) return parent.storedValue\n        parent = parent.parent\n    }\n    throw new MstError(`Failed to find the parent of ${getStateTreeNode(target)} of a given type`)\n}\n\n/**\n * Given an object in a model tree, returns the root object of that tree.\n *\n * Please note that in child nodes access to the root is only possible\n * once the `afterAttach` hook has fired.\n *\n * @param target\n * @returns\n */\nexport function getRoot<IT extends IAnyComplexType | IAnyStateTreeNode>(\n    target: IAnyStateTreeNode\n): TypeOrStateTreeNodeToStateTreeNode<IT> {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).root.storedValue\n}\n\n/**\n * Returns the path of the given object in the model tree\n *\n * @param target\n * @returns\n */\nexport function getPath(target: IAnyStateTreeNode): string {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).path\n}\n\n/**\n * Returns the path of the given object as unescaped string array.\n *\n * @param target\n * @returns\n */\nexport function getPathParts(target: IAnyStateTreeNode): string[] {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return splitJsonPath(getStateTreeNode(target).path)\n}\n\n/**\n * Returns true if the given object is the root of a model tree.\n *\n * @param target\n * @returns\n */\nexport function isRoot(target: IAnyStateTreeNode): boolean {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).isRoot\n}\n\n/**\n * Resolves a path relatively to a given object.\n * Returns undefined if no value can be found.\n *\n * @param target\n * @param path escaped json path\n * @returns\n */\nexport function resolvePath(target: IAnyStateTreeNode, path: string): any {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsString(path, 2)\n\n    const node = resolveNodeByPath(getStateTreeNode(target), path)\n    return node ? node.value : undefined\n}\n\n/**\n * Resolves a model instance given a root target, the type and the identifier you are searching for.\n * Returns undefined if no value can be found.\n *\n * @param type\n * @param target\n * @param identifier\n * @returns\n */\nexport function resolveIdentifier<IT extends IAnyModelType>(\n    type: IT,\n    target: IAnyStateTreeNode,\n    identifier: ReferenceIdentifier\n): IT[\"Type\"] | undefined {\n    // check all arguments\n    assertIsType(type, 1)\n    assertIsStateTreeNode(target, 2)\n    assertIsValidIdentifier(identifier, 3)\n\n    const node = getStateTreeNode(target).root.identifierCache!.resolve(\n        type,\n        normalizeIdentifier(identifier)\n    )\n    return node?.value\n}\n\n/**\n * Returns the identifier of the target node.\n * This is the *string normalized* identifier, which might not match the type of the identifier attribute\n *\n * @param target\n * @returns\n */\nexport function getIdentifier(target: IAnyStateTreeNode): string | null {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).identifier\n}\n\n/**\n * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns such reference if the check passes,\n * else it returns undefined.\n *\n * @param getter Function to access the reference.\n * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check.\n * @returns\n */\nexport function tryReference<N extends IAnyStateTreeNode>(\n    getter: () => N | null | undefined,\n    checkIfAlive = true\n): N | undefined {\n    try {\n        const node = getter()\n        if (node === undefined || node === null) {\n            return undefined\n        } else if (isStateTreeNode(node)) {\n            if (!checkIfAlive) {\n                return node\n            } else {\n                return isAlive(node) ? node : undefined\n            }\n        } else {\n            throw new MstError(\"The reference to be checked is not one of node, null or undefined\")\n        }\n    } catch (e) {\n        if (e instanceof InvalidReferenceError) {\n            return undefined\n        }\n        throw e\n    }\n}\n\n/**\n * Tests if a reference is valid (pointing to an existing node and optionally if alive) and returns if the check passes or not.\n *\n * @param getter Function to access the reference.\n * @param checkIfAlive true to also make sure the referenced node is alive (default), false to skip this check.\n * @returns\n */\nexport function isValidReference<N extends IAnyStateTreeNode>(\n    getter: () => N | null | undefined,\n    checkIfAlive = true\n): boolean {\n    try {\n        const node = getter()\n        if (node === undefined || node === null) {\n            return false\n        } else if (isStateTreeNode(node)) {\n            return checkIfAlive ? isAlive(node) : true\n        } else {\n            throw new MstError(\"The reference to be checked is not one of node, null or undefined\")\n        }\n    } catch (e) {\n        if (e instanceof InvalidReferenceError) {\n            return false\n        }\n        throw e\n    }\n}\n\n/**\n * Try to resolve a given path relative to a given node.\n *\n * @param target\n * @param path\n * @returns\n */\nexport function tryResolve(target: IAnyStateTreeNode, path: string): any {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsString(path, 2)\n\n    const node = resolveNodeByPath(getStateTreeNode(target), path, false)\n    if (node === undefined) return undefined\n    try {\n        return node.value\n    } catch (e) {\n        // For what ever reason not resolvable (e.g. totally not existing path, or value that cannot be fetched)\n        // see test / issue: 'try resolve doesn't work #686'\n        return undefined\n    }\n}\n\n/**\n * Given two state tree nodes that are part of the same tree,\n * returns the shortest jsonpath needed to navigate from the one to the other\n *\n * @param base\n * @param target\n * @returns\n */\nexport function getRelativePath(base: IAnyStateTreeNode, target: IAnyStateTreeNode): string {\n    // check all arguments\n    assertIsStateTreeNode(base, 1)\n    assertIsStateTreeNode(target, 2)\n\n    return getRelativePathBetweenNodes(getStateTreeNode(base), getStateTreeNode(target))\n}\n\n/**\n * Returns a deep copy of the given state tree node as new tree.\n * Shorthand for `snapshot(x) = getType(x).create(getSnapshot(x))`\n *\n * _Tip: clone will create a literal copy, including the same identifiers. To modify identifiers etc. during cloning, don't use clone but take a snapshot of the tree, modify it, and create new instance_\n *\n * @param source\n * @param keepEnvironment indicates whether the clone should inherit the same environment (`true`, the default), or not have an environment (`false`). If an object is passed in as second argument, that will act as the environment for the cloned tree.\n * @returns\n */\nexport function clone<T extends IAnyStateTreeNode>(\n    source: T,\n    keepEnvironment: boolean | any = true\n): T {\n    // check all arguments\n    assertIsStateTreeNode(source, 1)\n\n    const node = getStateTreeNode(source)\n    return node.type.create(\n        node.snapshot,\n        keepEnvironment === true\n            ? node.root.environment\n            : keepEnvironment === false\n              ? undefined\n              : keepEnvironment\n    ) // it's an object or something else\n}\n\n/**\n * Removes a model element from the state tree, and let it live on as a new state tree\n */\nexport function detach<T extends IAnyStateTreeNode>(target: T): T {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    getStateTreeNode(target).detach()\n    return target\n}\n\n/**\n * Removes a model element from the state tree, and mark it as end-of-life; the element should not be used anymore\n */\nexport function destroy(target: IAnyStateTreeNode): void {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    const node = getStateTreeNode(target)\n    if (node.isRoot) node.die()\n    else node.parent!.removeChild(node.subpath)\n}\n\n/**\n * Returns true if the given state tree node is not killed yet.\n * This means that the node is still a part of a tree, and that `destroy`\n * has not been called. If a node is not alive anymore, the only thing one can do with it\n * is requesting it's last path and snapshot\n *\n * @param target\n * @returns\n */\nexport function isAlive(target: IAnyStateTreeNode): boolean {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).observableIsAlive\n}\n\n/**\n * Use this utility to register a function that should be called whenever the\n * targeted state tree node is destroyed. This is a useful alternative to managing\n * cleanup methods yourself using the `beforeDestroy` hook.\n *\n * This methods returns the same disposer that was passed as argument.\n *\n * Example:\n * ```ts\n * const Todo = types.model({\n *   title: types.string\n * }).actions(self => ({\n *   afterCreate() {\n *     const autoSaveDisposer = reaction(\n *       () => getSnapshot(self),\n *       snapshot => sendSnapshotToServerSomehow(snapshot)\n *     )\n *     // stop sending updates to server if this\n *     // instance is destroyed\n *     addDisposer(self, autoSaveDisposer)\n *   }\n * }))\n * ```\n *\n * @param target\n * @param disposer\n * @returns The same disposer that was passed as argument\n */\nexport function addDisposer(target: IAnyStateTreeNode, disposer: IDisposer): IDisposer {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsFunction(disposer, 2)\n\n    const node = getStateTreeNode(target)\n    node.addDisposer(disposer)\n    return disposer\n}\n\n/**\n * Returns the environment of the current state tree, or throws. For more info on environments,\n * see [Dependency injection](/concepts/dependency-injection)\n *\n * Please note that in child nodes access to the root is only possible\n * once the `afterAttach` hook has fired\n *\n * Returns an empty environment if the tree wasn't initialized with an environment\n *\n * @param target\n * @returns\n */\nexport function getEnv<T = any>(target: IAnyStateTreeNode): T {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n\n    const node = getStateTreeNode(target)\n    const env = node.root.environment\n    if (!env) throw new MstError(`Failed to find the environment of ${node} ${node.path}`)\n    return env\n}\n\n/**\n * Returns whether the current state tree has environment or not.\n *\n * @export\n * @param {IStateTreeNode} target\n * @return {boolean}\n */\nexport function hasEnv(target: IAnyStateTreeNode): boolean {\n    // check all arguments\n    if (process.env.NODE_ENV !== \"production\") {\n        if (!isStateTreeNode(target))\n            throw new MstError(\n                \"expected first argument to be a mobx-state-tree node, got \" + target + \" instead\"\n            )\n    }\n\n    const node = getStateTreeNode(target)\n    const env = node.root.environment\n\n    return !!env\n}\n\n/**\n * Performs a depth first walk through a tree.\n */\nexport function walk(\n    target: IAnyStateTreeNode,\n    processor: (item: IAnyStateTreeNode) => void\n): void {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertIsFunction(processor, 2)\n\n    const node = getStateTreeNode(target)\n    // tslint:disable-next-line:no_unused-variable\n    node.getChildren().forEach(child => {\n        if (isStateTreeNode(child.storedValue)) walk(child.storedValue, processor)\n    })\n    processor(node.storedValue)\n}\n\nexport interface IModelReflectionPropertiesData {\n    name: string\n    properties: { [K: string]: IAnyType }\n}\n\n/**\n * Returns a reflection of the model type properties and name for either a model type or model node.\n *\n * @param typeOrNode\n * @returns\n */\nexport function getPropertyMembers(\n    typeOrNode: IAnyModelType | IAnyStateTreeNode\n): IModelReflectionPropertiesData {\n    let type: IAnyModelType\n\n    if (isStateTreeNode(typeOrNode)) {\n        type = getType(typeOrNode) as IAnyModelType\n    } else {\n        type = typeOrNode as IAnyModelType\n    }\n\n    assertArg(type, t => isModelType(t), \"model type or model instance\", 1)\n\n    return {\n        name: type.name,\n        properties: { ...type.properties }\n    }\n}\n\nexport interface IModelReflectionData extends IModelReflectionPropertiesData {\n    actions: string[]\n    views: string[]\n    volatile: string[]\n    flowActions: string[]\n}\n\n/**\n * Returns a reflection of the model node, including name, properties, views, volatile state,\n * and actions. `flowActions` is also provided as a separate array of names for any action that\n * came from a flow generator as well.\n *\n * In the case where a model has two actions: `doSomething` and `doSomethingWithFlow`, where\n * `doSomethingWithFlow` is a flow generator, the `actions` array will contain both actions,\n * i.e. [\"doSomething\", \"doSomethingWithFlow\"], and the `flowActions` array will contain only\n * the flow action, i.e. [\"doSomethingWithFlow\"].\n *\n * @param target\n * @returns\n */\nexport function getMembers(target: IAnyStateTreeNode): IModelReflectionData {\n    const type = getStateTreeNode(target).type as unknown as IAnyModelType\n\n    const reflected: IModelReflectionData = {\n        ...getPropertyMembers(type),\n        actions: [],\n        volatile: [],\n        views: [],\n        flowActions: []\n    }\n\n    const props = Object.getOwnPropertyNames(target)\n    props.forEach(key => {\n        if (key in reflected.properties) return\n        const descriptor = Object.getOwnPropertyDescriptor(target, key)!\n        if (descriptor.get) {\n            if (isComputedProp(target, key)) reflected.views.push(key)\n            else reflected.volatile.push(key)\n            return\n        }\n        if (descriptor.value._isFlowAction === true) {\n            reflected.flowActions.push(key)\n        }\n        if (descriptor.value._isMSTAction === true) {\n            reflected.actions.push(key)\n        } else if (isObservableProp(target, key)) {\n            reflected.volatile.push(key)\n        } else {\n            reflected.views.push(key)\n        }\n    })\n    return reflected\n}\n\nexport function cast<O extends string | number | boolean | null | undefined = never>(\n    snapshotOrInstance: O\n): O\nexport function cast<O = never>(\n    snapshotOrInstance:\n        | TypeOfValue<O>[\"CreationType\"]\n        | TypeOfValue<O>[\"SnapshotType\"]\n        | TypeOfValue<O>[\"Type\"]\n): O\n/**\n * Casts a node snapshot or instance type to an instance type so it can be assigned to a type instance.\n * Note that this is just a cast for the type system, this is, it won't actually convert a snapshot to an instance,\n * but just fool typescript into thinking so.\n * Either way, casting when outside an assignation operation won't compile.\n *\n * Example:\n * ```ts\n * const ModelA = types.model({\n *   n: types.number\n * }).actions(self => ({\n *   setN(aNumber: number) {\n *     self.n = aNumber\n *   }\n * }))\n *\n * const ModelB = types.model({\n *   innerModel: ModelA\n * }).actions(self => ({\n *   someAction() {\n *     // this will allow the compiler to assign a snapshot to the property\n *     self.innerModel = cast({ a: 5 })\n *   }\n * }))\n * ```\n *\n * @param snapshotOrInstance Snapshot or instance\n * @returns The same object cast as an instance\n */\nexport function cast(snapshotOrInstance: any): any {\n    return snapshotOrInstance as any\n}\n\n/**\n * Casts a node instance type to a snapshot type so it can be assigned to a type snapshot (e.g. to be used inside a create call).\n * Note that this is just a cast for the type system, this is, it won't actually convert an instance to a snapshot,\n * but just fool typescript into thinking so.\n *\n * Example:\n * ```ts\n * const ModelA = types.model({\n *   n: types.number\n * }).actions(self => ({\n *   setN(aNumber: number) {\n *     self.n = aNumber\n *   }\n * }))\n *\n * const ModelB = types.model({\n *   innerModel: ModelA\n * })\n *\n * const a = ModelA.create({ n: 5 });\n * // this will allow the compiler to use a model as if it were a snapshot\n * const b = ModelB.create({ innerModel: castToSnapshot(a)})\n * ```\n *\n * @param snapshotOrInstance Snapshot or instance\n * @returns The same object cast as an input (creation) snapshot\n */\nexport function castToSnapshot<I>(\n    snapshotOrInstance: I\n): Extract<I, IAnyStateTreeNode> extends never ? I : TypeOfValue<I>[\"CreationType\"] {\n    return snapshotOrInstance as any\n}\n\n/**\n * Casts a node instance type to a reference snapshot type so it can be assigned to a reference snapshot (e.g. to be used inside a create call).\n * Note that this is just a cast for the type system, this is, it won't actually convert an instance to a reference snapshot,\n * but just fool typescript into thinking so.\n *\n * Example:\n * ```ts\n * const ModelA = types.model({\n *   id: types.identifier,\n *   n: types.number\n * }).actions(self => ({\n *   setN(aNumber: number) {\n *     self.n = aNumber\n *   }\n * }))\n *\n * const ModelB = types.model({\n *   refA: types.reference(ModelA)\n * })\n *\n * const a = ModelA.create({ id: 'someId', n: 5 });\n * // this will allow the compiler to use a model as if it were a reference snapshot\n * const b = ModelB.create({ refA: castToReferenceSnapshot(a)})\n * ```\n *\n * @param instance Instance\n * @returns The same object cast as a reference snapshot (string or number)\n */\nexport function castToReferenceSnapshot<I>(\n    instance: I\n): Extract<I, IAnyStateTreeNode> extends never ? I : ReferenceIdentifier {\n    return instance as any\n}\n\n/**\n * Returns the unique node id (not to be confused with the instance identifier) for a\n * given instance.\n * This id is a number that is unique for each instance.\n *\n * @export\n * @param target\n * @returns\n */\nexport function getNodeId(target: IAnyStateTreeNode): number {\n    assertIsStateTreeNode(target, 1)\n\n    return getStateTreeNode(target).nodeId\n}\n"
  },
  {
    "path": "src/core/node/BaseNode.ts",
    "content": "import {\n    AnyObjectNode,\n    NodeLifeCycle,\n    Hook,\n    escapeJsonPath,\n    EventHandlers,\n    IAnyType,\n    IDisposer,\n    devMode,\n    MstError\n} from \"../../internal\"\nimport { createAtom, IAtom } from \"mobx\"\n\ntype HookSubscribers = {\n    [Hook.afterAttach]: (node: AnyNode, hook: Hook) => void\n    [Hook.afterCreate]: (node: AnyNode, hook: Hook) => void\n    [Hook.afterCreationFinalization]: (node: AnyNode, hook: Hook) => void\n    [Hook.beforeDestroy]: (node: AnyNode, hook: Hook) => void\n    [Hook.beforeDetach]: (node: AnyNode, hook: Hook) => void\n}\n\n/**\n * @internal\n * @hidden\n */\nexport abstract class BaseNode<C, S, T> {\n    private _escapedSubpath?: string\n\n    private _subpath!: string\n    get subpath() {\n        return this._subpath\n    }\n\n    private _subpathUponDeath?: string\n    get subpathUponDeath() {\n        return this._subpathUponDeath\n    }\n\n    private _pathUponDeath?: string\n    protected get pathUponDeath() {\n        return this._pathUponDeath\n    }\n\n    storedValue!: any // usually the same type as the value, but not always (such as with references)\n    get value(): T {\n        return (this.type as any).getValue(this)\n    }\n\n    private aliveAtom?: IAtom\n    private _state = NodeLifeCycle.INITIALIZING\n    get state() {\n        return this._state\n    }\n    set state(val: NodeLifeCycle) {\n        const wasAlive = this.isAlive\n        this._state = val\n        const isAlive = this.isAlive\n\n        if (this.aliveAtom && wasAlive !== isAlive) {\n            this.aliveAtom.reportChanged()\n        }\n    }\n\n    private _hookSubscribers?: EventHandlers<HookSubscribers>\n\n    protected abstract fireHook(name: Hook): void\n\n    protected fireInternalHook(name: Hook) {\n        if (this._hookSubscribers) {\n            this._hookSubscribers.emit(name, this, name)\n        }\n    }\n\n    registerHook<H extends Hook>(hook: H, hookHandler: HookSubscribers[H]): IDisposer {\n        if (!this._hookSubscribers) {\n            this._hookSubscribers = new EventHandlers()\n        }\n        return this._hookSubscribers.register(hook, hookHandler)\n    }\n\n    private _parent!: AnyObjectNode | null\n    get parent() {\n        return this._parent\n    }\n\n    constructor(\n        readonly type: IAnyType,\n        parent: AnyObjectNode | null,\n        subpath: string,\n        public environment: any\n    ) {\n        this.environment = environment\n        this.baseSetParent(parent, subpath)\n    }\n\n    getReconciliationType() {\n        return this.type\n    }\n\n    private pathAtom?: IAtom\n    protected baseSetParent(parent: AnyObjectNode | null, subpath: string) {\n        this._parent = parent\n        this._subpath = subpath\n        this._escapedSubpath = undefined // regenerate when needed\n        if (this.pathAtom) {\n            this.pathAtom.reportChanged()\n        }\n    }\n\n    /*\n     * Returns (escaped) path representation as string\n     */\n    get path(): string {\n        return this.getEscapedPath(true)\n    }\n\n    protected getEscapedPath(reportObserved: boolean): string {\n        if (reportObserved) {\n            if (!this.pathAtom) {\n                this.pathAtom = createAtom(`path`)\n            }\n            this.pathAtom.reportObserved()\n        }\n        if (!this.parent) return \"\"\n        // regenerate escaped subpath if needed\n        if (this._escapedSubpath === undefined) {\n            this._escapedSubpath = !this._subpath ? \"\" : escapeJsonPath(this._subpath)\n        }\n        return this.parent.getEscapedPath(reportObserved) + \"/\" + this._escapedSubpath\n    }\n\n    get isRoot(): boolean {\n        return this.parent === null\n    }\n\n    abstract get root(): AnyObjectNode\n\n    abstract setParent(newParent: AnyObjectNode | null, subpath: string | null): void\n\n    abstract get snapshot(): S\n    abstract getSnapshot(): S\n\n    get isAlive() {\n        return this.state !== NodeLifeCycle.DEAD\n    }\n\n    get isDetaching() {\n        return this.state === NodeLifeCycle.DETACHING\n    }\n\n    get observableIsAlive() {\n        if (!this.aliveAtom) {\n            this.aliveAtom = createAtom(`alive`)\n        }\n        this.aliveAtom.reportObserved()\n        return this.isAlive\n    }\n\n    abstract die(): void\n\n    abstract finalizeCreation(): void\n\n    protected baseFinalizeCreation(whenFinalized?: () => void) {\n        if (devMode()) {\n            if (!this.isAlive) {\n                // istanbul ignore next\n                throw new MstError(\n                    \"assertion failed: cannot finalize the creation of a node that is already dead\"\n                )\n            }\n        }\n\n        // goal: afterCreate hooks runs depth-first. After attach runs parent first, so on afterAttach the parent has completed already\n        if (this.state === NodeLifeCycle.CREATED) {\n            if (this.parent) {\n                if (this.parent.state !== NodeLifeCycle.FINALIZED) {\n                    // parent not ready yet, postpone\n                    return\n                }\n                this.fireHook(Hook.afterAttach)\n            }\n\n            this.state = NodeLifeCycle.FINALIZED\n\n            if (whenFinalized) {\n                whenFinalized()\n            }\n        }\n    }\n\n    abstract finalizeDeath(): void\n\n    protected baseFinalizeDeath() {\n        if (this._hookSubscribers) {\n            this._hookSubscribers.clearAll()\n        }\n\n        this._subpathUponDeath = this._subpath\n        this._pathUponDeath = this.getEscapedPath(false)\n        this.baseSetParent(null, \"\")\n        this.state = NodeLifeCycle.DEAD\n    }\n\n    abstract aboutToDie(): void\n\n    protected baseAboutToDie() {\n        this.fireHook(Hook.beforeDestroy)\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport type AnyNode = BaseNode<any, any, any>\n"
  },
  {
    "path": "src/core/node/Hook.ts",
    "content": "/**\n * @hidden\n */\nexport enum Hook {\n    afterCreate = \"afterCreate\",\n    afterAttach = \"afterAttach\",\n    afterCreationFinalization = \"afterCreationFinalization\",\n    beforeDetach = \"beforeDetach\",\n    beforeDestroy = \"beforeDestroy\"\n}\n\nexport interface IHooks {\n    [Hook.afterCreate]?: () => void\n    [Hook.afterAttach]?: () => void\n    [Hook.beforeDetach]?: () => void\n    [Hook.beforeDestroy]?: () => void\n}\n\nexport type IHooksGetter<T> = (self: T) => IHooks\n"
  },
  {
    "path": "src/core/node/create-node.ts",
    "content": "import {\n    MstError,\n    ObjectNode,\n    ScalarNode,\n    AnyNode,\n    getStateTreeNodeSafe,\n    AnyObjectNode,\n    ComplexType,\n    SimpleType\n} from \"../../internal\"\n\n/**\n * @internal\n * @hidden\n */\nexport function createObjectNode<C, S, T>(\n    type: ComplexType<C, S, T>,\n    parent: AnyObjectNode | null,\n    subpath: string,\n    environment: any,\n    initialValue: C | T\n): ObjectNode<C, S, T> {\n    const existingNode = getStateTreeNodeSafe(initialValue)\n    if (existingNode) {\n        if (existingNode.parent) {\n            // istanbul ignore next\n            throw new MstError(\n                `Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '${\n                    parent ? parent.path : \"\"\n                }/${subpath}', but it lives already at '${existingNode.path}'`\n            )\n        }\n\n        if (parent) {\n            existingNode.setParent(parent, subpath)\n        }\n        // else it already has no parent since it is a pre-requisite\n\n        return existingNode\n    }\n\n    // not a node, a snapshot\n    return new ObjectNode(type, parent, subpath, environment, initialValue as C)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function createScalarNode<C, S, T>(\n    type: SimpleType<C, S, T>,\n    parent: AnyObjectNode | null,\n    subpath: string,\n    environment: any,\n    initialValue: C\n): ScalarNode<C, S, T> {\n    return new ScalarNode(type, parent, subpath, environment, initialValue)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isNode(value: any): value is AnyNode {\n    return value instanceof ScalarNode || value instanceof ObjectNode\n}\n"
  },
  {
    "path": "src/core/node/identifier-cache.ts",
    "content": "import { IObservableArray, values, observable, entries } from \"mobx\"\nimport { MstError, ObjectNode, mobxShallow, AnyObjectNode, IAnyComplexType } from \"../../internal\"\n\nlet identifierCacheId = 0\n\n/**\n * @internal\n * @hidden\n */\nexport class IdentifierCache {\n    private cacheId = identifierCacheId++\n\n    // n.b. in cache all identifiers are normalized to strings\n    private cache = observable.map<string, IObservableArray<AnyObjectNode>>()\n\n    // last time the cache (array) for a given time changed\n    // n.b. it is not really the time, but just an integer that gets increased after each modification to the array\n    private lastCacheModificationPerId = observable.map<string, number>()\n\n    constructor() {}\n\n    private updateLastCacheModificationPerId(identifier: string) {\n        const lcm = this.lastCacheModificationPerId.get(identifier)\n        // we start at 1 since 0 means no update since cache creation\n        this.lastCacheModificationPerId.set(identifier, lcm === undefined ? 1 : lcm + 1)\n    }\n\n    getLastCacheModificationPerId(identifier: string): string {\n        const modificationId = this.lastCacheModificationPerId.get(identifier) || 0\n        return `${this.cacheId}-${modificationId}`\n    }\n\n    addNodeToCache(node: AnyObjectNode, lastCacheUpdate = true): void {\n        if (node.identifierAttribute) {\n            const identifier = node.identifier!\n            if (!this.cache.has(identifier)) {\n                this.cache.set(identifier, observable.array<AnyObjectNode>([], mobxShallow))\n            }\n            const set = this.cache.get(identifier)!\n            if (set.indexOf(node) !== -1) throw new MstError(`Already registered`)\n            set.push(node)\n            if (lastCacheUpdate) {\n                this.updateLastCacheModificationPerId(identifier)\n            }\n        }\n    }\n\n    mergeCache(node: AnyObjectNode) {\n        values(node.identifierCache!.cache).forEach(nodes =>\n            nodes.forEach(child => {\n                this.addNodeToCache(child)\n            })\n        )\n    }\n\n    notifyDied(node: AnyObjectNode) {\n        if (node.identifierAttribute) {\n            const id = node.identifier!\n            const set = this.cache.get(id)\n            if (set) {\n                set.remove(node)\n                // remove empty sets from cache\n                if (!set.length) {\n                    this.cache.delete(id)\n                }\n                this.updateLastCacheModificationPerId(node.identifier!)\n            }\n        }\n    }\n\n    splitCache(splitNode: AnyObjectNode): IdentifierCache {\n        const newCache = new IdentifierCache()\n        // The slash is added here so we only match children of the splitNode. In version 5.1.8 and\n        // earlier there was no trailing slash, so non children that started with the same path string\n        // were being matched incorrectly.\n        const basePath = splitNode.path + \"/\"\n        entries(this.cache).forEach(([id, nodes]) => {\n            let modified = false\n            for (let i = nodes.length - 1; i >= 0; i--) {\n                const node = nodes[i]\n                if (node === splitNode || node.path.indexOf(basePath) === 0) {\n                    newCache.addNodeToCache(node, false) // no need to update lastUpdated since it is a whole new cache\n                    nodes.splice(i, 1)\n                    // remove empty sets from cache\n                    if (!nodes.length) {\n                        this.cache.delete(id)\n                    }\n                    modified = true\n                }\n            }\n            if (modified) {\n                this.updateLastCacheModificationPerId(id)\n            }\n        })\n        return newCache\n    }\n\n    has(type: IAnyComplexType, identifier: string): boolean {\n        const set = this.cache.get(identifier)\n        if (!set) return false\n        return set.some(candidate => type.isAssignableFrom(candidate.type))\n    }\n\n    resolve<IT extends IAnyComplexType>(\n        type: IT,\n        identifier: string\n    ): ObjectNode<IT[\"CreationType\"], IT[\"SnapshotType\"], IT[\"TypeWithoutSTN\"]> | null {\n        const set = this.cache.get(identifier)\n        if (!set) return null\n        const matches = set.filter(candidate => type.isAssignableFrom(candidate.type))\n        switch (matches.length) {\n            case 0:\n                return null\n            case 1:\n                return matches[0]\n            default:\n                throw new MstError(\n                    `Cannot resolve a reference to type '${\n                        type.name\n                    }' with id: '${identifier}' unambigously, there are multiple candidates: ${matches\n                        .map(n => n.path)\n                        .join(\", \")}`\n                )\n        }\n    }\n}\n"
  },
  {
    "path": "src/core/node/livelinessChecking.ts",
    "content": "/**\n * Defines what MST should do when running into reads / writes to objects that have died.\n * - `\"warn\"`: Print a warning (default).\n * - `\"error\"`: Throw an exception.\n * - \"`ignore`\": Do nothing.\n */\nexport type LivelinessMode = \"warn\" | \"error\" | \"ignore\"\n\nlet livelinessChecking: LivelinessMode = \"warn\"\n\n/**\n * Defines what MST should do when running into reads / writes to objects that have died.\n * By default it will print a warning.\n * Use the `\"error\"` option to easy debugging to see where the error was thrown and when the offending read / write took place\n *\n * @param mode `\"warn\"`, `\"error\"` or `\"ignore\"`\n */\nexport function setLivelinessChecking(mode: LivelinessMode) {\n    livelinessChecking = mode\n}\n\n/**\n * Returns the current liveliness checking mode.\n *\n * @returns `\"warn\"`, `\"error\"` or `\"ignore\"`\n */\nexport function getLivelinessChecking(): LivelinessMode {\n    return livelinessChecking\n}\n\n/**\n * @deprecated use LivelinessMode instead\n * @hidden\n */\nexport type LivelynessMode = LivelinessMode\n\n/**\n * @deprecated use setLivelinessChecking instead\n * @hidden\n *\n * Defines what MST should do when running into reads / writes to objects that have died.\n * By default it will print a warning.\n * Use the `\"error\"` option to easy debugging to see where the error was thrown and when the offending read / write took place\n *\n * @param mode `\"warn\"`, `\"error\"` or `\"ignore\"`\n */\nexport function setLivelynessChecking(mode: LivelinessMode) {\n    setLivelinessChecking(mode)\n}\n"
  },
  {
    "path": "src/core/node/node-utils.ts",
    "content": "import {\n    MstError,\n    ObjectNode,\n    splitJsonPath,\n    joinJsonPath,\n    ScalarNode,\n    IChildNodesMap,\n    EMPTY_ARRAY,\n    AnyObjectNode,\n    AnyNode,\n    IAnyType,\n    IType,\n    assertArg,\n    STNValue,\n    Instance,\n    IAnyComplexType\n} from \"../../internal\"\n\n/**\n * @internal\n * @hidden\n */\nexport enum NodeLifeCycle {\n    INITIALIZING, // setting up\n    CREATED, // afterCreate has run\n    FINALIZED, // afterAttach has run\n    DETACHING, // being detached from the tree\n    DEAD // no coming back from this one\n}\n\n/** @hidden */\ndeclare const $stateTreeNodeType: unique symbol\n\n/**\n * Common interface that represents a node instance.\n * @hidden\n */\nexport interface IStateTreeNode<IT extends IAnyType = IAnyType> {\n    /**\n     * @internal\n     */\n    readonly $treenode?: any\n\n    // fake, will never be present, just for typing\n    // we use this weird trick to solve an issue with reference types\n    readonly [$stateTreeNodeType]?: [IT] | [any]\n}\n\n/** @hidden */\nexport type TypeOfValue<T extends IAnyStateTreeNode> =\n    T extends IStateTreeNode<infer IT> ? IT : never\n\n/**\n * Represents any state tree node instance.\n * @hidden\n */\nexport interface IAnyStateTreeNode extends STNValue<any, IAnyType> {}\n\n/**\n * Returns true if the given value is a node in a state tree.\n * More precisely, that is, if the value is an instance of a\n * `types.model`, `types.array` or `types.map`.\n *\n * @param value\n * @returns true if the value is a state tree node.\n */\nexport function isStateTreeNode<IT extends IAnyComplexType = IAnyComplexType>(\n    value: any\n): value is STNValue<Instance<IT>, IT> {\n    return !!(value && value.$treenode)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertIsStateTreeNode(\n    value: IAnyStateTreeNode,\n    argNumber: number | number[]\n): void {\n    assertArg(value, isStateTreeNode, \"mobx-state-tree node\", argNumber)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function getStateTreeNode(value: IAnyStateTreeNode): AnyObjectNode {\n    if (!isStateTreeNode(value)) {\n        // istanbul ignore next\n        throw new MstError(`Value ${value} is no MST Node`)\n    }\n    return value.$treenode!\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function getStateTreeNodeSafe(value: IAnyStateTreeNode): AnyObjectNode | null {\n    return (value && value.$treenode) || null\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function toJSON<S>(this: IStateTreeNode<IType<any, S, any>>): S {\n    return getStateTreeNode(this).snapshot\n}\n\nconst doubleDot = (_: any) => \"..\"\n\n/**\n * @internal\n * @hidden\n */\nexport function getRelativePathBetweenNodes(base: AnyObjectNode, target: AnyObjectNode): string {\n    // PRE condition target is (a child of) base!\n    if (base.root !== target.root) {\n        throw new MstError(\n            `Cannot calculate relative path: objects '${base}' and '${target}' are not part of the same object tree`\n        )\n    }\n\n    const baseParts = splitJsonPath(base.path)\n    const targetParts = splitJsonPath(target.path)\n    let common = 0\n    for (; common < baseParts.length; common++) {\n        if (baseParts[common] !== targetParts[common]) break\n    }\n    // TODO: assert that no targetParts paths are \"..\", \".\" or \"\"!\n    return (\n        baseParts.slice(common).map(doubleDot).join(\"/\") + joinJsonPath(targetParts.slice(common))\n    )\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function resolveNodeByPath(\n    base: AnyObjectNode,\n    path: string,\n    failIfResolveFails: boolean = true\n): AnyNode | undefined {\n    return resolveNodeByPathParts(base, splitJsonPath(path), failIfResolveFails)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function resolveNodeByPathParts(\n    base: AnyObjectNode,\n    pathParts: string[],\n    failIfResolveFails: boolean = true\n): AnyNode | undefined {\n    let current: AnyNode | null = base\n    try {\n        for (let i = 0; i < pathParts.length; i++) {\n            const part = pathParts[i]\n            if (part === \"..\") {\n                current = current!.parent\n                if (current) continue // not everything has a parent\n            } else if (part === \".\") {\n                continue\n            } else if (current) {\n                if (current instanceof ScalarNode) {\n                    // check if the value of a scalar resolves to a state tree node (e.g. references)\n                    // then we can continue resolving...\n                    const value: any = current.value\n                    if (isStateTreeNode(value)) {\n                        current = getStateTreeNode(value)\n                        // fall through\n                    }\n                }\n                if (current instanceof ObjectNode) {\n                    const subType = current.getChildType(part)\n                    if (subType) {\n                        current = current.getChildNode(part)\n                        if (current) continue\n                    }\n                }\n            }\n            throw new MstError(\n                `Could not resolve '${part}' in path '${\n                    joinJsonPath(pathParts.slice(0, i)) || \"/\"\n                }' while resolving '${joinJsonPath(pathParts)}'`\n            )\n        }\n    } catch (e) {\n        if (!failIfResolveFails) {\n            return undefined\n        }\n        throw e\n    }\n    return current!\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function convertChildNodesToArray(childNodes: IChildNodesMap | null): AnyNode[] {\n    if (!childNodes) return EMPTY_ARRAY as AnyNode[]\n\n    const keys = Object.keys(childNodes)\n    if (!keys.length) return EMPTY_ARRAY as AnyNode[]\n\n    const result = new Array(keys.length) as AnyNode[]\n    keys.forEach((key, index) => {\n        result[index] = childNodes![key]\n    })\n    return result\n}\n"
  },
  {
    "path": "src/core/node/object-node.ts",
    "content": "// noinspection ES6UnusedImports\nimport { action, computed, IComputedValue, reaction, _allowStateChangesInsideComputed } from \"mobx\"\nimport {\n    addHiddenFinalProp,\n    ComplexType,\n    convertChildNodesToArray,\n    createActionInvoker,\n    EMPTY_OBJECT,\n    extend,\n    MstError,\n    freeze,\n    IAnyType,\n    IdentifierCache,\n    IDisposer,\n    IJsonPatch,\n    IMiddleware,\n    IMiddlewareHandler,\n    IReversibleJsonPatch,\n    NodeLifeCycle,\n    resolveNodeByPathParts,\n    splitJsonPath,\n    splitPatch,\n    toJSON,\n    EventHandlers,\n    Hook,\n    BaseNode,\n    getLivelinessChecking,\n    normalizeIdentifier,\n    ReferenceIdentifier,\n    IMiddlewareEvent,\n    escapeJsonPath,\n    getPath,\n    warnError,\n    AnyNode,\n    IStateTreeNode,\n    ArgumentTypes,\n    IType,\n    devMode,\n    getCurrentActionContext\n} from \"../../internal\"\n\nlet nextNodeId = 1\n\nconst enum ObservableInstanceLifecycle {\n    // the actual observable instance has not been created yet\n    UNINITIALIZED,\n    // the actual observable instance is being created\n    CREATING,\n    // the actual observable instance has been created\n    CREATED\n}\n\nconst enum InternalEvents {\n    Dispose = \"dispose\",\n    Patch = \"patch\",\n    Snapshot = \"snapshot\"\n}\n\n/**\n * @internal\n * @hidden\n */\nexport interface IChildNodesMap {\n    [key: string]: AnyNode\n}\n\nconst snapshotReactionOptions = {\n    onError(e: any) {\n        throw e\n    }\n}\n\ntype InternalEventHandlers<S> = {\n    [InternalEvents.Dispose]: IDisposer\n    [InternalEvents.Patch]: (patch: IJsonPatch, reversePatch: IJsonPatch) => void\n    [InternalEvents.Snapshot]: (snapshot: S) => void\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class ObjectNode<C, S, T> extends BaseNode<C, S, T> {\n    declare readonly type: ComplexType<C, S, T>\n    declare storedValue: T & IStateTreeNode<IType<C, S, T>>\n\n    readonly nodeId = ++nextNodeId\n    readonly identifierAttribute?: string\n    readonly identifier: string | null // Identifier is always normalized to string, even if the identifier property isn't\n    readonly unnormalizedIdentifier: ReferenceIdentifier | null\n\n    identifierCache?: IdentifierCache\n    isProtectionEnabled = true\n    middlewares?: IMiddleware[]\n    hasSnapshotPostProcessor = false\n\n    private _applyPatches?: (patches: IJsonPatch[]) => void\n\n    applyPatches(patches: IJsonPatch[]): void {\n        this.createObservableInstanceIfNeeded()\n        this._applyPatches!(patches)\n    }\n\n    private _applySnapshot?: (snapshot: C) => void\n\n    applySnapshot(snapshot: C): void {\n        this.createObservableInstanceIfNeeded()\n        this._applySnapshot!(snapshot)\n    }\n\n    private _autoUnbox = true // unboxing is disabled when reading child nodes\n    _isRunningAction = false // only relevant for root\n    private _hasSnapshotReaction = false\n\n    private _observableInstanceState = ObservableInstanceLifecycle.UNINITIALIZED\n    private _childNodes: IChildNodesMap\n    private _initialSnapshot: C\n    private _cachedInitialSnapshot?: S\n    private _cachedInitialSnapshotCreated = false\n    private _snapshotComputed: IComputedValue<S>\n\n    constructor(\n        complexType: ComplexType<C, S, T>,\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: C\n    ) {\n        super(complexType, parent, subpath, environment)\n        this._snapshotComputed = computed<S>(() => freeze(this.getSnapshot()))\n\n        this.unbox = this.unbox.bind(this)\n\n        this._initialSnapshot = freeze(initialValue)\n        this.identifierAttribute = complexType.identifierAttribute\n\n        if (!parent) {\n            this.identifierCache = new IdentifierCache()\n        }\n\n        this._childNodes = complexType.initializeChildNodes(this, this._initialSnapshot)\n\n        // identifier can not be changed during lifecycle of a node\n        // so we safely can read it from initial snapshot\n        this.identifier = null\n        this.unnormalizedIdentifier = null\n        if (this.identifierAttribute && this._initialSnapshot) {\n            let id = (this._initialSnapshot as any)[this.identifierAttribute]\n            if (id === undefined) {\n                // try with the actual node if not (for optional identifiers)\n                const childNode = this._childNodes[this.identifierAttribute]\n                if (childNode) {\n                    id = childNode.value\n                }\n            }\n\n            if (typeof id !== \"string\" && typeof id !== \"number\") {\n                throw new MstError(\n                    `Instance identifier '${this.identifierAttribute}' for type '${this.type.name}' must be a string or a number`\n                )\n            }\n\n            // normalize internal identifier to string\n            this.identifier = normalizeIdentifier(id)\n            this.unnormalizedIdentifier = id\n        }\n\n        if (!parent) {\n            this.identifierCache!.addNodeToCache(this)\n        } else {\n            parent.root.identifierCache!.addNodeToCache(this)\n        }\n    }\n\n    createObservableInstanceIfNeeded(fireHooks = true): void {\n        if (this._observableInstanceState === ObservableInstanceLifecycle.UNINITIALIZED) {\n            this.createObservableInstance(fireHooks)\n        }\n    }\n\n    createObservableInstance(fireHooks = true): void {\n        if (devMode()) {\n            if (this.state !== NodeLifeCycle.INITIALIZING) {\n                // istanbul ignore next\n                throw new MstError(\n                    \"assertion failed: the creation of the observable instance must be done on the initializing phase\"\n                )\n            }\n        }\n        this._observableInstanceState = ObservableInstanceLifecycle.CREATING\n\n        // make sure the parent chain is created as well\n\n        // array with parent chain from parent to child\n        const parentChain = []\n\n        let parent = this.parent\n        // for performance reasons we never go back further than the most direct\n        // uninitialized parent\n        // this is done to avoid traversing the whole tree to the root when using\n        // the same reference again\n        while (\n            parent &&\n            parent._observableInstanceState === ObservableInstanceLifecycle.UNINITIALIZED\n        ) {\n            parentChain.unshift(parent)\n            parent = parent.parent\n        }\n\n        // initialize the uninitialized parent chain from parent to child\n        for (const p of parentChain) {\n            // delay firing hooks until after all parents have been created\n            p.createObservableInstanceIfNeeded(false)\n        }\n\n        const type = this.type\n\n        try {\n            this.storedValue = type.createNewInstance(this._childNodes) as typeof this.storedValue\n            this.preboot()\n\n            this._isRunningAction = true\n            type.finalizeNewInstance(this, this.storedValue)\n        } catch (e) {\n            // short-cut to die the instance, to avoid the snapshot computed starting to throw...\n            this.state = NodeLifeCycle.DEAD\n            throw e\n        } finally {\n            this._isRunningAction = false\n        }\n\n        this._observableInstanceState = ObservableInstanceLifecycle.CREATED\n\n        // NOTE: we need to touch snapshot, because non-observable\n        // \"_observableInstanceState\" field was touched\n        ;(this._snapshotComputed as any).trackAndCompute()\n\n        if (this.isRoot) this._addSnapshotReaction()\n\n        this._childNodes = EMPTY_OBJECT\n\n        this.state = NodeLifeCycle.CREATED\n\n        if (fireHooks) {\n            this.fireHook(Hook.afterCreate)\n            // Note that the parent might not be finalized at this point\n            // so afterAttach won't be called until later in that case\n            this.finalizeCreation()\n\n            // fire the hooks of the parents that we created\n            for (const p of parentChain.reverse()) {\n                p.fireHook(Hook.afterCreate)\n                // This will call afterAttach on the child if necessary\n                p.finalizeCreation()\n            }\n        }\n    }\n\n    get root(): AnyObjectNode {\n        const parent = this.parent\n        return parent ? parent.root : this\n    }\n\n    clearParent(): void {\n        if (!this.parent) return\n\n        // detach if attached\n        this.fireHook(Hook.beforeDetach)\n        const previousState = this.state\n        this.state = NodeLifeCycle.DETACHING\n\n        const root = this.root\n        const newEnv = root.environment\n        const newIdCache = root.identifierCache!.splitCache(this)\n\n        try {\n            this.parent.removeChild(this.subpath)\n            this.baseSetParent(null, \"\")\n            this.environment = newEnv\n            this.identifierCache = newIdCache\n        } finally {\n            this.state = previousState\n        }\n    }\n\n    setParent(newParent: AnyObjectNode, subpath: string): void {\n        const parentChanged = newParent !== this.parent\n        const subpathChanged = subpath !== this.subpath\n\n        if (!parentChanged && !subpathChanged) {\n            return\n        }\n\n        if (devMode()) {\n            if (!subpath) {\n                // istanbul ignore next\n                throw new MstError(\"assertion failed: subpath expected\")\n            }\n            if (!newParent) {\n                // istanbul ignore next\n                throw new MstError(\"assertion failed: new parent expected\")\n            }\n\n            if (this.parent && parentChanged) {\n                throw new MstError(\n                    `A node cannot exists twice in the state tree. Failed to add ${this} to path '${newParent.path}/${subpath}'.`\n                )\n            }\n            if (!this.parent && newParent.root === this) {\n                throw new MstError(\n                    `A state tree is not allowed to contain itself. Cannot assign ${this} to path '${newParent.path}/${subpath}'`\n                )\n            }\n            if (\n                !this.parent &&\n                !!this.environment &&\n                this.environment !== newParent.root.environment\n            ) {\n                throw new MstError(\n                    `A state tree cannot be made part of another state tree as long as their environments are different.`\n                )\n            }\n        }\n\n        if (parentChanged) {\n            // attach to new parent\n            this.environment = undefined // will use root's\n            newParent.root.identifierCache!.mergeCache(this)\n            this.baseSetParent(newParent, subpath)\n            this.fireHook(Hook.afterAttach)\n        } else if (subpathChanged) {\n            // moving to a new subpath on the same parent\n            this.baseSetParent(this.parent, subpath)\n        }\n    }\n\n    protected fireHook(name: Hook): void {\n        this.fireInternalHook(name)\n\n        const fn =\n            this.storedValue &&\n            typeof this.storedValue === \"object\" &&\n            (this.storedValue as any)[name]\n        if (typeof fn === \"function\") {\n            // we check for it to allow old mobx peer dependencies that don't have the method to work (even when still bugged)\n            if (_allowStateChangesInsideComputed) {\n                _allowStateChangesInsideComputed(() => {\n                    fn.apply(this.storedValue)\n                })\n            } else {\n                fn.apply(this.storedValue)\n            }\n        }\n    }\n\n    private _snapshotUponDeath?: S\n\n    // advantage of using computed for a snapshot is that nicely respects transactions etc.\n    get snapshot(): S {\n        if (this.hasSnapshotPostProcessor) {\n            this.createObservableInstanceIfNeeded()\n        }\n        return this._snapshotComputed.get()\n    }\n\n    // NOTE: we use this method to get snapshot without creating @computed overhead\n    getSnapshot(): S {\n        if (!this.isAlive) return this._snapshotUponDeath!\n        return this._observableInstanceState === ObservableInstanceLifecycle.CREATED\n            ? this._getActualSnapshot()\n            : this._getCachedInitialSnapshot()\n    }\n\n    private _getActualSnapshot(): S {\n        return this.type.getSnapshot(this)\n    }\n\n    private _getCachedInitialSnapshot(): S {\n        if (!this._cachedInitialSnapshotCreated) {\n            const type = this.type\n            const childNodes = this._childNodes\n            const snapshot = this._initialSnapshot\n\n            this._cachedInitialSnapshot = type.processInitialSnapshot(childNodes, snapshot)\n            this._cachedInitialSnapshotCreated = true\n        }\n\n        return this._cachedInitialSnapshot!\n    }\n\n    private isRunningAction(): boolean {\n        if (this._isRunningAction) return true\n        if (this.isRoot) return false\n        return this.parent!.isRunningAction()\n    }\n\n    assertAlive(context: AssertAliveContext): void {\n        const livelinessChecking = getLivelinessChecking()\n        if (!this.isAlive && livelinessChecking !== \"ignore\") {\n            const error = this._getAssertAliveError(context)\n            switch (livelinessChecking) {\n                case \"error\":\n                    throw new MstError(error)\n                case \"warn\":\n                    warnError(error)\n            }\n        }\n    }\n\n    private _getAssertAliveError(context: AssertAliveContext): string {\n        const escapedPath = this.getEscapedPath(false) || this.pathUponDeath || \"\"\n        const subpath = (context.subpath && escapeJsonPath(context.subpath)) || \"\"\n\n        let actionContext = context.actionContext || getCurrentActionContext()\n\n        // try to use a real action context if possible since it includes the action name\n        if (actionContext && actionContext.type !== \"action\" && actionContext.parentActionEvent) {\n            actionContext = actionContext.parentActionEvent\n        }\n\n        let actionFullPath = \"\"\n        if (actionContext && actionContext.name != null) {\n            // try to use the context, and if it not available use the node one\n            const actionPath =\n                (actionContext && actionContext.context && getPath(actionContext.context)) ||\n                escapedPath\n            actionFullPath = `${actionPath}.${actionContext.name}()`\n        }\n\n        return `You are trying to read or write to an object that is no longer part of a state tree. (Object type: '${this.type.name}', Path upon death: '${escapedPath}', Subpath: '${subpath}', Action: '${actionFullPath}'). Either detach nodes first, or don't use objects after removing / replacing them in the tree.`\n    }\n\n    getChildNode(subpath: string): AnyNode {\n        this.assertAlive({\n            subpath\n        })\n        this._autoUnbox = false\n        try {\n            return this._observableInstanceState === ObservableInstanceLifecycle.CREATED\n                ? this.type.getChildNode(this, subpath)\n                : this._childNodes![subpath]\n        } finally {\n            this._autoUnbox = true\n        }\n    }\n\n    getChildren(): ReadonlyArray<AnyNode> {\n        this.assertAlive(EMPTY_OBJECT)\n        this._autoUnbox = false\n        try {\n            return this._observableInstanceState === ObservableInstanceLifecycle.CREATED\n                ? this.type.getChildren(this)\n                : convertChildNodesToArray(this._childNodes)\n        } finally {\n            this._autoUnbox = true\n        }\n    }\n\n    getChildType(propertyName?: string): IAnyType {\n        return this.type.getChildType(propertyName)\n    }\n\n    get isProtected(): boolean {\n        return this.root.isProtectionEnabled\n    }\n\n    assertWritable(context: AssertAliveContext): void {\n        this.assertAlive(context)\n        if (!this.isRunningAction() && this.isProtected) {\n            throw new MstError(\n                `Cannot modify '${this}', the object is protected and can only be modified by using an action.`\n            )\n        }\n    }\n\n    removeChild(subpath: string): void {\n        this.type.removeChild(this, subpath)\n    }\n\n    // bound on the constructor\n    unbox(childNode: AnyNode | undefined): AnyNode | undefined {\n        if (!childNode) return childNode\n\n        this.assertAlive({\n            subpath: childNode.subpath || childNode.subpathUponDeath\n        })\n        return this._autoUnbox ? childNode.value : childNode\n    }\n\n    toString(): string {\n        const path = (this.isAlive ? this.path : this.pathUponDeath) || \"<root>\"\n        const identifier = this.identifier ? `(id: ${this.identifier})` : \"\"\n        return `${this.type.name}@${path}${identifier}${this.isAlive ? \"\" : \" [dead]\"}`\n    }\n\n    finalizeCreation(): void {\n        this.baseFinalizeCreation(() => {\n            for (let child of this.getChildren()) {\n                child.finalizeCreation()\n            }\n\n            this.fireInternalHook(Hook.afterCreationFinalization)\n        })\n    }\n\n    detach(): void {\n        if (!this.isAlive) throw new MstError(`Error while detaching, node is not alive.`)\n\n        this.clearParent()\n    }\n\n    private preboot(): void {\n        const self = this\n        this._applyPatches = createActionInvoker(\n            this.storedValue,\n            \"@APPLY_PATCHES\",\n            (patches: IJsonPatch[]) => {\n                patches.forEach(patch => {\n                    if (!patch.path) {\n                        self.type.applySnapshot(self, patch.value)\n                        return\n                    }\n                    const parts = splitJsonPath(patch.path)\n                    const node = resolveNodeByPathParts(self, parts.slice(0, -1)) as AnyObjectNode\n                    node.applyPatchLocally(parts[parts.length - 1], patch)\n                })\n            }\n        )\n        this._applySnapshot = createActionInvoker(\n            this.storedValue,\n            \"@APPLY_SNAPSHOT\",\n            (snapshot: C) => {\n                // if the snapshot is the same as the current one, avoid performing a reconcile\n                if (snapshot === (self.snapshot as any)) return\n                // else, apply it by calling the type logic\n                return self.type.applySnapshot(self, snapshot as any)\n            }\n        )\n\n        addHiddenFinalProp(this.storedValue, \"$treenode\", this)\n        addHiddenFinalProp(this.storedValue, \"toJSON\", toJSON)\n    }\n\n    die(): void {\n        if (!this.isAlive || this.state === NodeLifeCycle.DETACHING) return\n        this.aboutToDie()\n        this.finalizeDeath()\n    }\n\n    aboutToDie(): void {\n        if (this._observableInstanceState === ObservableInstanceLifecycle.UNINITIALIZED) {\n            return\n        }\n\n        this.getChildren().forEach(node => {\n            node.aboutToDie()\n        })\n\n        // beforeDestroy should run before the disposers since else we could end up in a situation where\n        // a disposer added with addDisposer at this stage (beforeDestroy) is actually never released\n        this.baseAboutToDie()\n\n        this._internalEventsEmit(InternalEvents.Dispose)\n        this._internalEventsClear(InternalEvents.Dispose)\n    }\n\n    finalizeDeath(): void {\n        // invariant: not called directly but from \"die\"\n        this.getChildren().forEach(node => {\n            node.finalizeDeath()\n        })\n        this.root.identifierCache!.notifyDied(this)\n\n        // \"kill\" the computed prop and just store the last snapshot\n        const snapshot = this.snapshot\n        this._snapshotUponDeath = snapshot\n\n        this._internalEventsClearAll()\n\n        this.baseFinalizeDeath()\n    }\n\n    onSnapshot(onChange: (snapshot: S) => void): IDisposer {\n        this._addSnapshotReaction()\n        return this._internalEventsRegister(InternalEvents.Snapshot, onChange)\n    }\n\n    protected emitSnapshot(snapshot: S): void {\n        this._internalEventsEmit(InternalEvents.Snapshot, snapshot)\n    }\n\n    onPatch(handler: (patch: IJsonPatch, reversePatch: IJsonPatch) => void): IDisposer {\n        return this._internalEventsRegister(InternalEvents.Patch, handler)\n    }\n\n    emitPatch(basePatch: IReversibleJsonPatch, source: AnyNode): void {\n        if (this._internalEventsHasSubscribers(InternalEvents.Patch)) {\n            // calculate the relative path of the patch\n            const path =\n                source.path.substr(this.path.length) + (basePatch.path ? \"/\" + basePatch.path : \"\")\n            const localizedPatch: IReversibleJsonPatch = extend({}, basePatch, { path })\n            const [patch, reversePatch] = splitPatch(localizedPatch)\n            this._internalEventsEmit(InternalEvents.Patch, patch, reversePatch)\n        }\n        if (this.parent) this.parent.emitPatch(basePatch, source)\n    }\n\n    hasDisposer(disposer: () => void): boolean {\n        return this._internalEventsHas(InternalEvents.Dispose, disposer)\n    }\n\n    addDisposer(disposer: () => void): void {\n        if (!this.hasDisposer(disposer)) {\n            this._internalEventsRegister(InternalEvents.Dispose, disposer, true)\n            return\n        }\n        throw new MstError(\"cannot add a disposer when it is already registered for execution\")\n    }\n\n    removeDisposer(disposer: () => void): void {\n        if (!this._internalEventsHas(InternalEvents.Dispose, disposer)) {\n            throw new MstError(\"cannot remove a disposer which was never registered for execution\")\n        }\n        this._internalEventsUnregister(InternalEvents.Dispose, disposer)\n    }\n\n    private removeMiddleware(middleware: IMiddleware): void {\n        if (this.middlewares) {\n            const index = this.middlewares.indexOf(middleware)\n            if (index >= 0) {\n                this.middlewares.splice(index, 1)\n            }\n        }\n    }\n\n    addMiddleWare(handler: IMiddlewareHandler, includeHooks: boolean = true): IDisposer {\n        const middleware = { handler, includeHooks }\n        if (!this.middlewares) this.middlewares = [middleware]\n        else this.middlewares.push(middleware)\n\n        return () => {\n            this.removeMiddleware(middleware)\n        }\n    }\n\n    applyPatchLocally(subpath: string, patch: IJsonPatch): void {\n        this.assertWritable({\n            subpath\n        })\n        this.createObservableInstanceIfNeeded()\n        this.type.applyPatchLocally(this, subpath, patch)\n    }\n\n    private _addSnapshotReaction(): void {\n        if (!this._hasSnapshotReaction) {\n            const snapshotDisposer = reaction(\n                () => this.snapshot,\n                snapshot => this.emitSnapshot(snapshot),\n                snapshotReactionOptions\n            )\n            this.addDisposer(snapshotDisposer)\n            this._hasSnapshotReaction = true\n        }\n    }\n\n    // #region internal event handling\n\n    private _internalEvents?: EventHandlers<InternalEventHandlers<S>>\n\n    // we proxy the methods to avoid creating an EventHandlers instance when it is not needed\n\n    private _internalEventsHasSubscribers(event: InternalEvents): boolean {\n        return !!this._internalEvents && this._internalEvents.hasSubscribers(event)\n    }\n\n    private _internalEventsRegister<IE extends InternalEvents>(\n        event: IE,\n        eventHandler: InternalEventHandlers<S>[IE],\n        atTheBeginning = false\n    ): IDisposer {\n        if (!this._internalEvents) {\n            this._internalEvents = new EventHandlers()\n        }\n        return this._internalEvents.register(event, eventHandler, atTheBeginning)\n    }\n\n    private _internalEventsHas<IE extends InternalEvents>(\n        event: IE,\n        eventHandler: InternalEventHandlers<S>[IE]\n    ): boolean {\n        return !!this._internalEvents && this._internalEvents.has(event, eventHandler)\n    }\n\n    private _internalEventsUnregister<IE extends InternalEvents>(\n        event: IE,\n        eventHandler: InternalEventHandlers<S>[IE]\n    ): void {\n        if (this._internalEvents) {\n            this._internalEvents.unregister(event, eventHandler)\n        }\n    }\n\n    private _internalEventsEmit<IE extends InternalEvents>(\n        event: IE,\n        ...args: ArgumentTypes<InternalEventHandlers<S>[IE]>\n    ): void {\n        if (this._internalEvents) {\n            this._internalEvents.emit(event, ...args)\n        }\n    }\n\n    private _internalEventsClear(event: InternalEvents): void {\n        if (this._internalEvents) {\n            this._internalEvents.clear(event)\n        }\n    }\n\n    private _internalEventsClearAll(): void {\n        if (this._internalEvents) {\n            this._internalEvents.clearAll()\n        }\n    }\n\n    // #endregion\n}\nObjectNode.prototype.createObservableInstance = action(\n    ObjectNode.prototype.createObservableInstance\n)\nObjectNode.prototype.detach = action(ObjectNode.prototype.detach)\nObjectNode.prototype.die = action(ObjectNode.prototype.die)\n\n/**\n * @internal\n * @hidden\n */\nexport type AnyObjectNode = ObjectNode<any, any, any>\n\n/**\n * @internal\n * @hidden\n */\nexport interface AssertAliveContext {\n    subpath?: string\n    actionContext?: IMiddlewareEvent\n}\n"
  },
  {
    "path": "src/core/node/scalar-node.ts",
    "content": "import {\n    MstError,\n    freeze,\n    NodeLifeCycle,\n    Hook,\n    BaseNode,\n    AnyObjectNode,\n    SimpleType,\n    devMode\n} from \"../../internal\"\nimport { action } from \"mobx\"\n\n/**\n * @internal\n * @hidden\n */\nexport class ScalarNode<C, S, T> extends BaseNode<C, S, T> {\n    // note about hooks:\n    // - afterCreate is not emmited in scalar nodes, since it would be emitted in the\n    //   constructor, before it can be subscribed by anybody\n    // - afterCreationFinalization could be emitted, but there's no need for it right now\n    // - beforeDetach is never emitted for scalar nodes, since they cannot be detached\n\n    declare readonly type: SimpleType<C, S, T>\n\n    constructor(\n        simpleType: SimpleType<C, S, T>,\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialSnapshot: C\n    ) {\n        super(simpleType, parent, subpath, environment)\n        try {\n            this.storedValue = simpleType.createNewInstance(initialSnapshot)\n        } catch (e) {\n            // short-cut to die the instance, to avoid the snapshot computed starting to throw...\n            this.state = NodeLifeCycle.DEAD\n            throw e\n        }\n\n        this.state = NodeLifeCycle.CREATED\n        // for scalar nodes there's no point in firing this event since it would fire on the constructor, before\n        // anybody can actually register for/listen to it\n        // this.fireHook(Hook.AfterCreate)\n\n        this.finalizeCreation()\n    }\n\n    get root(): AnyObjectNode {\n        // future optimization: store root ref in the node and maintain it\n        if (!this.parent) throw new MstError(`This scalar node is not part of a tree`)\n        return this.parent.root\n    }\n\n    setParent(newParent: AnyObjectNode, subpath: string): void {\n        const parentChanged = this.parent !== newParent\n        const subpathChanged = this.subpath !== subpath\n\n        if (!parentChanged && !subpathChanged) {\n            return\n        }\n\n        if (devMode()) {\n            if (!subpath) {\n                // istanbul ignore next\n                throw new MstError(\"assertion failed: subpath expected\")\n            }\n            if (!newParent) {\n                // istanbul ignore next\n                throw new MstError(\"assertion failed: parent expected\")\n            }\n            if (parentChanged) {\n                // istanbul ignore next\n                throw new MstError(\"assertion failed: scalar nodes cannot change their parent\")\n            }\n        }\n\n        this.environment = undefined // use parent's\n        this.baseSetParent(this.parent, subpath)\n    }\n\n    get snapshot(): S {\n        return freeze(this.getSnapshot())\n    }\n\n    getSnapshot(): S {\n        return this.type.getSnapshot(this)\n    }\n\n    toString(): string {\n        const path = (this.isAlive ? this.path : this.pathUponDeath) || \"<root>\"\n        return `${this.type.name}@${path}${this.isAlive ? \"\" : \" [dead]\"}`\n    }\n\n    die(): void {\n        if (!this.isAlive || this.state === NodeLifeCycle.DETACHING) return\n        this.aboutToDie()\n        this.finalizeDeath()\n    }\n\n    finalizeCreation(): void {\n        this.baseFinalizeCreation()\n    }\n\n    aboutToDie(): void {\n        this.baseAboutToDie()\n    }\n\n    finalizeDeath(): void {\n        this.baseFinalizeDeath()\n    }\n\n    protected fireHook(name: Hook): void {\n        this.fireInternalHook(name)\n    }\n}\nScalarNode.prototype.die = action(ScalarNode.prototype.die)\n"
  },
  {
    "path": "src/core/process.ts",
    "content": "import { deprecated, flow, createFlowSpawner } from \"../internal\"\n\n// based on: https://github.com/mobxjs/mobx-utils/blob/master/src/async-action.ts\n/*\n    All contents of this file are deprecated.\n\n    The term `process` has been replaced with `flow` to avoid conflicts with the\n    global `process` object.\n\n    Refer to `flow.ts` for any further changes to this implementation.\n*/\n\nconst DEPRECATION_MESSAGE =\n    \"See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information. \" +\n    \"Note that the middleware event types starting with `process` now start with `flow`.\"\n\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<R>(generator: () => IterableIterator<any>): () => Promise<R>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1>(generator: (a1: A1) => IterableIterator<any>): (a1: A1) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2>(\n    generator: (a1: A1, a2: A2) => IterableIterator<any>\n): (a1: A1, a2: A2) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2, A3>(\n    generator: (a1: A1, a2: A2, a3: A3) => IterableIterator<any>\n): (a1: A1, a2: A2, a3: A3) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2, A3, A4>(\n    generator: (a1: A1, a2: A2, a3: A3, a4: A4) => IterableIterator<any>\n): (a1: A1, a2: A2, a3: A3, a4: A4) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2, A3, A4, A5>(\n    generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => IterableIterator<any>\n): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2, A3, A4, A5, A6>(\n    generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => IterableIterator<any>\n): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2, A3, A4, A5, A6, A7>(\n    generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7) => IterableIterator<any>\n): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7) => Promise<any>\n/**\n * @deprecated has been renamed to `flow()`.\n * @hidden\n */\nexport function process<A1, A2, A3, A4, A5, A6, A7, A8>(\n    generator: (\n        a1: A1,\n        a2: A2,\n        a3: A3,\n        a4: A4,\n        a5: A5,\n        a6: A6,\n        a7: A7,\n        a8: A8\n    ) => IterableIterator<any>\n): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8) => Promise<any>\n/**\n * @hidden\n *\n * @deprecated has been renamed to `flow()`.\n * See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information.\n * Note that the middleware event types starting with `process` now start with `flow`.\n *\n * @returns {Promise}\n */\nexport function process(asyncAction: any): any {\n    deprecated(\"process\", \"`process()` has been renamed to `flow()`. \" + DEPRECATION_MESSAGE)\n    return flow(asyncAction)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function createProcessSpawner(name: string, generator: Function) {\n    deprecated(\n        \"process\",\n        \"`createProcessSpawner()` has been renamed to `createFlowSpawner()`. \" + DEPRECATION_MESSAGE\n    )\n    return createFlowSpawner(name, generator)\n}\n"
  },
  {
    "path": "src/core/type/type-checker.ts",
    "content": "import {\n    MstError,\n    EMPTY_ARRAY,\n    isPrimitive,\n    getStateTreeNode,\n    isStateTreeNode,\n    isPrimitiveType,\n    IAnyType,\n    ExtractCSTWithSTN,\n    isTypeCheckingEnabled,\n    devMode\n} from \"../../internal\"\n\n/** Validation context entry, this is, where the validation should run against which type */\nexport interface IValidationContextEntry {\n    /** Subpath where the validation should be run, or an empty string to validate it all */\n    path: string\n    /** Type to validate the subpath against */\n    type: IAnyType\n}\n\n/** Array of validation context entries */\nexport type IValidationContext = IValidationContextEntry[]\n\n/** Type validation error */\nexport interface IValidationError {\n    /** Validation context */\n    context: IValidationContext\n    /** Value that was being validated, either a snapshot or an instance */\n    value: any\n    /** Error message */\n    message?: string\n}\n\n/** Type validation result, which is an array of type validation errors */\nexport type IValidationResult = IValidationError[]\n\nfunction safeStringify(value: any) {\n    try {\n        return JSON.stringify(value)\n    } catch (e) {\n        // istanbul ignore next\n        return `<Unserializable: ${e}>`\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function prettyPrintValue(value: any) {\n    return typeof value === \"function\"\n        ? `<function${value.name ? \" \" + value.name : \"\"}>`\n        : isStateTreeNode(value)\n          ? `<${value}>`\n          : `\\`${safeStringify(value)}\\``\n}\n\nfunction shortenPrintValue(valueInString: string) {\n    return valueInString.length < 280\n        ? valueInString\n        : `${valueInString.substring(0, 272)}......${valueInString.substring(valueInString.length - 8)}`\n}\n\nfunction toErrorString(error: IValidationError): string {\n    const { value } = error\n    const type = error.context[error.context.length - 1].type!\n    const fullPath = error.context\n        .map(({ path }) => path)\n        .filter(path => path.length > 0)\n        .join(\"/\")\n\n    const pathPrefix = fullPath.length > 0 ? `at path \"/${fullPath}\" ` : ``\n\n    const currentTypename = isStateTreeNode(value)\n        ? `value of type ${getStateTreeNode(value).type.name}:`\n        : isPrimitive(value)\n          ? \"value\"\n          : \"snapshot\"\n    const isSnapshotCompatible =\n        type && isStateTreeNode(value) && type.is(getStateTreeNode(value).snapshot)\n\n    return (\n        `${pathPrefix}${currentTypename} ${prettyPrintValue(value)} is not assignable ${\n            type ? `to type: \\`${type.name}\\`` : ``\n        }` +\n        (error.message ? ` (${error.message})` : \"\") +\n        (type\n            ? isPrimitiveType(type) || isPrimitive(value)\n                ? `.`\n                : `, expected an instance of \\`${(type as IAnyType).name}\\` or a snapshot like \\`${(\n                      type as IAnyType\n                  ).describe()}\\` instead.` +\n                  (isSnapshotCompatible\n                      ? \" (Note that a snapshot of the provided value is compatible with the targeted type)\"\n                      : \"\")\n            : `.`)\n    )\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function getContextForPath(\n    context: IValidationContext,\n    path: string,\n    type: IAnyType\n): IValidationContext {\n    return context.concat([{ path, type }])\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function typeCheckSuccess(): IValidationResult {\n    return EMPTY_ARRAY as any\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function typeCheckFailure(\n    context: IValidationContext,\n    value: any,\n    message?: string\n): IValidationResult {\n    return [{ context, value, message }]\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function flattenTypeErrors(errors: IValidationResult[]): IValidationResult {\n    return errors.reduce((a, i) => a.concat(i), [])\n}\n\n// TODO; doublecheck: typecheck should only needed to be invoked from: type.create and array / map / value.property will change\n/**\n * @internal\n * @hidden\n */\nexport function typecheckInternal<IT extends IAnyType>(\n    type: IAnyType,\n    value: ExtractCSTWithSTN<IT>\n): void {\n    // runs typeChecking if it is in dev-mode or through a process.env.ENABLE_TYPE_CHECK flag\n    if (isTypeCheckingEnabled()) {\n        typecheck(type, value)\n    }\n}\n\n/**\n * Run's the typechecker for the given type on the given value, which can be a snapshot or an instance.\n * Throws if the given value is not according the provided type specification.\n * Use this if you need typechecks even in a production build (by default all automatic runtime type checks will be skipped in production builds)\n *\n * @param type Type to check against.\n * @param value Value to be checked, either a snapshot or an instance.\n */\nexport function typecheck<IT extends IAnyType>(type: IT, value: ExtractCSTWithSTN<IT>): void {\n    const errors = type.validate(value, [{ path: \"\", type }])\n\n    if (errors.length > 0) {\n        throw new MstError(validationErrorsToString(type, value, errors))\n    }\n}\n\nfunction validationErrorsToString<IT extends IAnyType>(\n    type: IT,\n    value: ExtractCSTWithSTN<IT>,\n    errors: IValidationError[]\n): string | undefined {\n    if (errors.length === 0) {\n        return undefined\n    }\n\n    return (\n        `Error while converting ${shortenPrintValue(prettyPrintValue(value))} to \\`${\n            type.name\n        }\\`:\\n\\n    ` + errors.map(toErrorString).join(\"\\n    \")\n    )\n}\n"
  },
  {
    "path": "src/core/type/type.ts",
    "content": "import { action } from \"mobx\"\n\nimport {\n    MstError,\n    isMutable,\n    isStateTreeNode,\n    getStateTreeNode,\n    IValidationContext,\n    IValidationResult,\n    typecheckInternal,\n    typeCheckFailure,\n    typeCheckSuccess,\n    IStateTreeNode,\n    IJsonPatch,\n    getType,\n    ObjectNode,\n    IChildNodesMap,\n    ModelPrimitive,\n    normalizeIdentifier,\n    AnyObjectNode,\n    AnyNode,\n    BaseNode,\n    ScalarNode,\n    getStateTreeNodeSafe,\n    assertArg\n} from \"../../internal\"\nimport type { Writable, WritableKeys } from \"ts-essentials\"\n\n/**\n * @internal\n * @hidden\n */\nexport enum TypeFlags {\n    String = 1,\n    Number = 1 << 1,\n    Boolean = 1 << 2,\n    Date = 1 << 3,\n    Literal = 1 << 4,\n    Array = 1 << 5,\n    Map = 1 << 6,\n    Object = 1 << 7,\n    Frozen = 1 << 8,\n    Optional = 1 << 9,\n    Reference = 1 << 10,\n    Identifier = 1 << 11,\n    Late = 1 << 12,\n    Refinement = 1 << 13,\n    Union = 1 << 14,\n    Null = 1 << 15,\n    Undefined = 1 << 16,\n    Integer = 1 << 17,\n    Custom = 1 << 18,\n    SnapshotProcessor = 1 << 19,\n    Lazy = 1 << 20,\n    Finite = 1 << 21,\n    Float = 1 << 22,\n    BigInt = 1 << 23\n}\n\n/**\n * @internal\n * @hidden\n */\nexport const cannotDetermineSubtype = \"cannotDetermine\"\n\n/**\n * A state tree node value.\n * @hidden\n */\nexport type STNValue<T, IT extends IAnyType> = T extends object ? T & IStateTreeNode<IT> : T\n\n/** @hidden */\nconst $type: unique symbol = Symbol(\"$type\")\n\ntype ExcludeReadonly<T> = T extends {} ? T[WritableKeys<T>] : T\n\n/**\n * A type, either complex or simple.\n */\nexport interface IType<C, S, T> {\n    // fake, will never be present, just for typing\n    /** @hidden */\n    readonly [$type]: undefined\n\n    /**\n     * Friendly type name.\n     */\n    name: string\n\n    /**\n     * Name of the identifier attribute or null if none.\n     */\n    readonly identifierAttribute?: string\n\n    /**\n     * Creates an instance for the type given an snapshot input.\n     *\n     * @returns An instance of that type.\n     */\n    create(snapshot?: C | ExcludeReadonly<T>, env?: any): this[\"Type\"]\n\n    /**\n     * Checks if a given snapshot / instance is of the given type.\n     *\n     * @param thing Snapshot or instance to be checked.\n     * @returns true if the value is of the current type, false otherwise.\n     */\n    is(thing: any): thing is C | this[\"Type\"]\n\n    /**\n     * Run's the type's typechecker on the given value with the given validation context.\n     *\n     * @param thing Value to be checked, either a snapshot or an instance.\n     * @param context Validation context, an array of { subpaths, subtypes } that should be validated\n     * @returns The validation result, an array with the list of validation errors.\n     */\n    validate(thing: C | T, context: IValidationContext): IValidationResult\n\n    /**\n     * Gets the textual representation of the type as a string.\n     */\n    describe(): string\n\n    /**\n     * @deprecated use `Instance<typeof MyType>` instead.\n     * @hidden\n     */\n    readonly Type: STNValue<T, this>\n\n    /**\n     * @deprecated do not use.\n     * @hidden\n     */\n    readonly TypeWithoutSTN: T\n\n    /**\n     * @deprecated use `SnapshotOut<typeof MyType>` instead.\n     * @hidden\n     */\n    readonly SnapshotType: S\n\n    /**\n     * @deprecated use `SnapshotIn<typeof MyType>` instead.\n     * @hidden\n     */\n    readonly CreationType: C\n\n    // Internal api's\n\n    /**\n     * @internal\n     * @hidden\n     */\n    flags: TypeFlags\n    /**\n     * @internal\n     * @hidden\n     */\n    isType: true\n    /**\n     * @internal\n     * @hidden\n     */\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: C | T\n    ): BaseNode<C, S, T>\n    /**\n     * @internal\n     * @hidden\n     */\n    reconcile(\n        current: BaseNode<C, S, T>,\n        newValue: C | T,\n        parent: AnyObjectNode,\n        subpath: string\n    ): BaseNode<C, S, T>\n    /**\n     * @internal\n     * @hidden\n     */\n    getSnapshot(node: BaseNode<C, S, T>, applyPostProcess?: boolean): S\n    /**\n     * @internal\n     * @hidden\n     */\n    isAssignableFrom(type: IAnyType): boolean\n    /**\n     * @internal\n     * @hidden\n     */\n    getSubTypes(): IAnyType[] | IAnyType | null | typeof cannotDetermineSubtype\n}\n\n/**\n * Any kind of type.\n */\nexport interface IAnyType extends IType<any, any, any> {}\n\n/**\n * A simple type, this is, a type where the instance and the snapshot representation are the same.\n */\nexport interface ISimpleType<T> extends IType<T, T, T> {}\n\n/** @hidden */\nexport type Primitives = ModelPrimitive | null | undefined\n\n/**\n * A complex type.\n * @deprecated just for compatibility with old versions, could be deprecated on the next major version\n * @hidden\n */\nexport interface IComplexType<C, S, T> extends IType<C, S, T & object> {}\n\n/**\n * Any kind of complex type.\n */\nexport interface IAnyComplexType extends IType<any, any, object> {}\n\n/** @hidden */\nexport type ExtractCSTWithoutSTN<\n    IT extends { [$type]: undefined; CreationType: any; SnapshotType: any; TypeWithoutSTN: any }\n> = IT[\"CreationType\"] | IT[\"SnapshotType\"] | IT[\"TypeWithoutSTN\"]\n/** @hidden */\nexport type ExtractCSTWithSTN<\n    IT extends { [$type]: undefined; CreationType: any; SnapshotType: any; Type: any }\n> = IT[\"CreationType\"] | IT[\"SnapshotType\"] | IT[\"Type\"]\n\n/**\n * The instance representation of a given type.\n */\nexport type Instance<T> = T extends { [$type]: undefined; Type: any } ? T[\"Type\"] : T\n\n/**\n * The input (creation) snapshot representation of a given type.\n */\nexport type SnapshotIn<T> = T extends { [$type]: undefined; CreationType: any }\n    ? T[\"CreationType\"]\n    : T extends IStateTreeNode<infer IT>\n      ? IT[\"CreationType\"]\n      : T\n\n/**\n * The output snapshot representation of a given type.\n */\nexport type SnapshotOut<T> = T extends { [$type]: undefined; SnapshotType: any }\n    ? T[\"SnapshotType\"]\n    : T extends IStateTreeNode<infer IT>\n      ? IT[\"SnapshotType\"]\n      : T\n\n/**\n * A type which is equivalent to the union of SnapshotIn and Instance types of a given typeof TYPE or typeof VARIABLE.\n * For primitives it defaults to the primitive itself.\n *\n * For example:\n * - `SnapshotOrInstance<typeof ModelA> = SnapshotIn<typeof ModelA> | Instance<typeof ModelA>`\n * - `SnapshotOrInstance<typeof self.a (where self.a is a ModelA)> = SnapshotIn<typeof ModelA> | Instance<typeof ModelA>`\n *\n * Usually you might want to use this when your model has a setter action that sets a property.\n *\n * Example:\n * ```ts\n * const ModelA = types.model({\n *   n: types.number\n * })\n *\n * const ModelB = types.model({\n *   innerModel: ModelA\n * }).actions(self => ({\n *   // this will accept as property both the snapshot and the instance, whichever is preferred\n *   setInnerModel(m: SnapshotOrInstance<typeof self.innerModel>) {\n *     self.innerModel = cast(m)\n *   }\n * }))\n * ```\n */\nexport type SnapshotOrInstance<T> = SnapshotIn<T> | Instance<T>\n\n/**\n * A base type produces a MST node (Node in the state tree)\n *\n * @internal\n * @hidden\n */\nexport abstract class BaseType<C, S, T, N extends BaseNode<any, any, any> = BaseNode<C, S, T>>\n    implements IType<C, S, T>\n{\n    [$type]!: undefined\n\n    // these are just to make inner types avaialable to inherited classes\n    readonly C!: C\n    readonly S!: S\n    readonly T!: T\n    readonly N!: N\n\n    readonly isType = true\n    readonly name: string\n\n    constructor(name: string) {\n        this.name = name\n    }\n\n    create(snapshot?: C, environment?: any) {\n        typecheckInternal(this, snapshot)\n        return this.instantiate(null, \"\", environment, snapshot!).value\n    }\n\n    getSnapshot(node: N, applyPostProcess?: boolean): S {\n        // istanbul ignore next\n        throw new MstError(\"unimplemented method\")\n    }\n\n    abstract reconcile(current: N, newValue: C | T, parent: AnyObjectNode, subpath: string): N\n\n    abstract instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: C | T\n    ): N\n\n    declare abstract flags: TypeFlags\n    abstract describe(): string\n\n    abstract isValidSnapshot(value: C, context: IValidationContext): IValidationResult\n\n    isAssignableFrom(type: IAnyType): boolean {\n        return type === this\n    }\n\n    validate(value: C | T, context: IValidationContext): IValidationResult {\n        const node = getStateTreeNodeSafe(value)\n        if (node) {\n            const valueType = getType(value)\n            return this.isAssignableFrom(valueType)\n                ? typeCheckSuccess()\n                : typeCheckFailure(context, value)\n            // it is tempting to compare snapshots, but in that case we should always clone on assignments...\n        }\n        return this.isValidSnapshot(value as C, context)\n    }\n\n    is(thing: any): thing is any {\n        return this.validate(thing, [{ path: \"\", type: this }]).length === 0\n    }\n\n    get Type(): any {\n        // istanbul ignore next\n        throw new MstError(\n            \"Factory.Type should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.Type`\"\n        )\n    }\n    get TypeWithoutSTN(): any {\n        // istanbul ignore next\n        throw new MstError(\n            \"Factory.TypeWithoutSTN should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.TypeWithoutSTN`\"\n        )\n    }\n    get SnapshotType(): any {\n        // istanbul ignore next\n        throw new MstError(\n            \"Factory.SnapshotType should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.SnapshotType`\"\n        )\n    }\n    get CreationType(): any {\n        // istanbul ignore next\n        throw new MstError(\n            \"Factory.CreationType should not be actually called. It is just a Type signature that can be used at compile time with Typescript, by using `typeof type.CreationType`\"\n        )\n    }\n\n    abstract getSubTypes(): IAnyType[] | IAnyType | null | typeof cannotDetermineSubtype\n}\nBaseType.prototype.create = action(BaseType.prototype.create)\n\n/**\n * @internal\n * @hidden\n */\nexport type AnyBaseType = BaseType<any, any, any, any>\n\n/**\n * @internal\n * @hidden\n */\nexport type ExtractNodeType<IT extends IAnyType> =\n    IT extends BaseType<any, any, any, infer N> ? N : never\n\n/**\n * A complex type produces a MST node (Node in the state tree)\n *\n * @internal\n * @hidden\n */\nexport abstract class ComplexType<C, S, T> extends BaseType<C, S, T, ObjectNode<C, S, T>> {\n    identifierAttribute?: string\n\n    constructor(name: string) {\n        super(name)\n    }\n\n    create(snapshot: C = this.getDefaultSnapshot(), environment?: any) {\n        return super.create(snapshot, environment)\n    }\n\n    getValue(node: this[\"N\"]): T {\n        node.createObservableInstanceIfNeeded()\n        return node.storedValue\n    }\n\n    abstract getDefaultSnapshot(): C\n\n    abstract createNewInstance(childNodes: IChildNodesMap): T\n    abstract finalizeNewInstance(node: this[\"N\"], instance: any): void\n\n    abstract applySnapshot(node: this[\"N\"], snapshot: C): void\n    abstract applyPatchLocally(node: this[\"N\"], subpath: string, patch: IJsonPatch): void\n    abstract processInitialSnapshot(childNodes: IChildNodesMap, snapshot: C): S\n\n    abstract getChildren(node: this[\"N\"]): ReadonlyArray<AnyNode>\n    abstract getChildNode(node: this[\"N\"], key: string): AnyNode\n    abstract getChildType(propertyName?: string): IAnyType\n    abstract initializeChildNodes(node: this[\"N\"], snapshot: any): IChildNodesMap\n    abstract removeChild(node: this[\"N\"], subpath: string): void\n\n    isMatchingSnapshotId(current: this[\"N\"], snapshot: C): boolean {\n        return (\n            !current.identifierAttribute ||\n            current.identifier ===\n                normalizeIdentifier((snapshot as any)[current.identifierAttribute])\n        )\n    }\n\n    private tryToReconcileNode(current: this[\"N\"], newValue: C | T) {\n        if (current.isDetaching) return false\n        if ((current.snapshot as any) === newValue) {\n            // newValue is the current snapshot of the node, noop\n            return true\n        }\n        if (isStateTreeNode(newValue) && getStateTreeNode(newValue) === current) {\n            // the current node is the same as the new one\n            return true\n        }\n        if (\n            current.type === this &&\n            isMutable(newValue) &&\n            !isStateTreeNode(newValue) &&\n            this.isMatchingSnapshotId(current, newValue as any)\n        ) {\n            // the newValue has no node, so can be treated like a snapshot\n            // we can reconcile\n            current.applySnapshot(newValue as C)\n            return true\n        }\n        return false\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: C | T,\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        const nodeReconciled = this.tryToReconcileNode(current, newValue)\n        if (nodeReconciled) {\n            current.setParent(parent, subpath)\n            return current\n        }\n\n        // current node cannot be recycled in any way\n        current.die() // noop if detaching\n        // attempt to reuse the new one\n        if (isStateTreeNode(newValue) && this.isAssignableFrom(getType(newValue))) {\n            // newValue is a Node as well, move it here..\n            const newNode = getStateTreeNode(newValue)\n            newNode.setParent(parent, subpath)\n            return newNode\n        }\n        // nothing to do, we have to create a new node\n        return this.instantiate(parent, subpath, undefined, newValue)\n    }\n\n    getSubTypes() {\n        return null\n    }\n}\nComplexType.prototype.create = action(ComplexType.prototype.create)\n\n/**\n * @internal\n * @hidden\n */\nexport abstract class SimpleType<C, S, T> extends BaseType<C, S, T, ScalarNode<C, S, T>> {\n    abstract instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: C\n    ): this[\"N\"]\n\n    createNewInstance(snapshot: C): T {\n        return snapshot as any\n    }\n\n    getValue(node: this[\"N\"]): T {\n        // if we ever find a case where scalar nodes can be accessed without iterating through its parent\n        // uncomment this to make sure the parent chain is created when this is accessed\n        // if (node.parent) {\n        //     node.parent.createObservableInstanceIfNeeded()\n        // }\n        return node.storedValue\n    }\n\n    getSnapshot(node: this[\"N\"]): S {\n        return node.storedValue\n    }\n\n    reconcile(current: this[\"N\"], newValue: C, parent: AnyObjectNode, subpath: string): this[\"N\"] {\n        // reconcile only if type and value are still the same, and only if the node is not detaching\n        if (!current.isDetaching && current.type === this && current.storedValue === newValue) {\n            return current\n        }\n        const res = this.instantiate(parent, subpath, undefined, newValue)\n        current.die() // noop if detaching\n        return res\n    }\n\n    getSubTypes() {\n        return null\n    }\n}\n\n/**\n * Returns if a given value represents a type.\n *\n * @param value Value to check.\n * @returns `true` if the value is a type.\n */\nexport function isType(value: any): value is IAnyType {\n    return typeof value === \"object\" && value && value.isType === true\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertIsType(type: IAnyType, argNumber: number | number[]) {\n    assertArg(type, isType, \"mobx-state-tree type\", argNumber)\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "// tslint:disable-next-line:no_unused-variable\nimport { IObservableArray, ObservableMap } from \"mobx\"\n\n/* all code is initially loaded through internal, to avoid circular dep issues */\nexport {\n    type IModelType,\n    type IAnyModelType,\n    type IDisposer,\n    type IMSTMap,\n    type IMapType,\n    type IMSTArray,\n    type IArrayType,\n    type IType,\n    type IAnyType,\n    type ModelPrimitive,\n    type ISimpleType,\n    type IComplexType,\n    type IAnyComplexType,\n    type IReferenceType,\n    type _CustomCSProcessor,\n    type _CustomOrOther,\n    type _CustomJoin,\n    type _NotCustomized,\n    typecheck,\n    escapeJsonPath,\n    unescapeJsonPath,\n    joinJsonPath,\n    splitJsonPath,\n    type IJsonPatch,\n    type IReversibleJsonPatch,\n    decorate,\n    addMiddleware,\n    type IMiddlewareEvent,\n    type IActionTrackingMiddleware2Call,\n    type IMiddlewareHandler,\n    type IMiddlewareEventType,\n    type IActionTrackingMiddlewareHooks,\n    type IActionTrackingMiddleware2Hooks,\n    process,\n    isStateTreeNode,\n    type IStateTreeNode,\n    type IAnyStateTreeNode,\n    flow,\n    castFlowReturn,\n    applyAction,\n    onAction,\n    type IActionRecorder,\n    type ISerializedActionCall,\n    recordActions,\n    createActionTrackingMiddleware,\n    createActionTrackingMiddleware2,\n    setLivelinessChecking,\n    getLivelinessChecking,\n    type LivelinessMode,\n    setLivelynessChecking, // to be deprecated\n    type LivelynessMode, // to be deprecated\n    type ModelSnapshotType,\n    type ModelCreationType,\n    type ModelSnapshotType2,\n    type ModelCreationType2,\n    type ModelInstanceType,\n    type ModelInstanceTypeProps,\n    type ModelPropertiesDeclarationToProperties,\n    type ModelProperties,\n    type ModelPropertiesDeclaration,\n    type ModelActions,\n    type ITypeUnion,\n    type CustomTypeOptions,\n    type UnionOptions,\n    type Instance,\n    type SnapshotIn,\n    type SnapshotOut,\n    type SnapshotOrInstance,\n    type TypeOrStateTreeNodeToStateTreeNode,\n    type UnionStringArray,\n    getType,\n    getChildType,\n    onPatch,\n    onSnapshot,\n    applyPatch,\n    type IPatchRecorder,\n    recordPatches,\n    protect,\n    unprotect,\n    isProtected,\n    applySnapshot,\n    getSnapshot,\n    hasParent,\n    getParent,\n    hasParentOfType,\n    getParentOfType,\n    getRoot,\n    getPath,\n    getPathParts,\n    isRoot,\n    resolvePath,\n    resolveIdentifier,\n    getIdentifier,\n    tryResolve,\n    getRelativePath,\n    clone,\n    detach,\n    destroy,\n    isAlive,\n    addDisposer,\n    hasEnv,\n    getEnv,\n    walk,\n    type IModelReflectionData,\n    type IModelReflectionPropertiesData,\n    type IMaybeIType,\n    type IMaybe,\n    type IMaybeNull,\n    type IOptionalIType,\n    type OptionalDefaultValueOrFunction,\n    type ValidOptionalValue,\n    type ValidOptionalValues,\n    getMembers,\n    getPropertyMembers,\n    type TypeOfValue,\n    cast,\n    castToSnapshot,\n    castToReferenceSnapshot,\n    isType,\n    isArrayType,\n    isFrozenType,\n    isIdentifierType,\n    isLateType,\n    isLiteralType,\n    isMapType,\n    isModelType,\n    isOptionalType,\n    isPrimitiveType,\n    isReferenceType,\n    isRefinementType,\n    isUnionType,\n    tryReference,\n    isValidReference,\n    type OnReferenceInvalidated,\n    type OnReferenceInvalidatedEvent,\n    type ReferenceOptions,\n    type ReferenceOptionsGetSet,\n    type ReferenceOptionsOnInvalidated,\n    type ReferenceIdentifier,\n    type ISnapshotProcessor,\n    type ISnapshotProcessors,\n    getNodeId,\n    type IActionContext,\n    getRunningActionContext,\n    isActionContextChildOf,\n    isActionContextThisOrChildOf,\n    types,\n    types as t, // We do this as a less ambiguous term for the traditional types module, which sometimes gets confused when discussing \"types\" in general\n    toGeneratorFunction,\n    toGenerator\n} from \"./internal\"\n"
  },
  {
    "path": "src/internal.ts",
    "content": "/*\n * All imports / exports should be proxied through this file.\n * Why? It gives us full control over the module load order, preventing circular dependency isses\n */\n\nexport * from \"./core/node/livelinessChecking\"\nexport * from \"./core/node/Hook\"\nexport * from \"./core/mst-operations\"\nexport * from \"./core/node/BaseNode\"\nexport * from \"./core/node/scalar-node\"\nexport * from \"./core/node/object-node\"\nexport * from \"./core/type/type\"\nexport * from \"./middlewares/create-action-tracking-middleware\"\nexport * from \"./middlewares/createActionTrackingMiddleware2\"\nexport * from \"./middlewares/on-action\"\nexport * from \"./core/action\"\nexport * from \"./core/actionContext\"\nexport * from \"./core/type/type-checker\"\nexport * from \"./core/node/identifier-cache\"\nexport * from \"./core/node/create-node\"\nexport * from \"./core/node/node-utils\"\nexport * from \"./core/process\"\nexport * from \"./core/flow\"\nexport * from \"./core/json-patch\"\nexport * from \"./utils\"\nexport * from \"./types/utility-types/snapshotProcessor\"\nexport * from \"./types/complex-types/map\"\nexport * from \"./types/complex-types/array\"\nexport * from \"./types/complex-types/model\"\nexport * from \"./types/primitives\"\nexport * from \"./types/utility-types/literal\"\nexport * from \"./types/utility-types/refinement\"\nexport * from \"./types/utility-types/enumeration\"\nexport * from \"./types/utility-types/union\"\nexport * from \"./types/utility-types/optional\"\nexport * from \"./types/utility-types/maybe\"\nexport * from \"./types/utility-types/late\"\nexport * from \"./types/utility-types/lazy\"\nexport * from \"./types/utility-types/frozen\"\nexport * from \"./types/utility-types/reference\"\nexport * from \"./types/utility-types/identifier\"\nexport * from \"./types/utility-types/custom\"\nexport * from \"./types\"\n"
  },
  {
    "path": "src/middlewares/create-action-tracking-middleware.ts",
    "content": "import { IMiddlewareEvent, IMiddlewareHandler } from \"../internal\"\n\nconst runningActions = new Map<number, { async: boolean; call: IMiddlewareEvent; context: any }>()\n\nexport interface IActionTrackingMiddlewareHooks<T> {\n    filter?: (call: IMiddlewareEvent) => boolean\n    onStart: (call: IMiddlewareEvent) => T\n    onResume: (call: IMiddlewareEvent, context: T) => void\n    onSuspend: (call: IMiddlewareEvent, context: T) => void\n    onSuccess: (call: IMiddlewareEvent, context: T, result: any) => void\n    onFail: (call: IMiddlewareEvent, context: T, error: any) => void\n}\n\n/**\n * Note: Consider migrating to `createActionTrackingMiddleware2`, it is easier to use.\n *\n * Convenience utility to create action based middleware that supports async processes more easily.\n * All hooks are called for both synchronous and asynchronous actions. Except that either `onSuccess` or `onFail` is called\n *\n * The create middleware tracks the process of an action (assuming it passes the `filter`).\n * `onResume` can return any value, which will be passed as second argument to any other hook. This makes it possible to keep state during a process.\n *\n * See the `atomic` middleware for an example\n *\n * @param hooks\n * @returns\n */\nexport function createActionTrackingMiddleware<T = any>(\n    hooks: IActionTrackingMiddlewareHooks<T>\n): IMiddlewareHandler {\n    return function actionTrackingMiddleware(\n        call: IMiddlewareEvent,\n        next: (actionCall: IMiddlewareEvent) => any,\n        abort: (value: any) => any\n    ) {\n        switch (call.type) {\n            case \"action\": {\n                if (!hooks.filter || hooks.filter(call) === true) {\n                    const context = hooks.onStart(call)\n                    hooks.onResume(call, context)\n                    runningActions.set(call.id, {\n                        call,\n                        context,\n                        async: false\n                    })\n                    try {\n                        const res = next(call)\n                        hooks.onSuspend(call, context)\n                        if (runningActions.get(call.id)!.async === false) {\n                            runningActions.delete(call.id)\n                            hooks.onSuccess(call, context, res)\n                        }\n                        return res\n                    } catch (e) {\n                        runningActions.delete(call.id)\n                        hooks.onFail(call, context, e)\n                        throw e\n                    }\n                } else {\n                    return next(call)\n                }\n            }\n            case \"flow_spawn\": {\n                const root = runningActions.get(call.rootId)!\n                root.async = true\n                return next(call)\n            }\n            case \"flow_resume\":\n            case \"flow_resume_error\": {\n                const root = runningActions.get(call.rootId)!\n                hooks.onResume(call, root.context)\n                try {\n                    return next(call)\n                } finally {\n                    hooks.onSuspend(call, root.context)\n                }\n            }\n            case \"flow_throw\": {\n                const root = runningActions.get(call.rootId)!\n                runningActions.delete(call.rootId)\n                hooks.onFail(call, root.context, call.args[0])\n                return next(call)\n            }\n            case \"flow_return\": {\n                const root = runningActions.get(call.rootId)!\n                runningActions.delete(call.rootId)\n                hooks.onSuccess(call, root.context, call.args[0])\n                return next(call)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/middlewares/createActionTrackingMiddleware2.ts",
    "content": "import { IMiddlewareEvent, IMiddlewareHandler, IActionContext } from \"../internal\"\n\nexport interface IActionTrackingMiddleware2Call<TEnv> extends Readonly<IActionContext> {\n    env: TEnv | undefined\n    readonly parentCall?: IActionTrackingMiddleware2Call<TEnv>\n}\n\nexport interface IActionTrackingMiddleware2Hooks<TEnv> {\n    filter?: (call: IActionTrackingMiddleware2Call<TEnv>) => boolean\n    onStart: (call: IActionTrackingMiddleware2Call<TEnv>) => void\n    onFinish: (call: IActionTrackingMiddleware2Call<TEnv>, error?: any) => void\n}\n\nclass RunningAction {\n    private flowsPending = 0\n    private running = true\n\n    constructor(\n        public readonly hooks: IActionTrackingMiddleware2Hooks<any> | undefined,\n        readonly call: IActionTrackingMiddleware2Call<any>\n    ) {\n        if (hooks) {\n            hooks.onStart(call)\n        }\n    }\n\n    finish(error?: any) {\n        if (this.running) {\n            this.running = false\n            if (this.hooks) {\n                this.hooks.onFinish(this.call, error)\n            }\n        }\n    }\n\n    incFlowsPending() {\n        this.flowsPending++\n    }\n\n    decFlowsPending() {\n        this.flowsPending--\n    }\n\n    get hasFlowsPending() {\n        return this.flowsPending > 0\n    }\n}\n\n/**\n * Convenience utility to create action based middleware that supports async processes more easily.\n * The flow is like this:\n * - for each action: if filter passes -> `onStart` -> (inner actions recursively) -> `onFinish`\n *\n * Example: if we had an action `a` that called inside an action `b1`, then `b2` the flow would be:\n * - `filter(a)`\n * - `onStart(a)`\n *   - `filter(b1)`\n *   - `onStart(b1)`\n *   - `onFinish(b1)`\n *   - `filter(b2)`\n *   - `onStart(b2)`\n *   - `onFinish(b2)`\n * - `onFinish(a)`\n *\n * The flow is the same no matter if the actions are sync or async.\n *\n * See the `atomic` middleware for an example\n *\n * @param hooks\n * @returns\n */\nexport function createActionTrackingMiddleware2<TEnv = any>(\n    middlewareHooks: IActionTrackingMiddleware2Hooks<TEnv>\n): IMiddlewareHandler {\n    const runningActions = new Map<number, RunningAction>()\n\n    return function actionTrackingMiddleware(\n        call: IMiddlewareEvent,\n        next: (actionCall: IMiddlewareEvent) => any\n    ) {\n        // find parentRunningAction\n        const parentRunningAction = call.parentActionEvent\n            ? runningActions.get(call.parentActionEvent.id)\n            : undefined\n\n        if (call.type === \"action\") {\n            const newCall: IActionTrackingMiddleware2Call<TEnv> = {\n                ...call,\n                // make a shallow copy of the parent action env\n                env: parentRunningAction && parentRunningAction.call.env,\n                parentCall: parentRunningAction && parentRunningAction.call\n            }\n\n            const passesFilter = !middlewareHooks.filter || middlewareHooks.filter(newCall)\n            const hooks = passesFilter ? middlewareHooks : undefined\n\n            const runningAction = new RunningAction(hooks, newCall)\n            runningActions.set(call.id, runningAction)\n\n            let res\n            try {\n                res = next(call)\n            } catch (e) {\n                runningActions.delete(call.id)\n                runningAction.finish(e)\n                throw e\n            }\n            // sync action finished\n            if (!runningAction.hasFlowsPending) {\n                runningActions.delete(call.id)\n                runningAction.finish()\n            }\n            return res\n        } else {\n            if (!parentRunningAction) {\n                return next(call)\n            }\n\n            switch (call.type) {\n                case \"flow_spawn\": {\n                    parentRunningAction.incFlowsPending()\n                    return next(call)\n                }\n                case \"flow_resume\":\n                case \"flow_resume_error\": {\n                    return next(call)\n                }\n                case \"flow_throw\": {\n                    const error = call.args[0]\n                    try {\n                        return next(call)\n                    } finally {\n                        parentRunningAction.decFlowsPending()\n                        if (!parentRunningAction.hasFlowsPending) {\n                            runningActions.delete(call.parentActionEvent!.id)\n                            parentRunningAction.finish(error)\n                        }\n                    }\n                }\n                case \"flow_return\": {\n                    try {\n                        return next(call)\n                    } finally {\n                        parentRunningAction.decFlowsPending()\n                        if (!parentRunningAction.hasFlowsPending) {\n                            runningActions.delete(call.parentActionEvent!.id)\n                            parentRunningAction.finish()\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/middlewares/on-action.ts",
    "content": "import { runInAction } from \"mobx\"\n\nimport {\n    getStateTreeNode,\n    isStateTreeNode,\n    addMiddleware,\n    tryResolve,\n    applyPatch,\n    getType,\n    applySnapshot,\n    isRoot,\n    isProtected,\n    MstError,\n    isPlainObject,\n    isPrimitive,\n    IDisposer,\n    isArray,\n    asArray,\n    getRelativePathBetweenNodes,\n    IAnyStateTreeNode,\n    warnError,\n    AnyNode,\n    assertIsStateTreeNode,\n    devMode,\n    assertArg,\n    IActionContext,\n    getRunningActionContext\n} from \"../internal\"\n\nexport interface ISerializedActionCall {\n    name: string\n    path?: string\n    args?: any[]\n}\n\nexport interface IActionRecorder {\n    actions: ReadonlyArray<ISerializedActionCall>\n    readonly recording: boolean\n    stop(): void\n    resume(): void\n    replay(target: IAnyStateTreeNode): void\n}\n\nfunction serializeArgument(node: AnyNode, actionName: string, index: number, arg: any): any {\n    if (arg instanceof Date) return { $MST_DATE: arg.getTime() }\n    if (isPrimitive(arg)) return arg\n    // We should not serialize MST nodes, even if we can, because we don't know if the receiving party can handle a raw snapshot instead of an\n    // MST type instance. So if one wants to serialize a MST node that was pass in, either explitly pass: 1: an id, 2: a (relative) path, 3: a snapshot\n    if (isStateTreeNode(arg)) return serializeTheUnserializable(`[MSTNode: ${getType(arg).name}]`)\n    if (typeof arg === \"function\") return serializeTheUnserializable(`[function]`)\n    if (typeof arg === \"object\" && !isPlainObject(arg) && !isArray(arg))\n        return serializeTheUnserializable(\n            `[object ${\n                (arg && (arg as any).constructor && (arg as any).constructor.name) ||\n                \"Complex Object\"\n            }]`\n        )\n    try {\n        // Check if serializable, cycle free etc...\n        // MWE: there must be a better way....\n        JSON.stringify(arg) // or throws\n        return arg\n    } catch (e) {\n        return serializeTheUnserializable(\"\" + e)\n    }\n}\n\nfunction deserializeArgument(adm: AnyNode, value: any): any {\n    if (value && typeof value === \"object\" && \"$MST_DATE\" in value)\n        return new Date(value[\"$MST_DATE\"])\n    return value\n}\n\nfunction serializeTheUnserializable(baseType: string) {\n    return {\n        $MST_UNSERIALIZABLE: true,\n        type: baseType\n    }\n}\n\n/**\n * Applies an action or a series of actions in a single MobX transaction.\n * Does not return any value\n * Takes an action description as produced by the `onAction` middleware.\n *\n * @param target\n * @param actions\n */\nexport function applyAction(\n    target: IAnyStateTreeNode,\n    actions: ISerializedActionCall | ISerializedActionCall[]\n): void {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    assertArg(actions, a => typeof a === \"object\", \"object or array\", 2)\n\n    runInAction(() => {\n        asArray(actions).forEach(action => baseApplyAction(target, action))\n    })\n}\n\nfunction baseApplyAction(target: IAnyStateTreeNode, action: ISerializedActionCall): any {\n    const resolvedTarget = tryResolve(target, action.path || \"\")\n    if (!resolvedTarget) throw new MstError(`Invalid action path: ${action.path || \"\"}`)\n    const node = getStateTreeNode(resolvedTarget)\n\n    // Reserved functions\n    if (action.name === \"@APPLY_PATCHES\") {\n        return applyPatch.call(null, resolvedTarget, action.args![0])\n    }\n    if (action.name === \"@APPLY_SNAPSHOT\") {\n        return applySnapshot.call(null, resolvedTarget, action.args![0])\n    }\n\n    if (!(typeof resolvedTarget[action.name] === \"function\"))\n        throw new MstError(`Action '${action.name}' does not exist in '${node.path}'`)\n    return resolvedTarget[action.name].apply(\n        resolvedTarget,\n        action.args ? action.args.map(v => deserializeArgument(node, v)) : []\n    )\n}\n\n/**\n * Small abstraction around `onAction` and `applyAction`, attaches an action listener to a tree and records all the actions emitted.\n * Returns an recorder object with the following signature:\n *\n * Example:\n * ```ts\n * export interface IActionRecorder {\n *      // the recorded actions\n *      actions: ISerializedActionCall[]\n *      // true if currently recording\n *      recording: boolean\n *      // stop recording actions\n *      stop(): void\n *      // resume recording actions\n *      resume(): void\n *      // apply all the recorded actions on the given object\n *      replay(target: IAnyStateTreeNode): void\n * }\n * ```\n *\n * The optional filter function allows to skip recording certain actions.\n *\n * @param subject\n * @returns\n */\nexport function recordActions(\n    subject: IAnyStateTreeNode,\n    filter?: (action: ISerializedActionCall, actionContext: IActionContext | undefined) => boolean\n): IActionRecorder {\n    // check all arguments\n    assertIsStateTreeNode(subject, 1)\n\n    const actions: ISerializedActionCall[] = []\n    const listener = (call: ISerializedActionCall) => {\n        const recordThis = filter ? filter(call, getRunningActionContext()) : true\n        if (recordThis) {\n            actions.push(call)\n        }\n    }\n\n    let disposer: IDisposer | undefined\n    const recorder: IActionRecorder = {\n        actions,\n        get recording() {\n            return !!disposer\n        },\n        stop() {\n            if (disposer) {\n                disposer()\n                disposer = undefined\n            }\n        },\n        resume() {\n            if (disposer) return\n            disposer = onAction(subject, listener)\n        },\n        replay(target) {\n            applyAction(target, actions)\n        }\n    }\n\n    recorder.resume()\n    return recorder\n}\n\n/**\n * Registers a function that will be invoked for each action that is called on the provided model instance, or to any of its children.\n * See [actions](https://github.com/mobxjs/mobx-state-tree#actions) for more details. onAction events are emitted only for the outermost called action in the stack.\n * Action can also be intercepted by middleware using addMiddleware to change the function call before it will be run.\n *\n * Not all action arguments might be serializable. For unserializable arguments, a struct like `{ $MST_UNSERIALIZABLE: true, type: \"someType\" }` will be generated.\n * MST Nodes are considered non-serializable as well (they could be serialized as there snapshot, but it is uncertain whether an replaying party will be able to handle such a non-instantiated snapshot).\n * Rather, when using `onAction` middleware, one should consider in passing arguments which are 1: an id, 2: a (relative) path, or 3: a snapshot. Instead of a real MST node.\n *\n * Example:\n * ```ts\n * const Todo = types.model({\n *   task: types.string\n * })\n *\n * const TodoStore = types.model({\n *   todos: types.array(Todo)\n * }).actions(self => ({\n *   add(todo) {\n *     self.todos.push(todo);\n *   }\n * }))\n *\n * const s = TodoStore.create({ todos: [] })\n *\n * let disposer = onAction(s, (call) => {\n *   console.log(call);\n * })\n *\n * s.add({ task: \"Grab a coffee\" })\n * // Logs: { name: \"add\", path: \"\", args: [{ task: \"Grab a coffee\" }] }\n * ```\n *\n * @param target\n * @param listener\n * @param attachAfter (default false) fires the listener *after* the action has executed instead of before.\n * @returns\n */\nexport function onAction(\n    target: IAnyStateTreeNode,\n    listener: (call: ISerializedActionCall) => void,\n    attachAfter = false\n): IDisposer {\n    // check all arguments\n    assertIsStateTreeNode(target, 1)\n    if (devMode()) {\n        if (!isRoot(target))\n            warnError(\n                \"Warning: Attaching onAction listeners to non root nodes is dangerous: No events will be emitted for actions initiated higher up in the tree.\"\n            )\n        if (!isProtected(target))\n            warnError(\n                \"Warning: Attaching onAction listeners to non protected nodes is dangerous: No events will be emitted for direct modifications without action.\"\n            )\n    }\n\n    return addMiddleware(target, function handler(rawCall, next) {\n        if (rawCall.type === \"action\" && rawCall.id === rawCall.rootId) {\n            const sourceNode = getStateTreeNode(rawCall.context)\n            const info = {\n                name: rawCall.name,\n                path: getRelativePathBetweenNodes(getStateTreeNode(target), sourceNode),\n                args: rawCall.args.map((arg: any, index: number) =>\n                    serializeArgument(sourceNode, rawCall.name, index, arg)\n                )\n            }\n            if (attachAfter) {\n                const res = next(rawCall)\n                listener(info)\n                return res\n            } else {\n                listener(info)\n                return next(rawCall)\n            }\n        } else {\n            return next(rawCall)\n        }\n    })\n}\n"
  },
  {
    "path": "src/types/complex-types/array.ts",
    "content": "import {\n    _getAdministration,\n    action,\n    IArrayDidChange,\n    IArrayWillChange,\n    IArrayWillSplice,\n    intercept,\n    IObservableArray,\n    observable,\n    observe\n} from \"mobx\"\nimport {\n    addHiddenFinalProp,\n    addHiddenWritableProp,\n    AnyNode,\n    AnyObjectNode,\n    assertIsType,\n    ComplexType,\n    convertChildNodesToArray,\n    createActionInvoker,\n    createObjectNode,\n    devMode,\n    EMPTY_ARRAY,\n    EMPTY_OBJECT,\n    ExtractCSTWithSTN,\n    MstError,\n    flattenTypeErrors,\n    getContextForPath,\n    getStateTreeNode,\n    IAnyStateTreeNode,\n    IAnyType,\n    IChildNodesMap,\n    IHooksGetter,\n    IJsonPatch,\n    isArray,\n    isNode,\n    isPlainObject,\n    isStateTreeNode,\n    IStateTreeNode,\n    isType,\n    IType,\n    IValidationContext,\n    IValidationResult,\n    mobxShallow,\n    ObjectNode,\n    typeCheckFailure,\n    typecheckInternal,\n    TypeFlags\n} from \"../../internal\"\n\n/** @hidden */\nexport interface IMSTArray<IT extends IAnyType> extends IObservableArray<IT[\"Type\"]> {\n    // needs to be split or else it will complain about not being compatible with the array interface\n    push(...items: IT[\"Type\"][]): number\n    push(...items: ExtractCSTWithSTN<IT>[]): number\n\n    concat(...items: ConcatArray<IT[\"Type\"]>[]): IT[\"Type\"][]\n    concat(...items: ConcatArray<ExtractCSTWithSTN<IT>>[]): IT[\"Type\"][]\n\n    concat(...items: (IT[\"Type\"] | ConcatArray<IT[\"Type\"]>)[]): IT[\"Type\"][]\n    concat(...items: (ExtractCSTWithSTN<IT> | ConcatArray<ExtractCSTWithSTN<IT>>)[]): IT[\"Type\"][]\n\n    splice(start: number, deleteCount?: number): IT[\"Type\"][]\n    splice(start: number, deleteCount: number, ...items: IT[\"Type\"][]): IT[\"Type\"][]\n    splice(start: number, deleteCount: number, ...items: ExtractCSTWithSTN<IT>[]): IT[\"Type\"][]\n\n    unshift(...items: IT[\"Type\"][]): number\n    unshift(...items: ExtractCSTWithSTN<IT>[]): number\n}\n\n/** @hidden */\nexport interface IArrayType<IT extends IAnyType>\n    extends IType<readonly IT[\"CreationType\"][] | undefined, IT[\"SnapshotType\"][], IMSTArray<IT>> {\n    hooks(hooks: IHooksGetter<IMSTArray<IAnyType>>): IArrayType<IT>\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class ArrayType<IT extends IAnyType> extends ComplexType<\n    readonly IT[\"CreationType\"][] | undefined,\n    IT[\"SnapshotType\"][],\n    IMSTArray<IT>\n> {\n    readonly flags = TypeFlags.Array\n    private readonly hookInitializers: Array<IHooksGetter<IMSTArray<IT>>> = []\n    constructor(\n        name: string,\n        private readonly _subType: IT,\n        hookInitializers: Array<IHooksGetter<IMSTArray<IT>>> = []\n    ) {\n        super(name)\n        this.hookInitializers = hookInitializers\n    }\n\n    hooks(hooks: IHooksGetter<IMSTArray<IT>>) {\n        const hookInitializers =\n            this.hookInitializers.length > 0 ? this.hookInitializers.concat(hooks) : [hooks]\n        return new ArrayType(this.name, this._subType, hookInitializers)\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        return createObjectNode(this, parent, subpath, environment, initialValue)\n    }\n\n    initializeChildNodes(objNode: this[\"N\"], snapshot: this[\"C\"] = []): IChildNodesMap {\n        const subType = (objNode.type as this)._subType\n        const result: IChildNodesMap = {}\n        snapshot.forEach((item, index) => {\n            const subpath = \"\" + index\n            result[subpath] = subType.instantiate(objNode, subpath, undefined, item)\n        })\n        return result\n    }\n\n    createNewInstance(childNodes: IChildNodesMap): this[\"T\"] {\n        const options = { ...mobxShallow, name: this.name }\n        return observable.array(convertChildNodesToArray(childNodes), options) as this[\"T\"]\n    }\n\n    finalizeNewInstance(node: this[\"N\"], instance: this[\"T\"]): void {\n        _getAdministration(instance).dehancer = node.unbox\n\n        const type = node.type as this\n        type.hookInitializers.forEach(initializer => {\n            const hooks = initializer(instance)\n            Object.keys(hooks).forEach(name => {\n                const hook = hooks[name as keyof typeof hooks]!\n                const actionInvoker = createActionInvoker(instance as IAnyStateTreeNode, name, hook)\n                ;(!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(\n                    instance,\n                    name,\n                    actionInvoker\n                )\n            })\n        })\n\n        intercept(instance as IObservableArray<AnyNode>, this.willChange)\n        observe(instance as IObservableArray<AnyNode>, this.didChange)\n    }\n\n    describe() {\n        return this.name\n    }\n\n    getChildren(node: this[\"N\"]): AnyNode[] {\n        return node.storedValue.slice()\n    }\n\n    getChildNode(node: this[\"N\"], key: string): AnyNode {\n        const index = Number(key)\n        if (index < node.storedValue.length) return node.storedValue[index]\n        throw new MstError(\"Not a child: \" + key)\n    }\n\n    willChange(\n        change: IArrayWillChange<AnyNode> | IArrayWillSplice<AnyNode>\n    ): IArrayWillChange<AnyNode> | IArrayWillSplice<AnyNode> | null {\n        const node = getStateTreeNode(change.object as IStateTreeNode<this>)\n        node.assertWritable({ subpath: \"\" + change.index })\n        const subType = (node.type as this)._subType\n        const childNodes = node.getChildren()\n\n        switch (change.type) {\n            case \"update\":\n                {\n                    if (change.newValue === change.object[change.index]) return null\n\n                    const updatedNodes = reconcileArrayChildren(\n                        node,\n                        subType,\n                        [childNodes[change.index]],\n                        [change.newValue],\n                        [change.index]\n                    )\n                    if (!updatedNodes) {\n                        return null\n                    }\n                    change.newValue = updatedNodes[0]\n                }\n                break\n            case \"splice\":\n                {\n                    const { index, removedCount, added } = change\n\n                    const addedNodes = reconcileArrayChildren(\n                        node,\n                        subType,\n                        childNodes.slice(index, index + removedCount),\n                        added,\n                        added.map((_, i) => index + i)\n                    )\n                    if (!addedNodes) {\n                        return null\n                    }\n                    change.added = addedNodes\n\n                    // update paths of remaining items\n                    for (let i = index + removedCount; i < childNodes.length; i++) {\n                        childNodes[i].setParent(node, \"\" + (i + added.length - removedCount))\n                    }\n                }\n                break\n        }\n        return change\n    }\n\n    getSnapshot(node: this[\"N\"]): this[\"S\"] {\n        return node.getChildren().map(childNode => childNode.snapshot)\n    }\n\n    processInitialSnapshot(childNodes: IChildNodesMap): this[\"S\"] {\n        const processed: this[\"S\"] = []\n        Object.keys(childNodes).forEach(key => {\n            processed.push(childNodes[key].getSnapshot())\n        })\n        return processed\n    }\n\n    didChange(change: IArrayDidChange<AnyNode>): void {\n        const node = getStateTreeNode(change.object as IAnyStateTreeNode)\n        switch (change.type) {\n            case \"update\":\n                return void node.emitPatch(\n                    {\n                        op: \"replace\",\n                        path: \"\" + change.index,\n                        value: change.newValue.snapshot,\n                        oldValue: change.oldValue ? change.oldValue.snapshot : undefined\n                    },\n                    node\n                )\n            case \"splice\":\n                // Perf: emit one patch for clear and replace ops.\n                if (change.removedCount && change.addedCount === change.object.length) {\n                    return void node.emitPatch(\n                        {\n                            op: \"replace\",\n                            path: \"\",\n                            value: node.snapshot,\n                            oldValue: change.removed.map(node => node.snapshot)\n                        },\n                        node\n                    )\n                }\n\n                for (let i = change.removedCount - 1; i >= 0; i--)\n                    node.emitPatch(\n                        {\n                            op: \"remove\",\n                            path: \"\" + (change.index + i),\n                            oldValue: change.removed[i].snapshot\n                        },\n                        node\n                    )\n                for (let i = 0; i < change.addedCount; i++)\n                    node.emitPatch(\n                        {\n                            op: \"add\",\n                            path: \"\" + (change.index + i),\n                            value: change.added[i].snapshot,\n                            oldValue: undefined\n                        },\n                        node\n                    )\n                return\n        }\n    }\n\n    applyPatchLocally(node: this[\"N\"], subpath: string, patch: IJsonPatch): void {\n        const target = node.storedValue\n        const index = subpath === \"-\" ? target.length : Number(subpath)\n        switch (patch.op) {\n            case \"replace\":\n                target[index] = patch.value\n                break\n            case \"add\":\n                target.splice(index, 0, patch.value)\n                break\n            case \"remove\":\n                target.splice(index, 1)\n                break\n        }\n    }\n\n    applySnapshot(node: this[\"N\"], snapshot: this[\"C\"]): void {\n        typecheckInternal(this, snapshot)\n        const target = node.storedValue\n        target.replace(snapshot as any)\n    }\n\n    getChildType(): IAnyType {\n        return this._subType\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (!isArray(value)) {\n            return typeCheckFailure(context, value, \"Value is not an array\")\n        }\n\n        return flattenTypeErrors(\n            value.map((item, index) =>\n                this._subType.validate(item, getContextForPath(context, \"\" + index, this._subType))\n            )\n        )\n    }\n\n    getDefaultSnapshot(): this[\"C\"] {\n        return EMPTY_ARRAY as this[\"C\"]\n    }\n\n    removeChild(node: this[\"N\"], subpath: string) {\n        node.storedValue.splice(Number(subpath), 1)\n    }\n}\nArrayType.prototype.applySnapshot = action(ArrayType.prototype.applySnapshot)\n\n/**\n * `types.array` - Creates an index based collection type who's children are all of a uniform declared type.\n *\n * This type will always produce [observable arrays](https://mobx.js.org/api.html#observablearray)\n *\n * Example:\n * ```ts\n * const Todo = types.model({\n *   task: types.string\n * })\n *\n * const TodoStore = types.model({\n *   todos: types.array(Todo)\n * })\n *\n * const s = TodoStore.create({ todos: [] })\n * unprotect(s) // needed to allow modifying outside of an action\n * s.todos.push({ task: \"Grab coffee\" })\n * console.log(s.todos[0]) // prints: \"Grab coffee\"\n * ```\n *\n * @param subtype\n * @returns\n */\nexport function array<IT extends IAnyType>(subtype: IT): IArrayType<IT> {\n    assertIsType(subtype, 1)\n    return new ArrayType<IT>(`${subtype.name}[]`, subtype)\n}\n\nfunction reconcileArrayChildren<TT>(\n    parent: AnyObjectNode,\n    childType: IType<any, any, TT>,\n    oldNodes: AnyNode[],\n    newValues: TT[],\n    newPaths: (string | number)[]\n): AnyNode[] | null {\n    let nothingChanged = true\n\n    for (let i = 0; ; i++) {\n        const hasNewNode = i <= newValues.length - 1\n        const oldNode = oldNodes[i]\n        let newValue = hasNewNode ? newValues[i] : undefined\n        const newPath = \"\" + newPaths[i]\n\n        // for some reason, instead of newValue we got a node, fallback to the storedValue\n        // TODO: https://github.com/mobxjs/mobx-state-tree/issues/340#issuecomment-325581681\n        if (isNode(newValue)) newValue = newValue.storedValue\n\n        if (!oldNode && !hasNewNode) {\n            // both are empty, end\n            break\n        } else if (!hasNewNode) {\n            // new one does not exists\n            nothingChanged = false\n            oldNodes.splice(i, 1)\n            if (oldNode instanceof ObjectNode) {\n                // since it is going to be returned by pop/splice/shift better create it before killing it\n                // so it doesn't end up in an undead state\n                oldNode.createObservableInstanceIfNeeded()\n            }\n            oldNode.die()\n            i--\n        } else if (!oldNode) {\n            // there is no old node, create it\n            // check if already belongs to the same parent. if so, avoid pushing item in. only swapping can occur.\n            if (isStateTreeNode(newValue) && getStateTreeNode(newValue).parent === parent) {\n                // this node is owned by this parent, but not in the reconcilable set, so it must be double\n                throw new MstError(\n                    `Cannot add an object to a state tree if it is already part of the same or another state tree. Tried to assign an object to '${\n                        parent.path\n                    }/${newPath}', but it lives already at '${getStateTreeNode(newValue).path}'`\n                )\n            }\n            nothingChanged = false\n            const newNode = valueAsNode(childType, parent, newPath, newValue)\n            oldNodes.splice(i, 0, newNode)\n        } else if (areSame(oldNode, newValue)) {\n            // both are the same, reconcile\n            oldNodes[i] = valueAsNode(childType, parent, newPath, newValue, oldNode)\n        } else {\n            // nothing to do, try to reorder\n            let oldMatch = undefined\n\n            // find a possible candidate to reuse\n            for (let j = i; j < oldNodes.length; j++) {\n                if (areSame(oldNodes[j], newValue)) {\n                    oldMatch = oldNodes.splice(j, 1)[0]\n                    break\n                }\n            }\n\n            nothingChanged = false\n            const newNode = valueAsNode(childType, parent, newPath, newValue, oldMatch)\n            oldNodes.splice(i, 0, newNode)\n        }\n    }\n\n    return nothingChanged ? null : oldNodes\n}\n\n/**\n * Convert a value to a node at given parent and subpath. Attempts to reuse old node if possible and given.\n */\nfunction valueAsNode(\n    childType: IAnyType,\n    parent: AnyObjectNode,\n    subpath: string,\n    newValue: any,\n    oldNode?: AnyNode\n) {\n    // ensure the value is valid-ish\n    typecheckInternal(childType, newValue)\n\n    function getNewNode() {\n        // the new value has a MST node\n        if (isStateTreeNode(newValue)) {\n            const childNode = getStateTreeNode(newValue)\n            childNode.assertAlive(EMPTY_OBJECT)\n\n            // the node lives here\n            if (childNode.parent !== null && childNode.parent === parent) {\n                childNode.setParent(parent, subpath)\n                return childNode\n            }\n        }\n        // there is old node and new one is a value/snapshot\n        if (oldNode) {\n            return childType.reconcile(oldNode, newValue, parent, subpath)\n        }\n\n        // nothing to do, create from scratch\n        return childType.instantiate(parent, subpath, undefined, newValue)\n    }\n\n    const newNode = getNewNode()\n    if (oldNode && oldNode !== newNode) {\n        if (oldNode instanceof ObjectNode) {\n            // since it is going to be returned by pop/splice/shift better create it before killing it\n            // so it doesn't end up in an undead state\n            oldNode.createObservableInstanceIfNeeded()\n        }\n        oldNode.die()\n    }\n    return newNode\n}\n\n/**\n * Check if a node holds a value.\n */\nfunction areSame(oldNode: AnyNode, newValue: any) {\n    // never consider dead old nodes for reconciliation\n    if (!oldNode.isAlive) {\n        return false\n    }\n\n    // the new value has the same node\n    if (isStateTreeNode(newValue)) {\n        const newNode = getStateTreeNode(newValue)\n        return newNode.isAlive && newNode === oldNode\n    }\n\n    // the provided value is the snapshot of the old node\n    if (oldNode.snapshot === newValue) {\n        return true\n    }\n\n    // Non object nodes don't get reconciled\n    if (!(oldNode instanceof ObjectNode)) {\n        return false\n    }\n\n    const oldNodeType = oldNode.getReconciliationType()\n    // new value is a snapshot with the correct identifier\n    return (\n        oldNode.identifier !== null &&\n        oldNode.identifierAttribute &&\n        isPlainObject(newValue) &&\n        oldNodeType.is(newValue) &&\n        (oldNodeType as any).isMatchingSnapshotId(oldNode, newValue)\n    )\n}\n\n/**\n * Returns if a given value represents an array type.\n *\n * @param type\n * @returns `true` if the type is an array type.\n */\nexport function isArrayType(type: unknown): type is IArrayType<IAnyType> {\n    return isType(type) && (type.flags & TypeFlags.Array) > 0\n}\n"
  },
  {
    "path": "src/types/complex-types/map.ts",
    "content": "import {\n    _interceptReads,\n    action,\n    IInterceptor,\n    IKeyValueMap,\n    IMapDidChange,\n    IMapWillChange,\n    intercept,\n    Lambda,\n    observable,\n    ObservableMap,\n    observe,\n    values,\n    IObservableMapInitialValues\n} from \"mobx\"\nimport {\n    ComplexType,\n    createObjectNode,\n    escapeJsonPath,\n    MstError,\n    flattenTypeErrors,\n    getContextForPath,\n    getStateTreeNode,\n    IAnyStateTreeNode,\n    IAnyType,\n    IChildNodesMap,\n    IValidationContext,\n    IJsonPatch,\n    isMutable,\n    isPlainObject,\n    isStateTreeNode,\n    isType,\n    IType,\n    IValidationResult,\n    ModelType,\n    ObjectNode,\n    typecheckInternal,\n    typeCheckFailure,\n    TypeFlags,\n    EMPTY_OBJECT,\n    normalizeIdentifier,\n    AnyObjectNode,\n    AnyNode,\n    IAnyModelType,\n    asArray,\n    cannotDetermineSubtype,\n    getSnapshot,\n    isValidIdentifier,\n    ExtractCSTWithSTN,\n    devMode,\n    createActionInvoker,\n    addHiddenFinalProp,\n    addHiddenWritableProp,\n    IHooksGetter\n} from \"../../internal\"\n\n/** @hidden */\nexport interface IMapType<IT extends IAnyType>\n    extends IType<\n        IKeyValueMap<IT[\"CreationType\"]> | undefined,\n        IKeyValueMap<IT[\"SnapshotType\"]>,\n        IMSTMap<IT>\n    > {\n    hooks(hooks: IHooksGetter<IMSTMap<IT>>): IMapType<IT>\n}\n\n/** @hidden */\nexport interface IMSTMap<IT extends IAnyType> {\n    // bases on ObservableMap, but fine tuned to the auto snapshot conversion of MST\n\n    clear(): void\n    delete(key: string): boolean\n    forEach(\n        callbackfn: (value: IT[\"Type\"], key: string | number, map: this) => void,\n        thisArg?: any\n    ): void\n    get(key: string | number): IT[\"Type\"] | undefined\n    has(key: string | number): boolean\n    set(key: string | number, value: ExtractCSTWithSTN<IT>): this\n    readonly size: number\n    put(value: ExtractCSTWithSTN<IT>): IT[\"Type\"]\n    keys(): IterableIterator<string>\n    values(): IterableIterator<IT[\"Type\"]>\n    entries(): IterableIterator<[string, IT[\"Type\"]]>\n    [Symbol.iterator](): IterableIterator<[string, IT[\"Type\"]]>\n    /** Merge another object into this map, returns self. */\n    merge(\n        other:\n            | IMSTMap<IType<any, any, IT[\"TypeWithoutSTN\"]>>\n            | IKeyValueMap<ExtractCSTWithSTN<IT>>\n            | any\n    ): this\n    replace(\n        values:\n            | IMSTMap<IType<any, any, IT[\"TypeWithoutSTN\"]>>\n            | IKeyValueMap<ExtractCSTWithSTN<IT>>\n            | any\n    ): this\n\n    toJSON(): IKeyValueMap<IT[\"SnapshotType\"]>\n\n    toString(): string\n    [Symbol.toStringTag]: \"Map\"\n\n    /**\n     * Observes this object. Triggers for the events 'add', 'update' and 'delete'.\n     * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe\n     * for callback details\n     */\n    observe(\n        listener: (changes: IMapDidChange<string, IT[\"Type\"]>) => void,\n        fireImmediately?: boolean\n    ): Lambda\n    intercept(handler: IInterceptor<IMapWillChange<string, IT[\"Type\"]>>): Lambda\n}\n\nconst needsIdentifierError = `Map.put can only be used to store complex values that have an identifier type attribute`\n\nfunction tryCollectModelTypes(type: IAnyType, modelTypes: Array<IAnyModelType>): boolean {\n    const subtypes = type.getSubTypes()\n    if (subtypes === cannotDetermineSubtype) {\n        return false\n    }\n    if (subtypes) {\n        const subtypesArray = asArray(subtypes)\n        for (const subtype of subtypesArray) {\n            if (!tryCollectModelTypes(subtype, modelTypes)) return false\n        }\n    }\n    if (type instanceof ModelType) {\n        modelTypes.push(type)\n    }\n    return true\n}\n\n/**\n * @internal\n * @hidden\n */\nexport enum MapIdentifierMode {\n    UNKNOWN,\n    YES,\n    NO\n}\n\nclass MSTMap<IT extends IAnyType> extends ObservableMap<string, any> {\n    constructor(initialData?: IObservableMapInitialValues<string, any> | undefined, name?: string) {\n        super(initialData, (observable.ref as any).enhancer, name)\n    }\n\n    get(key: string): IT[\"Type\"] | undefined {\n        // maybe this is over-enthousiastic? normalize numeric keys to strings\n        return super.get(\"\" + key)\n    }\n\n    has(key: string) {\n        return super.has(\"\" + key)\n    }\n\n    delete(key: string) {\n        return super.delete(\"\" + key)\n    }\n\n    set(key: string, value: ExtractCSTWithSTN<IT>): this {\n        return super.set(\"\" + key, value)\n    }\n\n    put(value: ExtractCSTWithSTN<IT>): IT[\"Type\"] {\n        if (!value) throw new MstError(`Map.put cannot be used to set empty values`)\n        if (isStateTreeNode(value)) {\n            const node = getStateTreeNode(value)\n            if (devMode()) {\n                if (!node.identifierAttribute) {\n                    throw new MstError(needsIdentifierError)\n                }\n            }\n            if (node.identifier === null) {\n                throw new MstError(needsIdentifierError)\n            }\n            this.set(node.identifier, value)\n            return value as any\n        } else if (!isMutable(value)) {\n            throw new MstError(`Map.put can only be used to store complex values`)\n        } else {\n            const mapNode = getStateTreeNode(this as IAnyStateTreeNode)\n            const mapType = mapNode.type as MapType<any>\n\n            if (mapType.identifierMode !== MapIdentifierMode.YES) {\n                throw new MstError(needsIdentifierError)\n            }\n\n            const idAttr = mapType.mapIdentifierAttribute!\n            const id = (value as any)[idAttr]\n            if (!isValidIdentifier(id)) {\n                // try again but this time after creating a node for the value\n                // since it might be an optional identifier\n                const newNode = this.put(mapType.getChildType().create(value, mapNode.environment))\n                return this.put(getSnapshot(newNode))\n            }\n            const key = normalizeIdentifier(id)\n            this.set(key, value)\n            return this.get(key) as any\n        }\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class MapType<IT extends IAnyType> extends ComplexType<\n    IKeyValueMap<IT[\"CreationType\"]> | undefined,\n    IKeyValueMap<IT[\"SnapshotType\"]>,\n    IMSTMap<IT>\n> {\n    identifierMode: MapIdentifierMode = MapIdentifierMode.UNKNOWN\n    mapIdentifierAttribute: string | undefined = undefined\n    readonly flags = TypeFlags.Map\n\n    private readonly hookInitializers: Array<IHooksGetter<IMSTMap<IT>>> = []\n\n    constructor(\n        name: string,\n        private readonly _subType: IAnyType,\n        hookInitializers: Array<IHooksGetter<IMSTMap<IT>>> = []\n    ) {\n        super(name)\n        this._determineIdentifierMode()\n        this.hookInitializers = hookInitializers\n    }\n\n    hooks(hooks: IHooksGetter<IMSTMap<IT>>) {\n        const hookInitializers =\n            this.hookInitializers.length > 0 ? this.hookInitializers.concat(hooks) : [hooks]\n        return new MapType(this.name, this._subType, hookInitializers)\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        this._determineIdentifierMode()\n        return createObjectNode(this, parent, subpath, environment, initialValue)\n    }\n\n    private _determineIdentifierMode() {\n        if (this.identifierMode !== MapIdentifierMode.UNKNOWN) {\n            return\n        }\n\n        const modelTypes: IAnyModelType[] = []\n        if (tryCollectModelTypes(this._subType, modelTypes)) {\n            const identifierAttribute: string | undefined = modelTypes.reduce(\n                (current: IAnyModelType[\"identifierAttribute\"], type) => {\n                    if (!type.identifierAttribute) return current\n                    if (current && current !== type.identifierAttribute) {\n                        throw new MstError(\n                            `The objects in a map should all have the same identifier attribute, expected '${current}', but child of type '${type.name}' declared attribute '${type.identifierAttribute}' as identifier`\n                        )\n                    }\n                    return type.identifierAttribute\n                },\n                undefined as IAnyModelType[\"identifierAttribute\"]\n            )\n\n            if (identifierAttribute) {\n                this.identifierMode = MapIdentifierMode.YES\n                this.mapIdentifierAttribute = identifierAttribute\n            } else {\n                this.identifierMode = MapIdentifierMode.NO\n            }\n        }\n    }\n\n    initializeChildNodes(objNode: this[\"N\"], initialSnapshot: this[\"C\"] = {}): IChildNodesMap {\n        const subType = (objNode.type as this)._subType\n        const result: IChildNodesMap = {}\n        Object.keys(initialSnapshot!).forEach(name => {\n            result[name] = subType.instantiate(objNode, name, undefined, initialSnapshot[name])\n        })\n\n        return result\n    }\n\n    createNewInstance(childNodes: IChildNodesMap): this[\"T\"] {\n        return new MSTMap(childNodes, this.name) as any\n    }\n\n    finalizeNewInstance(node: this[\"N\"], instance: ObservableMap<string, any>): void {\n        _interceptReads(instance, node.unbox)\n\n        const type = node.type as this\n        type.hookInitializers.forEach(initializer => {\n            const hooks = initializer(instance as unknown as IMSTMap<IT>)\n            Object.keys(hooks).forEach(name => {\n                const hook = hooks[name as keyof typeof hooks]!\n                const actionInvoker = createActionInvoker(instance as IAnyStateTreeNode, name, hook)\n                ;(!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(\n                    instance,\n                    name,\n                    actionInvoker\n                )\n            })\n        })\n\n        intercept(instance, this.willChange)\n        observe(instance, this.didChange)\n    }\n\n    describe() {\n        return this.name\n    }\n\n    getChildren(node: this[\"N\"]): ReadonlyArray<AnyNode> {\n        // return (node.storedValue as ObservableMap<any>).values()\n        return values(node.storedValue)\n    }\n\n    getChildNode(node: this[\"N\"], key: string): AnyNode {\n        const childNode = node.storedValue.get(\"\" + key)\n        if (!childNode) throw new MstError(\"Not a child \" + key)\n        return childNode\n    }\n\n    willChange(change: IMapWillChange<string, AnyNode>): IMapWillChange<string, AnyNode> | null {\n        const node = getStateTreeNode(change.object as IAnyStateTreeNode)\n        const key = change.name\n        node.assertWritable({ subpath: key })\n        const mapType = node.type as this\n        const subType = mapType._subType\n\n        switch (change.type) {\n            case \"update\":\n                {\n                    const { newValue } = change\n                    const oldValue = change.object.get(key)\n                    if (newValue === oldValue) return null\n                    typecheckInternal(subType, newValue)\n                    change.newValue = subType.reconcile(\n                        node.getChildNode(key),\n                        change.newValue,\n                        node,\n                        key\n                    )\n                    mapType.processIdentifier(key, change.newValue)\n                }\n                break\n            case \"add\":\n                {\n                    typecheckInternal(subType, change.newValue)\n                    change.newValue = subType.instantiate(node, key, undefined, change.newValue)\n                    mapType.processIdentifier(key, change.newValue)\n                }\n                break\n        }\n        return change\n    }\n\n    private processIdentifier(expected: string, node: AnyNode): void {\n        if (this.identifierMode === MapIdentifierMode.YES && node instanceof ObjectNode) {\n            const identifier = node.identifier!\n            if (identifier !== expected)\n                throw new MstError(\n                    `A map of objects containing an identifier should always store the object under their own identifier. Trying to store key '${identifier}', but expected: '${expected}'`\n                )\n        }\n    }\n\n    getSnapshot(node: this[\"N\"]): this[\"S\"] {\n        const res: any = {}\n        node.getChildren().forEach(childNode => {\n            res[childNode.subpath] = childNode.snapshot\n        })\n        return res\n    }\n\n    processInitialSnapshot(childNodes: IChildNodesMap): this[\"S\"] {\n        const processed: any = {}\n        Object.keys(childNodes).forEach(key => {\n            processed[key] = childNodes[key].getSnapshot()\n        })\n        return processed\n    }\n\n    didChange(change: IMapDidChange<string, AnyNode>): void {\n        const node = getStateTreeNode(change.object as IAnyStateTreeNode)\n        switch (change.type) {\n            case \"update\":\n                return void node.emitPatch(\n                    {\n                        op: \"replace\",\n                        path: escapeJsonPath(change.name),\n                        value: change.newValue.snapshot,\n                        oldValue: change.oldValue ? change.oldValue.snapshot : undefined\n                    },\n                    node\n                )\n            case \"add\":\n                return void node.emitPatch(\n                    {\n                        op: \"add\",\n                        path: escapeJsonPath(change.name),\n                        value: change.newValue.snapshot,\n                        oldValue: undefined\n                    },\n                    node\n                )\n            case \"delete\":\n                // a node got deleted, get the old snapshot and make the node die\n                const oldSnapshot = change.oldValue.snapshot\n                change.oldValue.die()\n                // emit the patch\n                return void node.emitPatch(\n                    {\n                        op: \"remove\",\n                        path: escapeJsonPath(change.name),\n                        oldValue: oldSnapshot\n                    },\n                    node\n                )\n        }\n    }\n\n    applyPatchLocally(node: this[\"N\"], subpath: string, patch: IJsonPatch): void {\n        const target = node.storedValue\n        switch (patch.op) {\n            case \"add\":\n            case \"replace\":\n                target.set(subpath, patch.value)\n                break\n            case \"remove\":\n                target.delete(subpath)\n                break\n        }\n    }\n\n    applySnapshot(node: this[\"N\"], snapshot: this[\"C\"]): void {\n        typecheckInternal(this, snapshot)\n        const target = node.storedValue\n        const currentKeys: { [key: string]: boolean } = {}\n        Array.from(target.keys()).forEach(key => {\n            currentKeys[key] = false\n        })\n        if (snapshot) {\n            // Don't use target.replace, as it will throw away all existing items first\n            for (let key in snapshot) {\n                target.set(key, snapshot[key])\n                currentKeys[\"\" + key] = true\n            }\n        }\n        Object.keys(currentKeys).forEach(key => {\n            if (currentKeys[key] === false) target.delete(key)\n        })\n    }\n\n    getChildType(): IAnyType {\n        return this._subType\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (!isPlainObject(value)) {\n            return typeCheckFailure(context, value, \"Value is not a plain object\")\n        }\n\n        return flattenTypeErrors(\n            Object.keys(value).map(path =>\n                this._subType.validate(value[path], getContextForPath(context, path, this._subType))\n            )\n        )\n    }\n\n    getDefaultSnapshot(): this[\"C\"] {\n        return EMPTY_OBJECT as this[\"C\"]\n    }\n\n    removeChild(node: this[\"N\"], subpath: string) {\n        node.storedValue.delete(subpath)\n    }\n}\nMapType.prototype.applySnapshot = action(MapType.prototype.applySnapshot)\n\n/**\n * `types.map` - Creates a key based collection type who's children are all of a uniform declared type.\n * If the type stored in a map has an identifier, it is mandatory to store the child under that identifier in the map.\n *\n * This type will always produce [observable maps](https://mobx.js.org/api.html#observablemap)\n *\n * Example:\n * ```ts\n * const Todo = types.model({\n *   id: types.identifier,\n *   task: types.string\n * })\n *\n * const TodoStore = types.model({\n *   todos: types.map(Todo)\n * })\n *\n * const s = TodoStore.create({ todos: {} })\n * unprotect(s)\n * s.todos.set(17, { task: \"Grab coffee\", id: 17 })\n * s.todos.put({ task: \"Grab cookie\", id: 18 }) // put will infer key from the identifier\n * console.log(s.todos.get(17).task) // prints: \"Grab coffee\"\n * ```\n *\n * @param subtype\n * @returns\n */\nexport function map<IT extends IAnyType>(subtype: IT): IMapType<IT> {\n    return new MapType<IT>(`Map<string, ${subtype.name}>`, subtype)\n}\n\n/**\n * Returns if a given value represents a map type.\n *\n * @param type\n * @returns `true` if it is a map type.\n */\nexport function isMapType(type: unknown): type is IMapType<IAnyType> {\n    return isType(type) && (type.flags & TypeFlags.Map) > 0\n}\n"
  },
  {
    "path": "src/types/complex-types/model.ts",
    "content": "import {\n    IObjectDidChange,\n    IObjectWillChange,\n    _getAdministration,\n    _interceptReads,\n    action,\n    computed,\n    defineProperty,\n    getAtom,\n    intercept,\n    makeObservable,\n    observable,\n    observe,\n    set\n} from \"mobx\"\nimport {\n    AnyNode,\n    AnyObjectNode,\n    ArrayType,\n    ComplexType,\n    EMPTY_ARRAY,\n    EMPTY_OBJECT,\n    FunctionWithFlag,\n    Hook,\n    IAnyType,\n    IChildNodesMap,\n    IJsonPatch,\n    IType,\n    IValidationContext,\n    IValidationResult,\n    Instance,\n    MapType,\n    MstError,\n    TypeFlags,\n    _CustomOrOther,\n    _NotCustomized,\n    addHiddenFinalProp,\n    addHiddenWritableProp,\n    assertArg,\n    assertIsString,\n    createActionInvoker,\n    createObjectNode,\n    devMode,\n    escapeJsonPath,\n    flattenTypeErrors,\n    freeze,\n    getContextForPath,\n    getPrimitiveFactoryFromValue,\n    getStateTreeNode,\n    isPlainObject,\n    isPrimitive,\n    isStateTreeNode,\n    isType,\n    mobxShallow,\n    optional,\n    typeCheckFailure,\n    typecheckInternal\n} from \"../../internal\"\n\nconst PRE_PROCESS_SNAPSHOT = \"preProcessSnapshot\"\nconst POST_PROCESS_SNAPSHOT = \"postProcessSnapshot\"\n\n/** @hidden */\nexport interface ModelProperties {\n    [key: string]: IAnyType\n}\n\n/** @hidden */\nexport type ModelPrimitive = string | number | boolean | Date | bigint\n\n/** @hidden */\nexport interface ModelPropertiesDeclaration {\n    [key: string]: ModelPrimitive | IAnyType\n}\n\n/**\n * Unmaps syntax property declarations to a map of { propName: IType }\n *\n * @hidden\n */\nexport type ModelPropertiesDeclarationToProperties<T extends ModelPropertiesDeclaration> =\n    T extends { [k: string]: IAnyType } // optimization to reduce nesting\n        ? T\n        : {\n              [K in keyof T]: T[K] extends IAnyType // keep IAnyType check on the top to reduce nesting\n                  ? T[K]\n                  : T[K] extends string\n                    ? IType<string | undefined, string, string>\n                    : T[K] extends number\n                      ? IType<number | undefined, number, number>\n                      : T[K] extends boolean\n                        ? IType<boolean | undefined, boolean, boolean>\n                        : T[K] extends Date\n                          ? IType<number | Date | undefined, number, Date>\n                          : never\n          }\n\n/**\n * Checks if a value is optional (undefined, any or unknown).\n * @hidden\n *\n * Examples:\n * - string = false\n * - undefined = true\n * - string | undefined = true\n * - string & undefined = false, but we don't care\n * - any = true\n * - unknown = true\n */\ntype IsOptionalValue<C, TV, FV> = undefined extends C ? TV : FV\n\n// type _A = IsOptionalValue<string, true, false> // false\n// type _B = IsOptionalValue<undefined, true, false> // true\n// type _C = IsOptionalValue<string | undefined, true, false> // true\n// type _D = IsOptionalValue<string & undefined, true, false> // false, but we don't care\n// type _E = IsOptionalValue<any, true, false> // true\n// type _F = IsOptionalValue<unknown, true, false> // true\n\ntype AnyObject = Record<string, any>\n\n/**\n * Name of the properties of an object that can't be set to undefined, any or unknown\n * @hidden\n */\ntype DefinablePropsNames<T> = { [K in keyof T]: IsOptionalValue<T[K], never, K> }[keyof T]\n\n/** @hidden */\nexport type ExtractCFromProps<P extends ModelProperties> = MaybeEmpty<{\n    [k in keyof P]: P[k][\"CreationType\"] | P[k][\"TypeWithoutSTN\"]\n}>\n\n/** @hidden */\nexport type MaybeEmpty<T> = keyof T extends never ? EmptyObject : T\n\n/** @hidden */\nexport type ModelCreationType<PC> = MaybeEmpty<{ [P in DefinablePropsNames<PC>]: PC[P] }> &\n    Partial<PC>\n\n// Ensure an object can be given additional properties.\n//\n// For the empty object type, `Record<string, never>`, we need to convert into a record type that will allow us to\n// assign new values. Any other type should allow additional properties by default.\ntype WithAdditionalProperties<T> = T extends Record<string, never> ? EmptyObject : T\n\ndeclare const $nonEmptyObject: unique symbol\ntype EmptyObject = { [$nonEmptyObject]?: never }\n\n/** @hidden */\nexport type ModelCreationType2<P extends ModelProperties, CustomC> = MaybeEmpty<\n    keyof P extends never\n        ? _CustomOrOther<CustomC, ModelCreationType<EmptyObject>>\n        : _CustomOrOther<CustomC, ModelCreationType<ExtractCFromProps<P>>>\n>\n\n/** @hidden */\nexport type ModelSnapshotType<P extends ModelProperties> = {\n    [K in keyof P]: P[K][\"SnapshotType\"]\n}\n\n/** @hidden */\nexport type ModelSnapshotType2<P extends ModelProperties, CustomS> = _CustomOrOther<\n    CustomS,\n    ModelSnapshotType<P>\n>\n\n/**\n * @hidden\n * we keep this separate from ModelInstanceType to shorten model instance types generated declarations\n */\nexport type ModelInstanceTypeProps<P extends ModelProperties> = {\n    [K in keyof P]: P[K][\"Type\"]\n}\n\n/**\n * @hidden\n * do not transform this to an interface or model instance type generated declarations will be longer\n */\nexport type ModelInstanceType<P extends ModelProperties, O> = ModelInstanceTypeProps<P> & O\n\n/** @hidden */\nexport interface ModelActions {\n    [key: string]: FunctionWithFlag\n}\n\nexport interface IModelType<\n    PROPS extends ModelProperties,\n    OTHERS,\n    CustomC = _NotCustomized,\n    CustomS = _NotCustomized\n> extends IType<\n        ModelCreationType2<PROPS, CustomC>,\n        ModelSnapshotType2<PROPS, CustomS>,\n        ModelInstanceType<PROPS, OTHERS>\n    > {\n    readonly properties: PROPS\n\n    named(newName: string): IModelType<PROPS, OTHERS, CustomC, CustomS>\n\n    // warning: redefining props after a process snapshot is used ends up on the fixed (custom) C, S typings being overridden\n    // so it is recommended to use pre/post process snapshot after all props have been defined\n    props<PROPS2 extends ModelPropertiesDeclaration>(\n        props: PROPS2\n    ): IModelType<PROPS & ModelPropertiesDeclarationToProperties<PROPS2>, OTHERS, CustomC, CustomS>\n\n    views<V extends Object>(\n        fn: (self: Instance<this>) => V\n    ): IModelType<PROPS, OTHERS & V, CustomC, CustomS>\n\n    actions<A extends ModelActions>(\n        fn: (self: Instance<this>) => A\n    ): IModelType<PROPS, OTHERS & A, CustomC, CustomS>\n\n    volatile<TP extends object>(\n        fn: (self: Instance<this>) => TP\n    ): IModelType<PROPS, OTHERS & TP, CustomC, CustomS>\n\n    extend<A extends ModelActions = {}, V extends Object = {}, VS extends Object = {}>(\n        fn: (self: Instance<this>) => { actions?: A; views?: V; state?: VS }\n    ): IModelType<PROPS, OTHERS & A & V & VS, CustomC, CustomS>\n\n    preProcessSnapshot<NewC = ModelCreationType2<PROPS, CustomC>>(\n        fn: (snapshot: NewC) => WithAdditionalProperties<ModelCreationType2<PROPS, CustomC>>\n    ): IModelType<PROPS, OTHERS, NewC, CustomS>\n\n    postProcessSnapshot<NewS = ModelSnapshotType2<PROPS, CustomS>>(\n        fn: (snapshot: ModelSnapshotType2<PROPS, CustomS>) => NewS\n    ): IModelType<PROPS, OTHERS, CustomC, NewS>\n}\n\n/**\n * Any model type.\n */\nexport interface IAnyModelType extends IModelType<any, any, any, any> {}\n\n/** @hidden */\nexport type ExtractProps<T extends IAnyModelType> =\n    T extends IModelType<infer P, any, any, any> ? P : never\n\n/** @hidden */\nexport type ExtractOthers<T extends IAnyModelType> =\n    T extends IModelType<any, infer O, any, any> ? O : never\n\nfunction objectTypeToString(this: any) {\n    return getStateTreeNode(this).toString()\n}\n\n/**\n * @internal\n * @hidden\n */\nexport interface ModelTypeConfig {\n    name?: string\n    properties?: ModelPropertiesDeclaration\n    initializers?: ReadonlyArray<(instance: any) => any>\n    preProcessor?: (snapshot: any) => any\n    postProcessor?: (snapshot: any) => any\n}\n\nconst defaultObjectOptions = {\n    name: \"AnonymousModel\",\n    properties: {},\n    initializers: EMPTY_ARRAY\n}\n\nfunction toPropertiesObject(declaredProps: ModelPropertiesDeclaration): ModelProperties {\n    const keysList = Object.keys(declaredProps)\n    const alreadySeenKeys = new Set<string>()\n\n    keysList.forEach(key => {\n        if (alreadySeenKeys.has(key)) {\n            throw new MstError(\n                `${key} is declared twice in the model. Model should not contain the same keys`\n            )\n        }\n        alreadySeenKeys.add(key)\n    })\n\n    // loop through properties and ensures that all items are types\n    return keysList.reduce(\n        (props, key) => {\n            // warn if user intended a HOOK\n            if (key in Hook) {\n                throw new MstError(\n                    `Hook '${key}' was defined as property. Hooks should be defined as part of the actions`\n                )\n            }\n\n            // the user intended to use a view\n            const descriptor = Object.getOwnPropertyDescriptor(declaredProps, key)!\n            if (\"get\" in descriptor) {\n                throw new MstError(\n                    \"Getters are not supported as properties. Please use views instead\"\n                )\n            }\n            // undefined and null are not valid\n            const value = descriptor.value\n            if (value === null || value === undefined) {\n                throw new MstError(\n                    \"The default value of an attribute cannot be null or undefined as the type cannot be inferred. Did you mean `types.maybe(someType)`?\"\n                )\n            }\n            // its a primitive, convert to its type\n            else if (isPrimitive(value)) {\n                props[key] = optional(getPrimitiveFactoryFromValue(value), value)\n            }\n            // map defaults to empty object automatically for models\n            else if (value instanceof MapType) {\n                props[key] = optional(value, {})\n            } else if (value instanceof ArrayType) {\n                props[key] = optional(value, [])\n            }\n            // its already a type\n            else if (isType(value)) {\n                // do nothing, it's already a type\n            }\n            // its a function, maybe the user wanted a view?\n            else if (devMode() && typeof value === \"function\") {\n                throw new MstError(\n                    `Invalid type definition for property '${key}', it looks like you passed a function. Did you forget to invoke it, or did you intend to declare a view / action?`\n                )\n            }\n            // no other complex values\n            else if (devMode() && typeof value === \"object\") {\n                throw new MstError(\n                    `Invalid type definition for property '${key}', it looks like you passed an object. Try passing another model type or a types.frozen.`\n                )\n            } else {\n                throw new MstError(\n                    `Invalid type definition for property '${key}', cannot infer a type from a value like '${value}' (${typeof value})`\n                )\n            }\n\n            return props\n        },\n        { ...declaredProps } as any\n    )\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class ModelType<\n        PROPS extends ModelProperties,\n        OTHERS,\n        CustomC,\n        CustomS,\n        MT extends IModelType<PROPS, OTHERS, CustomC, CustomS>\n    >\n    extends ComplexType<\n        ModelCreationType2<PROPS, CustomC>,\n        ModelSnapshotType2<PROPS, CustomS>,\n        ModelInstanceType<PROPS, OTHERS>\n    >\n    implements IModelType<PROPS, OTHERS, CustomC, CustomS>\n{\n    readonly flags = TypeFlags.Object\n\n    /*\n     * The original object definition\n     */\n    public readonly initializers!: ((instance: any) => any)[]\n    public readonly properties!: PROPS\n\n    private preProcessor!: (snapshot: any) => any | undefined\n    private postProcessor!: (snapshot: any) => any | undefined\n    private readonly propertyNames: string[]\n\n    constructor(opts: ModelTypeConfig) {\n        super(opts.name || defaultObjectOptions.name)\n        Object.assign(this, defaultObjectOptions, opts)\n        // ensures that any default value gets converted to its related type\n        this.properties = toPropertiesObject(this.properties) as PROPS\n        freeze(this.properties) // make sure nobody messes with it\n        this.propertyNames = Object.keys(this.properties)\n        this.identifierAttribute = this._getIdentifierAttribute()\n    }\n\n    private _getIdentifierAttribute(): string | undefined {\n        let identifierAttribute: string | undefined = undefined\n        this.forAllProps((propName, propType) => {\n            if (propType.flags & TypeFlags.Identifier) {\n                if (identifierAttribute)\n                    throw new MstError(\n                        `Cannot define property '${propName}' as object identifier, property '${identifierAttribute}' is already defined as identifier property`\n                    )\n                identifierAttribute = propName\n            }\n        })\n        return identifierAttribute\n    }\n\n    cloneAndEnhance(opts: ModelTypeConfig): IAnyModelType {\n        return new ModelType({\n            name: opts.name || this.name,\n            properties: Object.assign({}, this.properties, opts.properties),\n            initializers: this.initializers.concat(opts.initializers || []),\n            preProcessor: opts.preProcessor || this.preProcessor,\n            postProcessor: opts.postProcessor || this.postProcessor\n        })\n    }\n\n    actions<A extends ModelActions>(fn: (self: Instance<this>) => A) {\n        const actionInitializer = (self: Instance<this>) => {\n            this.instantiateActions(self, fn(self))\n            return self\n        }\n        return this.cloneAndEnhance({ initializers: [actionInitializer] })\n    }\n\n    private instantiateActions(self: this[\"T\"], actions: ModelActions): void {\n        // check if return is correct\n        if (!isPlainObject(actions)) {\n            throw new MstError(\n                `actions initializer should return a plain object containing actions`\n            )\n        }\n\n        // bind actions to the object created\n        Object.getOwnPropertyNames(actions).forEach(name => {\n            if (name in this.properties) {\n                throw new MstError(`'${name}' is a property and cannot be declared as an action`)\n            }\n\n            // warn if preprocessor was given\n            if (name === PRE_PROCESS_SNAPSHOT)\n                throw new MstError(\n                    `Cannot define action '${PRE_PROCESS_SNAPSHOT}', it should be defined using 'type.preProcessSnapshot(fn)' instead`\n                )\n            // warn if postprocessor was given\n            if (name === POST_PROCESS_SNAPSHOT)\n                throw new MstError(\n                    `Cannot define action '${POST_PROCESS_SNAPSHOT}', it should be defined using 'type.postProcessSnapshot(fn)' instead`\n                )\n\n            let action2 = actions[name]\n\n            // apply hook composition\n            let baseAction = (self as any)[name]\n            if (name in Hook && baseAction) {\n                let specializedAction = action2\n                action2 = function () {\n                    baseAction.apply(null, arguments)\n                    specializedAction.apply(null, arguments)\n                }\n            }\n\n            // the goal of this is to make sure actions using \"this\" can call themselves,\n            // while still allowing the middlewares to register them\n            const middlewares = (action2 as any).$mst_middleware // make sure middlewares are not lost\n            let boundAction = action2.bind(actions)\n            boundAction._isFlowAction = (action2 as FunctionWithFlag)._isFlowAction || false\n            boundAction.$mst_middleware = middlewares\n            const actionInvoker = createActionInvoker(self as any, name, boundAction)\n            actions[name] = actionInvoker\n\n            // See #646, allow models to be mocked\n            ;(!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(self, name, actionInvoker)\n        })\n    }\n\n    named: MT[\"named\"] = name => {\n        return this.cloneAndEnhance({ name })\n    }\n\n    props: MT[\"props\"] = properties => {\n        return this.cloneAndEnhance({ properties })\n    }\n\n    volatile<TP extends object>(fn: (self: Instance<this>) => TP) {\n        if (typeof fn !== \"function\") {\n            throw new MstError(\n                `You passed an ${typeof fn} to volatile state as an argument, when function is expected`\n            )\n        }\n        const stateInitializer = (self: Instance<this>) => {\n            this.instantiateVolatileState(self, fn(self))\n            return self\n        }\n        return this.cloneAndEnhance({ initializers: [stateInitializer] })\n    }\n\n    private instantiateVolatileState(\n        self: this[\"T\"],\n        state: {\n            [key: string]: any\n        }\n    ): void {\n        // check views return\n        if (!isPlainObject(state)) {\n            throw new MstError(\n                `volatile state initializer should return a plain object containing state`\n            )\n        }\n\n        Object.getOwnPropertyNames(state).forEach(name => {\n            if (name in this.properties) {\n                throw new MstError(\n                    `'${name}' is a property and cannot be declared as volatile state`\n                )\n            }\n\n            set(self, name, state[name])\n        })\n    }\n\n    extend<A extends ModelActions = {}, V extends Object = {}, VS extends Object = {}>(\n        fn: (self: Instance<this>) => { actions?: A; views?: V; state?: VS }\n    ) {\n        const initializer = (self: Instance<this>) => {\n            const { actions, views, state, ...rest } = fn(self)\n            for (let key in rest)\n                throw new MstError(\n                    `The \\`extend\\` function should return an object with a subset of the fields 'actions', 'views' and 'state'. Found invalid key '${key}'`\n                )\n            if (state) this.instantiateVolatileState(self, state)\n            if (views) this.instantiateViews(self, views)\n            if (actions) this.instantiateActions(self, actions)\n            return self\n        }\n        return this.cloneAndEnhance({ initializers: [initializer] })\n    }\n\n    views<V extends Object>(fn: (self: Instance<this>) => V) {\n        const viewInitializer = (self: Instance<this>) => {\n            this.instantiateViews(self, fn(self))\n            return self\n        }\n        return this.cloneAndEnhance({ initializers: [viewInitializer] })\n    }\n\n    private instantiateViews(self: this[\"T\"], views: Object): void {\n        // check views return\n        if (!isPlainObject(views)) {\n            throw new MstError(`views initializer should return a plain object containing views`)\n        }\n\n        Object.getOwnPropertyNames(views).forEach(name => {\n            if (name in this.properties) {\n                throw new MstError(`'${name}' is a property and cannot be declared as a view`)\n            }\n\n            // is this a computed property?\n            const descriptor = Object.getOwnPropertyDescriptor(views, name)!\n            if (\"get\" in descriptor) {\n                defineProperty(self, name, descriptor)\n                makeObservable(self, { [name]: computed } as any)\n            } else if (typeof descriptor.value === \"function\") {\n                // this is a view function, merge as is!\n                // See #646, allow models to be mocked\n                ;(!devMode() ? addHiddenFinalProp : addHiddenWritableProp)(\n                    self,\n                    name,\n                    descriptor.value\n                )\n            } else {\n                throw new MstError(\n                    `A view member should either be a function or getter based property`\n                )\n            }\n        })\n    }\n\n    preProcessSnapshot: MT[\"preProcessSnapshot\"] = preProcessor => {\n        const currentPreprocessor = this.preProcessor\n        if (!currentPreprocessor) return this.cloneAndEnhance({ preProcessor })\n        else\n            return this.cloneAndEnhance({\n                preProcessor: snapshot => currentPreprocessor(preProcessor(snapshot))\n            })\n    }\n\n    postProcessSnapshot: MT[\"postProcessSnapshot\"] = postProcessor => {\n        const currentPostprocessor = this.postProcessor\n        if (!currentPostprocessor) return this.cloneAndEnhance({ postProcessor })\n        else\n            return this.cloneAndEnhance({\n                postProcessor: snapshot => postProcessor(currentPostprocessor(snapshot))\n            })\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        const value = isStateTreeNode(initialValue)\n            ? initialValue\n            : this.applySnapshotPreProcessor(initialValue)\n        return createObjectNode(this, parent, subpath, environment, value)\n        // Optimization: record all prop- view- and action names after first construction, and generate an optimal base class\n        // that pre-reserves all these fields for fast object-member lookups\n    }\n\n    initializeChildNodes(objNode: this[\"N\"], initialSnapshot: any = {}): IChildNodesMap {\n        const type = objNode.type as this\n        const result: IChildNodesMap = {}\n        type.forAllProps((name, childType) => {\n            result[name] = childType.instantiate(\n                objNode,\n                name,\n                undefined,\n                (initialSnapshot as any)[name]\n            )\n        })\n        return result\n    }\n\n    createNewInstance(childNodes: IChildNodesMap): this[\"T\"] {\n        const options = { ...mobxShallow, name: this.name }\n        return observable.object(childNodes, EMPTY_OBJECT, options) as any\n    }\n\n    finalizeNewInstance(node: this[\"N\"], instance: this[\"T\"]): void {\n        addHiddenFinalProp(instance, \"toString\", objectTypeToString)\n\n        this.forAllProps(name => {\n            _interceptReads(instance, name, node.unbox)\n        })\n\n        this.initializers.reduce((self, fn) => fn(self), instance)\n        intercept(instance, this.willChange)\n        observe(instance, this.didChange)\n    }\n\n    private willChange(chg: IObjectWillChange): IObjectWillChange | null {\n        // TODO: mobx typings don't seem to take into account that newValue can be set even when removing a prop\n        const change = chg as IObjectWillChange & { newValue?: any }\n\n        const node = getStateTreeNode(change.object)\n        const subpath = change.name as string\n        node.assertWritable({ subpath })\n        const childType = (node.type as this).properties[subpath]\n        // only properties are typed, state are stored as-is references\n        if (childType) {\n            typecheckInternal(childType, change.newValue)\n            change.newValue = childType.reconcile(\n                node.getChildNode(subpath),\n                change.newValue,\n                node,\n                subpath\n            )\n        }\n        return change\n    }\n\n    private didChange(chg: IObjectDidChange) {\n        // TODO: mobx typings don't seem to take into account that newValue can be set even when removing a prop\n        const change = chg as IObjectWillChange & { newValue?: any; oldValue?: any }\n\n        const childNode = getStateTreeNode(change.object)\n        const childType = (childNode.type as this).properties[change.name as string]\n        if (!childType) {\n            // don't emit patches for volatile state\n            return\n        }\n        const oldChildValue = change.oldValue ? change.oldValue.snapshot : undefined\n        childNode.emitPatch(\n            {\n                op: \"replace\",\n                path: escapeJsonPath(change.name as string),\n                value: change.newValue.snapshot,\n                oldValue: oldChildValue\n            },\n            childNode\n        )\n    }\n\n    getChildren(node: this[\"N\"]): ReadonlyArray<AnyNode> {\n        const res: AnyNode[] = []\n        this.forAllProps(name => {\n            res.push(this.getChildNode(node, name))\n        })\n        return res\n    }\n\n    getChildNode(node: this[\"N\"], key: string): AnyNode {\n        if (!(key in this.properties)) throw new MstError(\"Not a value property: \" + key)\n        const adm = _getAdministration(node.storedValue, key)\n        const childNode = adm.raw?.()\n        if (!childNode) throw new MstError(\"Node not available for property \" + key)\n        return childNode\n    }\n\n    getSnapshot(node: this[\"N\"], applyPostProcess = true): this[\"S\"] {\n        const res = {} as any\n        this.forAllProps((name, type) => {\n            // TODO: FIXME, make sure the observable ref is used!\n            const atom = getAtom(node.storedValue, name)\n            ;(atom as any).reportObserved()\n\n            res[name] = this.getChildNode(node, name).snapshot\n        })\n        if (applyPostProcess) {\n            return this.applySnapshotPostProcessor(res)\n        }\n        return res\n    }\n\n    processInitialSnapshot(childNodes: IChildNodesMap): this[\"S\"] {\n        const processed = {} as any\n        Object.keys(childNodes).forEach(key => {\n            processed[key] = childNodes[key].getSnapshot()\n        })\n        return this.applySnapshotPostProcessor(processed)\n    }\n\n    applyPatchLocally(node: this[\"N\"], subpath: string, patch: IJsonPatch): void {\n        if (!(patch.op === \"replace\" || patch.op === \"add\")) {\n            throw new MstError(`object does not support operation ${patch.op}`)\n        }\n        ;(node.storedValue as any)[subpath] = patch.value\n    }\n\n    applySnapshot(node: this[\"N\"], snapshot: this[\"C\"]): void {\n        typecheckInternal(this, snapshot)\n        const preProcessedSnapshot = this.applySnapshotPreProcessor(snapshot)\n        this.forAllProps(name => {\n            ;(node.storedValue as any)[name] = preProcessedSnapshot[name]\n        })\n    }\n\n    applySnapshotPreProcessor(snapshot: any) {\n        const processor = this.preProcessor\n        return processor ? processor.call(null, snapshot) : snapshot\n    }\n\n    applySnapshotPostProcessor(snapshot: any) {\n        const postProcessor = this.postProcessor\n        if (postProcessor) return postProcessor.call(null, snapshot)\n        return snapshot\n    }\n\n    getChildType(propertyName: string): IAnyType {\n        assertIsString(propertyName, 1)\n\n        return this.properties[propertyName]\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        let snapshot = this.applySnapshotPreProcessor(value)\n\n        if (!isPlainObject(snapshot)) {\n            return typeCheckFailure(context, snapshot, \"Value is not a plain object\")\n        }\n\n        return flattenTypeErrors(\n            this.propertyNames.map(key =>\n                this.properties[key].validate(\n                    snapshot[key],\n                    getContextForPath(context, key, this.properties[key])\n                )\n            )\n        )\n    }\n\n    private forAllProps(fn: (name: string, type: IAnyType) => void) {\n        this.propertyNames.forEach(key => fn(key, this.properties[key]))\n    }\n\n    describe() {\n        // optimization: cache\n        return (\n            \"{ \" +\n            this.propertyNames.map(key => key + \": \" + this.properties[key].describe()).join(\"; \") +\n            \" }\"\n        )\n    }\n\n    getDefaultSnapshot(): this[\"C\"] {\n        return EMPTY_OBJECT as this[\"C\"]\n    }\n\n    removeChild(node: this[\"N\"], subpath: string) {\n        ;(node.storedValue as any)[subpath] = undefined\n    }\n}\nModelType.prototype.applySnapshot = action(ModelType.prototype.applySnapshot)\n\nexport function model<P extends ModelPropertiesDeclaration = {}>(\n    name: string,\n    properties?: P\n): IModelType<ModelPropertiesDeclarationToProperties<P>, {}>\nexport function model<P extends ModelPropertiesDeclaration = {}>(\n    properties?: P\n): IModelType<ModelPropertiesDeclarationToProperties<P>, {}>\n/**\n * `types.model` - Creates a new model type by providing a name, properties, volatile state and actions.\n *\n * See the [model type](/concepts/trees#creating-models) description or the [getting started](intro/getting-started.md#getting-started-1) tutorial.\n */\nexport function model(...args: any[]): any {\n    if (devMode() && typeof args[0] !== \"string\" && args[1]) {\n        throw new MstError(\n            \"Model creation failed. First argument must be a string when two arguments are provided\"\n        )\n    }\n\n    const name = typeof args[0] === \"string\" ? args.shift() : \"AnonymousModel\"\n    const properties = args.shift() || {}\n    return new ModelType({ name, properties })\n}\n\n// TODO: this can be simplified in TS3, since we can transform _NotCustomized to unknown, since unkonwn & X = X\n// and then back unknown to _NotCustomized if needed\n/** @hidden */\nexport type _CustomJoin<A, B> = A extends _NotCustomized ? B : A & B\n\n// generated with C:\\VSProjects\\github\\mobx-state-tree-upstream\\packages\\mobx-state-tree\\scripts\\generate-compose-type.js\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>): IModelType<PA & PB, OA & OB, _CustomJoin<FCA, FCB>, _CustomJoin<FSA, FSB>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>): IModelType<PA & PB, OA & OB, _CustomJoin<FCA, FCB>, _CustomJoin<FSA, FSB>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>): IModelType<PA & PB & PC, OA & OB & OC, _CustomJoin<FCA, _CustomJoin<FCB, FCC>>, _CustomJoin<FSA, _CustomJoin<FSB, FSC>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>): IModelType<PA & PB & PC, OA & OB & OC, _CustomJoin<FCA, _CustomJoin<FCB, FCC>>, _CustomJoin<FSA, _CustomJoin<FSB, FSC>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>): IModelType<PA & PB & PC & PD, OA & OB & OC & OD, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, FCD>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, FSD>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>): IModelType<PA & PB & PC & PD, OA & OB & OC & OD, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, FCD>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, FSD>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>): IModelType<PA & PB & PC & PD & PE, OA & OB & OC & OD & OE, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, FCE>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, FSE>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE>(A:\n  IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>): IModelType<PA & PB & PC & PD & PE, OA & OB & OC & OD & OE, _CustomJoin<FCA,\n    _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, FCE>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, FSE>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>): IModelType<PA & PB & PC & PD & PE & PF, OA & OB & OC & OD & OE & OF, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, FCF>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, FSF>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>): IModelType<PA & PB & PC & PD & PE & PF, OA & OB & OC & OD & OE & OF, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, FCF>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, FSF>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF, PG extends ModelProperties, OG, FCG, FSG>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>, G: IModelType<PG, OG, FCG, FSG>): IModelType<PA & PB & PC & PD & PE & PF & PG, OA & OB & OC & OD & OE & OF & OG, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, _CustomJoin<FCF, FCG>>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, _CustomJoin<FSF, FSG>>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF, PG extends ModelProperties, OG, FCG, FSG>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>, G: IModelType<PG, OG, FCG, FSG>): IModelType<PA & PB & PC & PD & PE & PF & PG, OA & OB & OC & OD & OE & OF & OG, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, _CustomJoin<FCF, FCG>>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, _CustomJoin<FSF, FSG>>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF, PG extends ModelProperties, OG, FCG, FSG, PH extends ModelProperties, OH, FCH, FSH>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>, G: IModelType<PG, OG, FCG, FSG>, H: IModelType<PH, OH, FCH, FSH>): IModelType<PA & PB & PC & PD & PE & PF & PG & PH, OA & OB & OC & OD & OE & OF & OG & OH, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, _CustomJoin<FCF, _CustomJoin<FCG, FCH>>>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, _CustomJoin<FSF, _CustomJoin<FSG, FSH>>>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF, PG extends ModelProperties, OG, FCG, FSG, PH extends ModelProperties, OH, FCH, FSH>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>, G: IModelType<PG, OG, FCG, FSG>, H: IModelType<PH, OH, FCH, FSH>): IModelType<PA & PB & PC & PD & PE & PF & PG & PH, OA & OB & OC & OD & OE & OF & OG & OH, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, _CustomJoin<FCF, _CustomJoin<FCG, FCH>>>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, _CustomJoin<FSF, _CustomJoin<FSG, FSH>>>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF, PG extends ModelProperties, OG, FCG, FSG, PH extends ModelProperties, OH, FCH, FSH, PI extends ModelProperties, OI, FCI, FSI>(name: string, A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>, G: IModelType<PG, OG, FCG, FSG>, H: IModelType<PH, OH, FCH, FSH>, I: IModelType<PI, OI, FCI, FSI>): IModelType<PA & PB & PC & PD & PE & PF & PG & PH & PI, OA & OB & OC & OD & OE & OF & OG & OH & OI, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, _CustomJoin<FCF, _CustomJoin<FCG, _CustomJoin<FCH, FCI>>>>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, _CustomJoin<FSF, _CustomJoin<FSG, _CustomJoin<FSH, FSI>>>>>>>>>\n// prettier-ignore\nexport function compose<PA extends ModelProperties, OA, FCA, FSA, PB extends ModelProperties, OB, FCB, FSB, PC extends ModelProperties, OC, FCC, FSC, PD extends ModelProperties, OD, FCD, FSD, PE extends ModelProperties, OE, FCE, FSE, PF\n  extends ModelProperties, OF, FCF, FSF, PG extends ModelProperties, OG, FCG, FSG, PH extends ModelProperties, OH, FCH, FSH, PI extends ModelProperties, OI, FCI, FSI>(A: IModelType<PA, OA, FCA, FSA>, B: IModelType<PB, OB, FCB, FSB>, C: IModelType<PC, OC, FCC, FSC>, D: IModelType<PD, OD, FCD, FSD>, E: IModelType<PE, OE, FCE, FSE>, F: IModelType<PF, OF, FCF, FSF>, G: IModelType<PG, OG, FCG, FSG>, H: IModelType<PH, OH, FCH, FSH>, I: IModelType<PI, OI, FCI, FSI>): IModelType<PA & PB & PC & PD & PE & PF & PG & PH & PI, OA & OB & OC & OD & OE & OF & OG & OH & OI, _CustomJoin<FCA, _CustomJoin<FCB, _CustomJoin<FCC, _CustomJoin<FCD, _CustomJoin<FCE, _CustomJoin<FCF, _CustomJoin<FCG, _CustomJoin<FCH, FCI>>>>>>>>, _CustomJoin<FSA, _CustomJoin<FSB, _CustomJoin<FSC, _CustomJoin<FSD, _CustomJoin<FSE, _CustomJoin<FSF, _CustomJoin<FSG, _CustomJoin<FSH, FSI>>>>>>>>>\n\n/**\n * `types.compose` - Composes a new model from one or more existing model types.\n * This method can be invoked in two forms:\n * Given 2 or more model types, the types are composed into a new Type.\n * Given first parameter as a string and 2 or more model types,\n * the types are composed into a new Type with the given name\n */\nexport function compose(...args: any[]): any {\n    // TODO: just join the base type names if no name is provided\n    const hasTypename = typeof args[0] === \"string\"\n    const typeName: string = hasTypename ? args[0] : \"AnonymousModel\"\n    if (hasTypename) {\n        args.shift()\n    }\n\n    // check all parameters\n    if (devMode()) {\n        args.forEach((type, i) => {\n            assertArg(type, isModelType, \"mobx-state-tree model type\", hasTypename ? i + 2 : i + 1)\n        })\n    }\n\n    return args\n        .reduce((prev, cur) =>\n            prev.cloneAndEnhance({\n                name: prev.name + \"_\" + cur.name,\n                properties: cur.properties,\n                initializers: cur.initializers,\n                preProcessor: (snapshot: any) =>\n                    cur.applySnapshotPreProcessor(prev.applySnapshotPreProcessor(snapshot)),\n                postProcessor: (snapshot: any) =>\n                    cur.applySnapshotPostProcessor(prev.applySnapshotPostProcessor(snapshot))\n            })\n        )\n        .named(typeName)\n}\n\n/**\n * Returns if a given value represents a model type.\n *\n * @param type\n * @returns\n */\nexport function isModelType(type: unknown): type is IAnyModelType {\n    return isType(type) && (type.flags & TypeFlags.Object) > 0\n}\n"
  },
  {
    "path": "src/types/index.ts",
    "content": "// we import the types to re-export them inside types.\nimport {\n    enumeration,\n    model,\n    compose,\n    custom,\n    reference,\n    safeReference,\n    union,\n    optional,\n    literal,\n    maybe,\n    maybeNull,\n    refinement,\n    string,\n    boolean,\n    number,\n    integer,\n    float,\n    finite,\n    bigint,\n    DatePrimitive,\n    map,\n    array,\n    frozen,\n    identifier,\n    identifierNumber,\n    late,\n    lazy,\n    undefinedType,\n    nullType,\n    snapshotProcessor\n} from \"../internal\"\n\nexport const types = {\n    enumeration,\n    model,\n    compose,\n    custom,\n    reference,\n    safeReference,\n    union,\n    optional,\n    literal,\n    maybe,\n    maybeNull,\n    refinement,\n    string,\n    boolean,\n    number,\n    integer,\n    float,\n    finite,\n    bigint,\n    Date: DatePrimitive,\n    map,\n    array,\n    frozen,\n    identifier,\n    identifierNumber,\n    late,\n    lazy,\n    undefined: undefinedType,\n    null: nullType,\n    snapshotProcessor\n}\n"
  },
  {
    "path": "src/types/primitives.ts",
    "content": "import {\n    SimpleType,\n    isPrimitive,\n    MstError,\n    identity,\n    createScalarNode,\n    ISimpleType,\n    IType,\n    TypeFlags,\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    isType,\n    isInteger,\n    AnyObjectNode,\n    AnyNode,\n    isFloat,\n    isFinite\n} from \"../internal\"\n\n// TODO: implement CoreType using types.custom ?\n/**\n * @internal\n * @hidden\n */\nexport class CoreType<C, S, T> extends SimpleType<C, S, T> {\n    constructor(\n        name: string,\n        readonly flags: TypeFlags,\n        private readonly checker: (value: C) => boolean,\n        private readonly initializer: (v: C) => T = identity\n    ) {\n        super(name)\n        this.flags = flags\n    }\n\n    describe() {\n        return this.name\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: C\n    ): this[\"N\"] {\n        return createScalarNode(this, parent, subpath, environment, initialValue)\n    }\n\n    createNewInstance(snapshot: C) {\n        return this.initializer(snapshot)\n    }\n\n    isValidSnapshot(value: C, context: IValidationContext): IValidationResult {\n        if (isPrimitive(value) && this.checker(value as any)) {\n            return typeCheckSuccess()\n        }\n        const typeName = this.name === \"Date\" ? \"Date or a unix milliseconds timestamp\" : this.name\n        return typeCheckFailure(context, value, `Value is not a ${typeName}`)\n    }\n}\n\n/**\n * `types.string` - Creates a type that can only contain a string value.\n * This type is used for string values by default\n *\n * Example:\n * ```ts\n * const Person = types.model({\n *   firstName: types.string,\n *   lastName: \"Doe\"\n * })\n * ```\n */\n// tslint:disable-next-line:variable-name\nexport const string: ISimpleType<string> = new CoreType<string, string, string>(\n    \"string\",\n    TypeFlags.String,\n    v => typeof v === \"string\"\n)\n\n/**\n * `types.number` - Creates a type that can only contain a numeric value.\n * This type is used for numeric values by default\n *\n * Example:\n * ```ts\n * const Vector = types.model({\n *   x: types.number,\n *   y: 1.5\n * })\n * ```\n */\n// tslint:disable-next-line:variable-name\nexport const number: ISimpleType<number> = new CoreType<number, number, number>(\n    \"number\",\n    TypeFlags.Number,\n    v => typeof v === \"number\"\n)\n\n/**\n * `types.integer` - Creates a type that can only contain an integer value.\n *\n * Example:\n * ```ts\n * const Size = types.model({\n *   width: types.integer,\n *   height: 10\n * })\n * ```\n */\n// tslint:disable-next-line:variable-name\nexport const integer: ISimpleType<number> = new CoreType<number, number, number>(\n    \"integer\",\n    TypeFlags.Integer,\n    v => isInteger(v)\n)\n\n/**\n * `types.float` - Creates a type that can only contain an float value.\n *\n * Example:\n * ```ts\n * const Size = types.model({\n *   width: types.float,\n *   height: 10\n * })\n * ```\n */\n// tslint:disable-next-line:variable-name\nexport const float: ISimpleType<number> = new CoreType<number, number, number>(\n    \"float\",\n    TypeFlags.Float,\n    v => isFloat(v)\n)\n\n/**\n * `types.finite` - Creates a type that can only contain an finite value.\n *\n * Example:\n * ```ts\n * const Size = types.model({\n *   width: types.finite,\n *   height: 10\n * })\n * ```\n */\n// tslint:disable-next-line:variable-name\nexport const finite: ISimpleType<number> = new CoreType<number, number, number>(\n    \"finite\",\n    TypeFlags.Finite,\n    v => isFinite(v)\n)\n\nconst _BigIntPrimitive = new CoreType<bigint | string | number, string, bigint>(\n    \"bigint\",\n    TypeFlags.BigInt,\n    v => {\n        if (typeof v === \"bigint\") {\n            return true\n        }\n\n        if (typeof v === \"string\" || typeof v === \"number\") {\n            try {\n                // BigInt primitive constructor verifies whether the value is a valid integer\n                BigInt(v)\n                return true\n            } catch {}\n        }\n        return false\n    },\n    v => (typeof v === \"bigint\" ? v : BigInt(v))\n)\n_BigIntPrimitive.getSnapshot = function (node: AnyNode) {\n    return String(node.storedValue)\n}\n\n/**\n * `types.bigint` - Creates a type that can only contain a bigint value.\n * Snapshots serialize to string (JSON-safe) and deserialize from string, number or bigint.\n *\n * Example:\n * ```ts\n * const BigId = types.model({\n *   id: types.identifier,\n *   value: types.bigint\n * })\n * getSnapshot(store).value // \"0\" (string, JSON-safe)\n * ```\n */\nexport const bigint: IType<bigint | string | number, string, bigint> = _BigIntPrimitive\n\n/**\n * `types.boolean` - Creates a type that can only contain a boolean value.\n * This type is used for boolean values by default\n *\n * Example:\n * ```ts\n * const Thing = types.model({\n *   isCool: types.boolean,\n *   isAwesome: false\n * })\n * ```\n */\n// tslint:disable-next-line:variable-name\nexport const boolean: ISimpleType<boolean> = new CoreType<boolean, boolean, boolean>(\n    \"boolean\",\n    TypeFlags.Boolean,\n    v => typeof v === \"boolean\"\n)\n\n/**\n * `types.null` - The type of the value `null`\n */\nexport const nullType: ISimpleType<null> = new CoreType<null, null, null>(\n    \"null\",\n    TypeFlags.Null,\n    v => v === null\n)\n\n/**\n * `types.undefined` - The type of the value `undefined`\n */\nexport const undefinedType: ISimpleType<undefined> = new CoreType<undefined, undefined, undefined>(\n    \"undefined\",\n    TypeFlags.Undefined,\n    v => v === undefined\n)\n\nconst _DatePrimitive = new CoreType<number | Date, number, Date>(\n    \"Date\",\n    TypeFlags.Date,\n    v => typeof v === \"number\" || v instanceof Date,\n    v => (v instanceof Date ? v : new Date(v))\n)\n_DatePrimitive.getSnapshot = function (node: AnyNode) {\n    return node.storedValue.getTime()\n}\n\n/**\n * `types.Date` - Creates a type that can only contain a javascript Date value.\n *\n * Example:\n * ```ts\n * const LogLine = types.model({\n *   timestamp: types.Date,\n * })\n *\n * LogLine.create({ timestamp: new Date() })\n * ```\n */\nexport const DatePrimitive: IType<number | Date, number, Date> = _DatePrimitive\n\n/**\n * @internal\n * @hidden\n */\nexport function getPrimitiveFactoryFromValue(value: any): ISimpleType<any> {\n    switch (typeof value) {\n        case \"string\":\n            return string\n        case \"number\":\n            return number // In the future, isInteger(value) ? integer : number would be interesting, but would be too breaking for now\n        case \"boolean\":\n            return boolean\n        case \"bigint\":\n            return bigint\n        case \"object\":\n            if (value instanceof Date) return DatePrimitive\n    }\n    throw new MstError(\"Cannot determine primitive type from value \" + value)\n}\n\n/**\n * Returns if a given value represents a primitive type.\n *\n * @param type\n * @returns\n */\nexport function isPrimitiveType(\n    type: unknown\n): type is\n    | ISimpleType<string>\n    | ISimpleType<number>\n    | ISimpleType<boolean>\n    | typeof bigint\n    | typeof DatePrimitive {\n    return (\n        isType(type) &&\n        (type.flags &\n            (TypeFlags.String |\n                TypeFlags.Number |\n                TypeFlags.Integer |\n                TypeFlags.Boolean |\n                TypeFlags.Date |\n                TypeFlags.BigInt)) >\n            0\n    )\n}\n"
  },
  {
    "path": "src/types/utility-types/custom.ts",
    "content": "import {\n    createScalarNode,\n    SimpleType,\n    IType,\n    TypeFlags,\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    AnyObjectNode\n} from \"../../internal\"\n\nexport interface CustomTypeOptions<S, T> {\n    /** Friendly name */\n    name: string\n    /** given a serialized value and environment, how to turn it into the target type */\n    fromSnapshot(snapshot: S, env?: any): T\n    /** return the serialization of the current value */\n    toSnapshot(value: T): S\n    /** if true, this is a converted value, if false, it's a snapshot */\n    isTargetType(value: T | S): boolean\n    /** a non empty string is assumed to be a validation error */\n    getValidationMessage(snapshot: S): string\n    // TODO: isSnapshotEqual\n    // TODO: isValueEqual\n}\n\n/**\n * `types.custom` - Creates a custom type. Custom types can be used for arbitrary immutable values, that have a serializable representation. For example, to create your own Date representation, Decimal type etc.\n *\n * The signature of the options is:\n * ```ts\n * export interface CustomTypeOptions<S, T> {\n *     // Friendly name\n *     name: string\n *     // given a serialized value and environment, how to turn it into the target type\n *     fromSnapshot(snapshot: S, env: any): T\n *     // return the serialization of the current value\n *     toSnapshot(value: T): S\n *     // if true, this is a converted value, if false, it's a snapshot\n *     isTargetType(value: T | S): value is T\n *     // a non empty string is assumed to be a validation error\n *     getValidationMessage?(snapshot: S): string\n * }\n * ```\n *\n * Example:\n * ```ts\n * const DecimalPrimitive = types.custom<string, Decimal>({\n *     name: \"Decimal\",\n *     fromSnapshot(value: string) {\n *         return new Decimal(value)\n *     },\n *     toSnapshot(value: Decimal) {\n *         return value.toString()\n *     },\n *     isTargetType(value: string | Decimal): boolean {\n *         return value instanceof Decimal\n *     },\n *     getValidationMessage(value: string): string {\n *         if (/^-?\\d+\\.\\d+$/.test(value)) return \"\" // OK\n *         return `'${value}' doesn't look like a valid decimal number`\n *     }\n * })\n *\n * const Wallet = types.model({\n *     balance: DecimalPrimitive\n * })\n * ```\n *\n * @param options\n * @returns\n */\nexport function custom<S, T>(options: CustomTypeOptions<S, T>): IType<S | T, S, T> {\n    return new CustomType<S, T>(options)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class CustomType<S, T> extends SimpleType<S | T, S, T> {\n    readonly flags = TypeFlags.Custom\n\n    constructor(protected readonly options: CustomTypeOptions<S, T>) {\n        super(options.name)\n    }\n\n    describe() {\n        return this.name\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (this.options.isTargetType(value)) return typeCheckSuccess()\n\n        const typeError: string = this.options.getValidationMessage(value as S)\n        if (typeError) {\n            return typeCheckFailure(\n                context,\n                value,\n                `Invalid value for type '${this.name}': ${typeError}`\n            )\n        }\n        return typeCheckSuccess()\n    }\n\n    getSnapshot(node: this[\"N\"]): S {\n        return this.options.toSnapshot(node.storedValue)\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: S | T\n    ): this[\"N\"] {\n        const valueToStore: T = this.options.isTargetType(initialValue)\n            ? (initialValue as T)\n            : this.options.fromSnapshot(initialValue as S, parent && parent.root.environment)\n        return createScalarNode(this, parent, subpath, environment, valueToStore)\n    }\n\n    reconcile(current: this[\"N\"], value: S | T, parent: AnyObjectNode, subpath: string): this[\"N\"] {\n        const isSnapshot = !this.options.isTargetType(value)\n        // in theory customs use scalar nodes which cannot be detached, but still...\n        if (!current.isDetaching) {\n            const unchanged =\n                current.type === this &&\n                (isSnapshot ? value === current.snapshot : value === current.storedValue)\n            if (unchanged) {\n                current.setParent(parent, subpath)\n                return current\n            }\n        }\n        const valueToStore: T = isSnapshot\n            ? this.options.fromSnapshot(value as S, parent.root.environment)\n            : (value as T)\n        const newNode = this.instantiate(parent, subpath, undefined, valueToStore)\n        current.die() // noop if detaching\n        return newNode\n    }\n}\n"
  },
  {
    "path": "src/types/utility-types/enumeration.ts",
    "content": "import { ISimpleType, union, literal, assertIsString, devMode } from \"../../internal\"\n\n/** @hidden */\nexport type UnionStringArray<T extends readonly string[]> = T[number]\n\n// strongly typed enumeration forms for plain and readonly string arrays (when passed directly to the function).\n// with these overloads, we get correct typing for native TS string enums when we use Object.values(Enum) as Enum[] as options.\n// these overloads also allow both mutable and immutable arrays, making types.enumeration<Enum>(Object.values(Enum)) possible.\n// the only case where this doesn't work is when passing to the function an array variable with a mutable type constraint;\n// for these cases, it will just fallback and assume the type is a generic string.\nexport function enumeration<T extends string>(\n    options: readonly T[]\n): ISimpleType<UnionStringArray<T[]>>\nexport function enumeration<T extends string>(\n    name: string,\n    options: readonly T[]\n): ISimpleType<UnionStringArray<T[]>>\n/**\n * `types.enumeration` - Can be used to create an string based enumeration.\n * (note: this methods is just sugar for a union of string literals)\n *\n * Example:\n * ```ts\n * const TrafficLight = types.model({\n *   color: types.enumeration(\"Color\", [\"Red\", \"Orange\", \"Green\"])\n * })\n * ```\n *\n * @param name descriptive name of the enumeration (optional)\n * @param options possible values this enumeration can have\n * @returns\n */\nexport function enumeration<T extends string>(\n    name: string | readonly T[],\n    options?: readonly T[]\n): ISimpleType<T[number]> {\n    const realOptions: readonly T[] = typeof name === \"string\" ? options! : name\n    // check all options\n    if (devMode()) {\n        realOptions.forEach((option, i) => {\n            assertIsString(option, i + 1)\n        })\n    }\n    const type = union(...realOptions.map(option => literal(\"\" + option)))\n    if (typeof name === \"string\") type.name = name\n    return type as ISimpleType<T[number]>\n}\n"
  },
  {
    "path": "src/types/utility-types/frozen.ts",
    "content": "import {\n    isSerializable,\n    deepFreeze,\n    createScalarNode,\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    TypeFlags,\n    isType,\n    optional,\n    IType,\n    IAnyType,\n    AnyObjectNode,\n    SimpleType,\n    type ISimpleType\n} from \"../../internal\"\n\n/**\n * @internal\n * @hidden\n */\nexport class Frozen<T> extends SimpleType<T, T, T> {\n    flags = TypeFlags.Frozen\n\n    constructor(private subType?: IAnyType) {\n        super(subType ? `frozen(${subType.name})` : \"frozen\")\n    }\n\n    describe() {\n        return \"<any immutable value>\"\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        value: this[\"C\"]\n    ): this[\"N\"] {\n        // create the node\n        return createScalarNode(this, parent, subpath, environment, deepFreeze(value))\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (!isSerializable(value)) {\n            return typeCheckFailure(\n                context,\n                value,\n                \"Value is not serializable and cannot be frozen\"\n            )\n        }\n        if (this.subType) return this.subType.validate(value, context)\n        return typeCheckSuccess()\n    }\n}\n\nconst untypedFrozenInstance = new Frozen()\n\nexport function frozen<C>(subType: IType<C, any, any>): IType<C, C, C>\nexport function frozen<T>(defaultValue: T): IType<T | undefined | null, T, T>\nexport function frozen<T = any>(): IType<T, T, T> // do not assume undefined by default, let the user specify it if needed\n/**\n * `types.frozen` - Frozen can be used to store any value that is serializable in itself (that is valid JSON).\n * Frozen values need to be immutable or treated as if immutable. They need be serializable as well.\n * Values stored in frozen will snapshotted as-is by MST, and internal changes will not be tracked.\n *\n * This is useful to store complex, but immutable values like vectors etc. It can form a powerful bridge to parts of your application that should be immutable, or that assume data to be immutable.\n *\n * Note: if you want to store free-form state that is mutable, or not serializeable, consider using volatile state instead.\n *\n * Frozen properties can be defined in three different ways\n * 1. `types.frozen(SubType)` - provide a valid MST type and frozen will check if the provided data conforms the snapshot for that type\n * 2. `types.frozen({ someDefaultValue: true})` - provide a primitive value, object or array, and MST will infer the type from that object, and also make it the default value for the field\n * 3. `types.frozen<TypeScriptType>()` - provide a typescript type, to help in strongly typing the field (design time only)\n *\n * Example:\n * ```ts\n * const GameCharacter = types.model({\n *   name: string,\n *   location: types.frozen({ x: 0, y: 0})\n * })\n *\n * const hero = GameCharacter.create({\n *   name: \"Mario\",\n *   location: { x: 7, y: 4 }\n * })\n *\n * hero.location = { x: 10, y: 2 } // OK\n * hero.location.x = 7 // Not ok!\n * ```\n *\n * ```ts\n * type Point = { x: number, y: number }\n *    const Mouse = types.model({\n *         loc: types.frozen<Point>()\n *    })\n * ```\n *\n * @param defaultValueOrType\n * @returns\n */\nexport function frozen(arg?: any): any {\n    if (arguments.length === 0) return untypedFrozenInstance\n    else if (isType(arg)) return new Frozen(arg)\n    else return optional(untypedFrozenInstance, arg)\n}\n\n/**\n * Returns if a given value represents a frozen type.\n *\n * @param type\n * @returns\n */\nexport function isFrozenType(type: unknown): type is ISimpleType<any> {\n    return isType(type) && (type.flags & TypeFlags.Frozen) > 0\n}\n"
  },
  {
    "path": "src/types/utility-types/identifier.ts",
    "content": "import {\n    MstError,\n    createScalarNode,\n    SimpleType,\n    TypeFlags,\n    isType,\n    IValidationContext,\n    IValidationResult,\n    typeCheckFailure,\n    ModelType,\n    typeCheckSuccess,\n    ISimpleType,\n    AnyObjectNode,\n    ScalarNode,\n    assertArg\n} from \"../../internal\"\n\nabstract class BaseIdentifierType<T> extends SimpleType<T, T, T> {\n    readonly flags = TypeFlags.Identifier\n\n    constructor(\n        name: string,\n        private readonly validType: \"string\" | \"number\"\n    ) {\n        super(name)\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"]\n    ): this[\"N\"] {\n        if (!parent || !(parent.type instanceof ModelType))\n            throw new MstError(\n                `Identifier types can only be instantiated as direct child of a model type`\n            )\n\n        return createScalarNode(this, parent, subpath, environment, initialValue)\n    }\n\n    reconcile(current: this[\"N\"], newValue: this[\"C\"], parent: AnyObjectNode, subpath: string) {\n        // we don't consider detaching here since identifier are scalar nodes, and scalar nodes cannot be detached\n        if (current.storedValue !== newValue)\n            throw new MstError(\n                `Tried to change identifier from '${current.storedValue}' to '${newValue}'. Changing identifiers is not allowed.`\n            )\n        current.setParent(parent, subpath)\n        return current\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (typeof value !== this.validType) {\n            return typeCheckFailure(\n                context,\n                value,\n                `Value is not a valid ${this.describe()}, expected a ${this.validType}`\n            )\n        }\n        return typeCheckSuccess()\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class IdentifierType extends BaseIdentifierType<string> {\n    readonly flags = TypeFlags.Identifier\n\n    constructor() {\n        super(`identifier`, \"string\")\n    }\n\n    describe() {\n        return `identifier`\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class IdentifierNumberType extends BaseIdentifierType<number> {\n    constructor() {\n        super(\"identifierNumber\", \"number\")\n    }\n\n    getSnapshot(node: ScalarNode<number, number, number>): number {\n        return node.storedValue\n    }\n\n    describe() {\n        return `identifierNumber`\n    }\n}\n\n/**\n * `types.identifier` - Identifiers are used to make references, lifecycle events and reconciling works.\n * Inside a state tree, for each type can exist only one instance for each given identifier.\n * For example there couldn't be 2 instances of user with id 1. If you need more, consider using references.\n * Identifier can be used only as type property of a model.\n * This type accepts as parameter the value type of the identifier field that can be either string or number.\n *\n * Example:\n * ```ts\n *  const Todo = types.model(\"Todo\", {\n *      id: types.identifier,\n *      title: types.string\n *  })\n * ```\n *\n * @returns\n */\nexport const identifier: ISimpleType<string> = new IdentifierType()\n\n/**\n * `types.identifierNumber` - Similar to `types.identifier`. This one will serialize from / to a number when applying snapshots\n *\n * Example:\n * ```ts\n *  const Todo = types.model(\"Todo\", {\n *      id: types.identifierNumber,\n *      title: types.string\n *  })\n * ```\n *\n * @returns\n */\nexport const identifierNumber: ISimpleType<number> = new IdentifierNumberType()\n\n/**\n * Returns if a given value represents an identifier type.\n *\n * @param type\n * @returns\n */\nexport function isIdentifierType(\n    type: unknown\n): type is typeof identifier | typeof identifierNumber {\n    return isType(type) && (type.flags & TypeFlags.Identifier) > 0\n}\n\n/**\n * Valid types for identifiers.\n */\nexport type ReferenceIdentifier = string | number\n\n/**\n * @internal\n * @hidden\n */\nexport function normalizeIdentifier(id: ReferenceIdentifier): string {\n    return \"\" + id\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isValidIdentifier(id: any): id is ReferenceIdentifier {\n    return typeof id === \"string\" || typeof id === \"number\"\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertIsValidIdentifier(id: ReferenceIdentifier, argNumber: number | number[]) {\n    assertArg(id, isValidIdentifier, \"string or number (identifier)\", argNumber)\n}\n"
  },
  {
    "path": "src/types/utility-types/late.ts",
    "content": "import {\n    MstError,\n    BaseType,\n    IValidationContext,\n    IValidationResult,\n    TypeFlags,\n    isType,\n    IAnyType,\n    typeCheckSuccess,\n    AnyObjectNode,\n    ExtractNodeType,\n    cannotDetermineSubtype,\n    devMode\n} from \"../../internal\"\n\nclass Late<IT extends IAnyType> extends BaseType<\n    IT[\"CreationType\"],\n    IT[\"SnapshotType\"],\n    IT[\"TypeWithoutSTN\"],\n    ExtractNodeType<IT>\n> {\n    private _subType?: IT\n\n    get flags() {\n        return (this._subType ? this._subType.flags : 0) | TypeFlags.Late\n    }\n\n    getSubType(mustSucceed: true): IT\n    getSubType(mustSucceed: false): IT | undefined\n    getSubType(mustSucceed: boolean): IT | undefined {\n        if (!this._subType) {\n            let t = undefined\n            try {\n                t = this._definition()\n            } catch (e) {\n                if (e instanceof ReferenceError)\n                    // can happen in strict ES5 code when a definition is self refering\n                    t = undefined\n                else throw e\n            }\n            if (mustSucceed && t === undefined)\n                throw new MstError(\n                    \"Late type seems to be used too early, the definition (still) returns undefined\"\n                )\n            if (t) {\n                if (devMode() && !isType(t))\n                    throw new MstError(\n                        \"Failed to determine subtype, make sure types.late returns a type definition.\"\n                    )\n                this._subType = t\n            }\n        }\n        return this._subType\n    }\n\n    constructor(\n        name: string,\n        private readonly _definition: () => IT\n    ) {\n        super(name)\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        return this.getSubType(true).instantiate(parent, subpath, environment, initialValue) as any\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        return this.getSubType(true).reconcile(current, newValue, parent, subpath) as any\n    }\n\n    describe() {\n        const t = this.getSubType(false)\n        return t ? t.name : \"<uknown late type>\"\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        const t = this.getSubType(false)\n        if (!t) {\n            // See #916; the variable the definition closure is pointing to wasn't defined yet, so can't be evaluted yet here\n            return typeCheckSuccess()\n        }\n        return t.validate(value, context)\n    }\n\n    isAssignableFrom(type: IAnyType) {\n        const t = this.getSubType(false)\n        return t ? t.isAssignableFrom(type) : false\n    }\n\n    getSubTypes() {\n        const subtype = this.getSubType(false)\n        return subtype ? subtype : cannotDetermineSubtype\n    }\n}\n\nexport function late<T extends IAnyType>(type: () => T): T\nexport function late<T extends IAnyType>(name: string, type: () => T): T\n/**\n * `types.late` - Defines a type that gets implemented later. This is useful when you have to deal with circular dependencies.\n * Please notice that when defining circular dependencies TypeScript isn't smart enough to inference them.\n *\n * Example:\n * ```ts\n *   // TypeScript isn't smart enough to infer self referencing types.\n *  const Node = types.model({\n *       children: types.array(types.late((): IAnyModelType => Node)) // then typecast each array element to Instance<typeof Node>\n *  })\n * ```\n *\n * @param name The name to use for the type that will be returned.\n * @param type A function that returns the type that will be defined.\n * @returns\n */\nexport function late(nameOrType: any, maybeType?: () => IAnyType): IAnyType {\n    const name = typeof nameOrType === \"string\" ? nameOrType : `late(${nameOrType.toString()})`\n    const type = typeof nameOrType === \"string\" ? maybeType : nameOrType\n    // checks that the type is actually a late type\n    if (devMode()) {\n        if (!(typeof type === \"function\" && type.length === 0))\n            throw new MstError(\n                \"Invalid late type, expected a function with zero arguments that returns a type, got: \" +\n                    type\n            )\n    }\n    return new Late(name, type)\n}\n\n/**\n * Returns if a given value represents a late type.\n *\n * @param type\n * @returns\n */\nexport function isLateType(type: unknown): type is IAnyType {\n    return isType(type) && (type.flags & TypeFlags.Late) > 0\n}\n"
  },
  {
    "path": "src/types/utility-types/lazy.ts",
    "content": "import { action, IObservableArray, observable, when } from \"mobx\"\nimport { AnyNode } from \"../../core/node/BaseNode\"\nimport { IType } from \"../../core/type/type\"\nimport {\n    IValidationContext,\n    IValidationResult,\n    TypeFlags,\n    typeCheckSuccess,\n    AnyObjectNode,\n    SimpleType,\n    createScalarNode,\n    deepFreeze,\n    isSerializable,\n    typeCheckFailure\n} from \"../../internal\"\n\ninterface LazyOptions<T extends IType<any, any, any>, U> {\n    loadType: () => Promise<T>\n    shouldLoadPredicate: (parent: U) => boolean\n}\n\nexport function lazy<T extends IType<any, any, any>, U>(\n    name: string,\n    options: LazyOptions<T, U>\n): T {\n    // TODO: fix this unknown casting to be stricter\n    return new Lazy(name, options) as unknown as T\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class Lazy<T extends IType<any, any, any>, U> extends SimpleType<T, T, T> {\n    flags = TypeFlags.Lazy\n\n    private loadedType: T | null = null\n    private pendingNodeList: IObservableArray<AnyNode> = observable.array()\n\n    constructor(\n        name: string,\n        private readonly options: LazyOptions<T, U>\n    ) {\n        super(name)\n\n        when(\n            () =>\n                this.pendingNodeList.length > 0 &&\n                this.pendingNodeList.some(\n                    node =>\n                        node.isAlive &&\n                        this.options.shouldLoadPredicate(node.parent ? node.parent.value : null)\n                ),\n            () => {\n                this.options.loadType().then(\n                    action((type: T) => {\n                        this.loadedType = type\n                        this.pendingNodeList.forEach(node => {\n                            if (!node.parent) return\n                            if (!this.loadedType) return\n\n                            node.parent.applyPatches([\n                                {\n                                    op: \"replace\",\n                                    path: `/${node.subpath}`,\n                                    value: node.snapshot\n                                }\n                            ])\n                        })\n                    })\n                )\n            }\n        )\n    }\n\n    describe() {\n        return `<lazy ${this.name}>`\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        value: this[\"C\"]\n    ): this[\"N\"] {\n        if (this.loadedType) {\n            return this.loadedType.instantiate(parent, subpath, environment, value) as this[\"N\"]\n        }\n\n        const node = createScalarNode(this, parent, subpath, environment, deepFreeze(value))\n        this.pendingNodeList.push(node)\n\n        when(\n            () => !node.isAlive,\n            () => this.pendingNodeList.splice(this.pendingNodeList.indexOf(node), 1)\n        )\n\n        return node\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (this.loadedType) {\n            return this.loadedType.validate(value, context)\n        }\n        if (!isSerializable(value)) {\n            return typeCheckFailure(context, value, \"Value is not serializable and cannot be lazy\")\n        }\n        return typeCheckSuccess()\n    }\n\n    reconcile(current: this[\"N\"], value: T, parent: AnyObjectNode, subpath: string): this[\"N\"] {\n        if (this.loadedType) {\n            current.die()\n            return this.loadedType.instantiate(\n                parent,\n                subpath,\n                parent.environment,\n                value\n            ) as this[\"N\"]\n        }\n        return super.reconcile(current, value, parent, subpath)\n    }\n}\n"
  },
  {
    "path": "src/types/utility-types/literal.ts",
    "content": "import {\n    isPrimitive,\n    createScalarNode,\n    ISimpleType,\n    TypeFlags,\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    isType,\n    Primitives,\n    AnyObjectNode,\n    SimpleType,\n    devMode\n} from \"../../internal\"\nimport { assertArg } from \"../../utils\"\n\n/**\n * @internal\n * @hidden\n */\nexport class Literal<T> extends SimpleType<T, T, T> {\n    readonly value: T\n    readonly flags = TypeFlags.Literal\n\n    constructor(value: T) {\n        super(JSON.stringify(value))\n        this.value = value\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"]\n    ): this[\"N\"] {\n        return createScalarNode(this, parent, subpath, environment, initialValue)\n    }\n\n    describe() {\n        return JSON.stringify(this.value)\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (isPrimitive(value) && value === this.value) {\n            return typeCheckSuccess()\n        }\n        return typeCheckFailure(\n            context,\n            value,\n            `Value is not a literal ${JSON.stringify(this.value)}`\n        )\n    }\n}\n\n/**\n * `types.literal` - The literal type will return a type that will match only the exact given type.\n * The given value must be a primitive, in order to be serialized to a snapshot correctly.\n * You can use literal to match exact strings for example the exact male or female string.\n *\n * Example:\n * ```ts\n * const Person = types.model({\n *     name: types.string,\n *     gender: types.union(types.literal('male'), types.literal('female'))\n * })\n * ```\n *\n * @param value The value to use in the strict equal check\n * @returns\n */\nexport function literal<S extends Primitives>(value: S): ISimpleType<S> {\n    // check that the given value is a primitive\n    assertArg(value, isPrimitive, \"primitive\", 1)\n\n    return new Literal<S>(value)\n}\n\n/**\n * Returns if a given value represents a literal type.\n *\n * @param type\n * @returns\n */\nexport function isLiteralType(type: unknown): type is ISimpleType<any> {\n    return isType(type) && (type.flags & TypeFlags.Literal) > 0\n}\n"
  },
  {
    "path": "src/types/utility-types/maybe.ts",
    "content": "import {\n    union,\n    optional,\n    IType,\n    undefinedType,\n    nullType,\n    IAnyType,\n    assertIsType\n} from \"../../internal\"\n\nconst optionalUndefinedType = optional(undefinedType, undefined)\nconst optionalNullType = optional(nullType, null)\n\n/** @hidden */\nexport interface IMaybeIType<IT extends IAnyType, C, O>\n    extends IType<IT[\"CreationType\"] | C, IT[\"SnapshotType\"] | O, IT[\"TypeWithoutSTN\"] | O> {}\n\n/** @hidden */\nexport interface IMaybe<IT extends IAnyType> extends IMaybeIType<IT, undefined, undefined> {}\n\n/** @hidden */\nexport interface IMaybeNull<IT extends IAnyType> extends IMaybeIType<IT, null | undefined, null> {}\n\n/**\n * `types.maybe` - Maybe will make a type nullable, and also optional.\n * The value `undefined` will be used to represent nullability.\n *\n * @param type\n * @returns\n */\nexport function maybe<IT extends IAnyType>(type: IT): IMaybe<IT> {\n    assertIsType(type, 1)\n\n    return union(type, optionalUndefinedType)\n}\n\n/**\n * `types.maybeNull` - Maybe will make a type nullable, and also optional.\n * The value `null` will be used to represent no value.\n *\n * @param type\n * @returns\n */\nexport function maybeNull<IT extends IAnyType>(type: IT): IMaybeNull<IT> {\n    assertIsType(type, 1)\n\n    return union(type, optionalNullType)\n}\n"
  },
  {
    "path": "src/types/utility-types/optional.ts",
    "content": "import {\n    isStateTreeNode,\n    IType,\n    TypeFlags,\n    isType,\n    IValidationContext,\n    IValidationResult,\n    typecheckInternal,\n    typeCheckSuccess,\n    MstError,\n    IAnyType,\n    AnyObjectNode,\n    BaseType,\n    assertIsType,\n    ExtractCSTWithSTN,\n    devMode\n} from \"../../internal\"\n\ntype IFunctionReturn<T> = () => T\n\ntype IOptionalValue<C, T> = C | IFunctionReturn<C | T>\n\n/** @hidden */\nexport type ValidOptionalValue = string | boolean | number | null | undefined\n\n/** @hidden */\nexport type ValidOptionalValues = [ValidOptionalValue, ...ValidOptionalValue[]]\n\n/**\n * @hidden\n * @internal\n */\nexport class OptionalValue<\n    IT extends IAnyType,\n    OptionalVals extends ValidOptionalValues\n> extends BaseType<\n    IT[\"CreationType\"] | OptionalVals[number],\n    IT[\"SnapshotType\"],\n    IT[\"TypeWithoutSTN\"]\n> {\n    get flags() {\n        return this._subtype.flags | TypeFlags.Optional\n    }\n\n    constructor(\n        private readonly _subtype: IT,\n        private readonly _defaultValue: IOptionalValue<IT[\"CreationType\"], IT[\"Type\"]>,\n        readonly optionalValues: OptionalVals\n    ) {\n        super(_subtype.name)\n    }\n\n    describe() {\n        return this._subtype.describe() + \"?\"\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        if (this.optionalValues.indexOf(initialValue) >= 0) {\n            const defaultInstanceOrSnapshot = this.getDefaultInstanceOrSnapshot()\n            return this._subtype.instantiate(\n                parent,\n                subpath,\n                environment,\n                defaultInstanceOrSnapshot\n            )\n        }\n        return this._subtype.instantiate(parent, subpath, environment, initialValue)\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        return this._subtype.reconcile(\n            current,\n            this.optionalValues.indexOf(newValue) < 0 && this._subtype.is(newValue)\n                ? newValue\n                : this.getDefaultInstanceOrSnapshot(),\n            parent,\n            subpath\n        )\n    }\n\n    getDefaultInstanceOrSnapshot(): this[\"C\"] | this[\"T\"] {\n        const defaultInstanceOrSnapshot =\n            typeof this._defaultValue === \"function\"\n                ? (this._defaultValue as IFunctionReturn<this[\"C\"] | this[\"T\"]>)()\n                : this._defaultValue\n\n        // while static values are already snapshots and checked on types.optional\n        // generator functions must always be rechecked just in case\n        if (typeof this._defaultValue === \"function\") {\n            typecheckInternal(this, defaultInstanceOrSnapshot)\n        }\n\n        return defaultInstanceOrSnapshot\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        // defaulted values can be skipped\n        if (this.optionalValues.indexOf(value) >= 0) {\n            return typeCheckSuccess()\n        }\n        // bounce validation to the sub-type\n        return this._subtype.validate(value, context)\n    }\n\n    isAssignableFrom(type: IAnyType) {\n        return this._subtype.isAssignableFrom(type)\n    }\n\n    getSubTypes() {\n        return this._subtype\n    }\n}\n\n/** @hidden */\nexport type OptionalDefaultValueOrFunction<IT extends IAnyType> =\n    | IT[\"CreationType\"]\n    | IT[\"SnapshotType\"]\n    | (() => ExtractCSTWithSTN<IT>)\n\n/** @hidden */\nexport interface IOptionalIType<IT extends IAnyType, OptionalVals extends ValidOptionalValues>\n    extends IType<\n        IT[\"CreationType\"] | OptionalVals[number],\n        IT[\"SnapshotType\"],\n        IT[\"TypeWithoutSTN\"]\n    > {}\n\nfunction checkOptionalPreconditions<IT extends IAnyType>(\n    type: IAnyType,\n    defaultValueOrFunction: OptionalDefaultValueOrFunction<IT>\n) {\n    // make sure we never pass direct instances\n    if (typeof defaultValueOrFunction !== \"function\" && isStateTreeNode(defaultValueOrFunction)) {\n        throw new MstError(\n            \"default value cannot be an instance, pass a snapshot or a function that creates an instance/snapshot instead\"\n        )\n    }\n    assertIsType(type, 1)\n    if (devMode()) {\n        // we only check default values if they are passed directly\n        // if they are generator functions they will be checked once they are generated\n        // we don't check generator function results here to avoid generating a node just for type-checking purposes\n        // which might generate side-effects\n        if (typeof defaultValueOrFunction !== \"function\") {\n            typecheckInternal(type, defaultValueOrFunction)\n        }\n    }\n}\n\nexport function optional<IT extends IAnyType>(\n    type: IT,\n    defaultValueOrFunction: OptionalDefaultValueOrFunction<IT>\n): IOptionalIType<IT, [undefined]>\nexport function optional<IT extends IAnyType, OptionalVals extends ValidOptionalValues>(\n    type: IT,\n    defaultValueOrFunction: OptionalDefaultValueOrFunction<IT>,\n    optionalValues: OptionalVals\n): IOptionalIType<IT, OptionalVals>\n/**\n * `types.optional` - Can be used to create a property with a default value.\n *\n * Depending on the third argument (`optionalValues`) there are two ways of operation:\n * - If the argument is not provided, then if a value is not provided in the snapshot (`undefined` or missing),\n *   it will default to the provided `defaultValue`\n * - If the argument is provided, then if the value in the snapshot matches one of the optional values inside the array then it will\n *   default to the provided `defaultValue`. Additionally, if one of the optional values inside the array is `undefined` then a missing\n *   property is also valid.\n *\n *   Note that it is also possible to include values of the same type as the intended subtype as optional values,\n *   in this case the optional value will be transformed into the `defaultValue` (e.g. `types.optional(types.string, \"unnamed\", [undefined, \"\"])`\n *   will transform the snapshot values `undefined` (and therefore missing) and empty strings into the string `\"unnamed\"` when it gets\n *   instantiated).\n *\n * If `defaultValue` is a function, the function will be invoked for every new instance.\n * Applying a snapshot in which the optional value is one of the optional values (or `undefined`/_not_ present if none are provided) causes the\n * value to be reset.\n *\n * Example:\n * ```ts\n * const Todo = types.model({\n *   title: types.string,\n *   subtitle1: types.optional(types.string, \"\", [null]),\n *   subtitle2: types.optional(types.string, \"\", [null, undefined]),\n *   done: types.optional(types.boolean, false),\n *   created: types.optional(types.Date, () => new Date()),\n * })\n *\n * // if done is missing / undefined it will become false\n * // if created is missing / undefined it will get a freshly generated timestamp\n * // if subtitle1 is null it will default to \"\", but it cannot be missing or undefined\n * // if subtitle2 is null or undefined it will default to \"\"; since it can be undefined it can also be missing\n * const todo = Todo.create({ title: \"Get coffee\", subtitle1: null })\n * ```\n *\n * @param type\n * @param defaultValueOrFunction\n * @param optionalValues an optional array with zero or more primitive values (string, number, boolean, null or undefined)\n *                       that will be converted into the default. `[ undefined ]` is assumed when none is provided\n * @returns\n */\nexport function optional<IT extends IAnyType, OptionalVals extends ValidOptionalValues>(\n    type: IT,\n    defaultValueOrFunction: OptionalDefaultValueOrFunction<IT>,\n    optionalValues?: OptionalVals\n): IOptionalIType<IT, OptionalVals> {\n    checkOptionalPreconditions(type, defaultValueOrFunction)\n\n    return new OptionalValue(\n        type,\n        defaultValueOrFunction,\n        optionalValues ? optionalValues : undefinedAsOptionalValues\n    )\n}\n\nconst undefinedAsOptionalValues: [undefined] = [undefined]\n\n/**\n * Returns if a value represents an optional type.\n *\n * @template IT\n * @param type\n * @returns\n */\nexport function isOptionalType(type: unknown): type is IOptionalIType<IAnyType, [any, ...any[]]> {\n    return isType(type) && (type.flags & TypeFlags.Optional) > 0\n}\n"
  },
  {
    "path": "src/types/utility-types/reference.ts",
    "content": "import {\n    getStateTreeNode,\n    isStateTreeNode,\n    createScalarNode,\n    IType,\n    TypeFlags,\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    MstError,\n    IAnyType,\n    IAnyStateTreeNode,\n    IAnyComplexType,\n    Hook,\n    IDisposer,\n    maybe,\n    isModelType,\n    IMaybe,\n    NodeLifeCycle,\n    ReferenceIdentifier,\n    normalizeIdentifier,\n    getIdentifier,\n    applyPatch,\n    AnyNode,\n    AnyObjectNode,\n    SimpleType,\n    assertIsType,\n    isValidIdentifier,\n    IStateTreeNode,\n    devMode,\n    isType,\n    type IAnyModelType\n} from \"../../internal\"\n\nexport type OnReferenceInvalidatedEvent<STN extends IAnyStateTreeNode> = {\n    parent: IAnyStateTreeNode\n    invalidTarget: STN | undefined\n    invalidId: ReferenceIdentifier\n    replaceRef: (newRef: STN | null | undefined) => void\n    removeRef: () => void\n    cause: \"detach\" | \"destroy\" | \"invalidSnapshotReference\"\n}\n\nexport type OnReferenceInvalidated<STN extends IAnyStateTreeNode> = (\n    event: OnReferenceInvalidatedEvent<STN>\n) => void\n\nfunction getInvalidationCause(hook: Hook): \"detach\" | \"destroy\" | undefined {\n    switch (hook) {\n        case Hook.beforeDestroy:\n            return \"destroy\"\n        case Hook.beforeDetach:\n            return \"detach\"\n        default:\n            return undefined\n    }\n}\n\n/** @hidden */\nexport type ReferenceT<IT extends IAnyType> = IT[\"TypeWithoutSTN\"] &\n    IStateTreeNode<IReferenceType<IT>>\n\nclass StoredReference<IT extends IAnyType> {\n    readonly identifier!: ReferenceIdentifier\n    node!: AnyNode\n\n    private resolvedReference?: {\n        node: AnyObjectNode\n        lastCacheModification: string\n    }\n\n    constructor(\n        value: ReferenceT<IT> | ReferenceIdentifier,\n        private readonly targetType: IT\n    ) {\n        if (isValidIdentifier(value)) {\n            this.identifier = value\n        } else if (isStateTreeNode(value)) {\n            const targetNode = getStateTreeNode(value)\n            if (!targetNode.identifierAttribute)\n                throw new MstError(`Can only store references with a defined identifier attribute.`)\n            const id = targetNode.unnormalizedIdentifier\n            if (id === null || id === undefined) {\n                throw new MstError(\n                    `Can only store references to tree nodes with a defined identifier.`\n                )\n            }\n            this.identifier = id\n        } else {\n            throw new MstError(\n                `Can only store references to tree nodes or identifiers, got: '${value}'`\n            )\n        }\n    }\n\n    private updateResolvedReference(node: AnyNode) {\n        const normalizedId = normalizeIdentifier(this.identifier)\n        const root = node.root\n        const lastCacheModification =\n            root.identifierCache!.getLastCacheModificationPerId(normalizedId)\n        if (\n            !this.resolvedReference ||\n            this.resolvedReference.lastCacheModification !== lastCacheModification\n        ) {\n            const { targetType } = this\n            // reference was initialized with the identifier of the target\n\n            const target = root.identifierCache!.resolve(targetType, normalizedId)\n\n            if (!target) {\n                throw new InvalidReferenceError(\n                    `[mobx-state-tree] Failed to resolve reference '${this.identifier}' to type '${this.targetType.name}' (from node: ${node.path})`\n                )\n            }\n\n            this.resolvedReference = {\n                node: target!,\n                lastCacheModification: lastCacheModification\n            }\n        }\n    }\n\n    get resolvedValue(): ReferenceT<IT> {\n        this.updateResolvedReference(this.node)\n        return this.resolvedReference!.node.value\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class InvalidReferenceError extends Error {\n    constructor(m: string) {\n        super(m)\n\n        Object.setPrototypeOf(this, InvalidReferenceError.prototype)\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport abstract class BaseReferenceType<IT extends IAnyComplexType> extends SimpleType<\n    ReferenceIdentifier,\n    ReferenceIdentifier,\n    IT[\"TypeWithoutSTN\"]\n> {\n    readonly flags = TypeFlags.Reference\n\n    constructor(\n        protected readonly targetType: IT,\n        private readonly onInvalidated?: OnReferenceInvalidated<ReferenceT<IT>>\n    ) {\n        super(`reference(${targetType.name})`)\n    }\n\n    describe() {\n        return this.name\n    }\n\n    isAssignableFrom(type: IAnyType): boolean {\n        return this.targetType.isAssignableFrom(type)\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        return isValidIdentifier(value)\n            ? typeCheckSuccess()\n            : typeCheckFailure(\n                  context,\n                  value,\n                  \"Value is not a valid identifier, which is a string or a number\"\n              )\n    }\n\n    private fireInvalidated(\n        cause: \"detach\" | \"destroy\" | \"invalidSnapshotReference\",\n        storedRefNode: this[\"N\"],\n        referenceId: ReferenceIdentifier,\n        refTargetNode: AnyObjectNode | null\n    ) {\n        // to actually invalidate a reference we need an alive parent,\n        // since it is a scalar value (immutable-ish) and we need to change it\n        // from the parent\n        const storedRefParentNode = storedRefNode.parent\n        if (!storedRefParentNode || !storedRefParentNode.isAlive) {\n            return\n        }\n        const storedRefParentValue = storedRefParentNode.storedValue\n        if (!storedRefParentValue) {\n            return\n        }\n        this.onInvalidated!({\n            cause,\n            parent: storedRefParentValue,\n            invalidTarget: refTargetNode ? refTargetNode.storedValue : undefined,\n            invalidId: referenceId,\n            replaceRef(newRef) {\n                applyPatch(storedRefNode.root.storedValue, {\n                    op: \"replace\",\n                    value: newRef,\n                    path: storedRefNode.path\n                })\n            },\n            removeRef() {\n                if (isModelType(storedRefParentNode.type)) {\n                    this.replaceRef(undefined as any)\n                } else {\n                    applyPatch(storedRefNode.root.storedValue, {\n                        op: \"remove\",\n                        path: storedRefNode.path\n                    })\n                }\n            }\n        })\n    }\n\n    private addTargetNodeWatcher(\n        storedRefNode: this[\"N\"],\n        referenceId: ReferenceIdentifier\n    ): IDisposer | undefined {\n        // this will make sure the target node becomes created\n        const refTargetValue = this.getValue(storedRefNode)\n        if (!refTargetValue) {\n            return undefined\n        }\n        const refTargetNode = getStateTreeNode(refTargetValue)\n\n        const hookHandler = (_: AnyNode, refTargetNodeHook: Hook) => {\n            const cause = getInvalidationCause(refTargetNodeHook)\n            if (!cause) {\n                return\n            }\n            this.fireInvalidated(cause, storedRefNode, referenceId, refTargetNode)\n        }\n\n        const refTargetDetachHookDisposer = refTargetNode.registerHook(\n            Hook.beforeDetach,\n            hookHandler\n        )\n        const refTargetDestroyHookDisposer = refTargetNode.registerHook(\n            Hook.beforeDestroy,\n            hookHandler\n        )\n\n        return () => {\n            refTargetDetachHookDisposer()\n            refTargetDestroyHookDisposer()\n        }\n    }\n\n    protected watchTargetNodeForInvalidations(\n        storedRefNode: this[\"N\"],\n        identifier: ReferenceIdentifier,\n        customGetSet: ReferenceOptionsGetSet<IT> | undefined\n    ) {\n        if (!this.onInvalidated) {\n            return\n        }\n\n        let onRefTargetDestroyedHookDisposer: IDisposer | undefined\n\n        // get rid of the watcher hook when the stored ref node is destroyed\n        // detached is ignored since scalar nodes (where the reference resides) cannot be detached\n        storedRefNode.registerHook(Hook.beforeDestroy, () => {\n            if (onRefTargetDestroyedHookDisposer) {\n                onRefTargetDestroyedHookDisposer()\n            }\n        })\n\n        const startWatching = (sync: boolean) => {\n            // re-create hook in case the stored ref gets reattached\n            if (onRefTargetDestroyedHookDisposer) {\n                onRefTargetDestroyedHookDisposer()\n            }\n\n            // make sure the target node is actually there and initialized\n            const storedRefParentNode = storedRefNode.parent\n            const storedRefParentValue = storedRefParentNode && storedRefParentNode.storedValue\n            if (storedRefParentNode && storedRefParentNode.isAlive && storedRefParentValue) {\n                let refTargetNodeExists: boolean\n                if (customGetSet) {\n                    refTargetNodeExists = !!customGetSet.get(identifier, storedRefParentValue)\n                } else {\n                    refTargetNodeExists = storedRefNode.root.identifierCache!.has(\n                        this.targetType,\n                        normalizeIdentifier(identifier)\n                    )\n                }\n\n                if (!refTargetNodeExists) {\n                    // we cannot change the reference in sync mode\n                    // since we are in the middle of a reconciliation/instantiation and the change would be overwritten\n                    // for those cases just let the wrong reference be assigned and fail upon usage\n                    // (like current references do)\n                    // this means that effectively this code will only run when it is created from a snapshot\n                    if (!sync) {\n                        this.fireInvalidated(\n                            \"invalidSnapshotReference\",\n                            storedRefNode,\n                            identifier,\n                            null\n                        )\n                    }\n                } else {\n                    onRefTargetDestroyedHookDisposer = this.addTargetNodeWatcher(\n                        storedRefNode,\n                        identifier\n                    )\n                }\n            }\n        }\n\n        if (storedRefNode.state === NodeLifeCycle.FINALIZED) {\n            // already attached, so the whole tree is ready\n            startWatching(true)\n        } else {\n            if (!storedRefNode.isRoot) {\n                // start watching once the whole tree is ready\n                storedRefNode.root.registerHook(Hook.afterCreationFinalization, () => {\n                    // make sure to attach it so it can start listening\n                    if (storedRefNode.parent) {\n                        storedRefNode.parent.createObservableInstanceIfNeeded()\n                    }\n                })\n            }\n            // start watching once the node is attached somewhere / parent changes\n            storedRefNode.registerHook(Hook.afterAttach, () => {\n                startWatching(false)\n            })\n        }\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class IdentifierReferenceType<IT extends IAnyComplexType> extends BaseReferenceType<IT> {\n    constructor(targetType: IT, onInvalidated?: OnReferenceInvalidated<ReferenceT<IT>>) {\n        super(targetType, onInvalidated)\n    }\n\n    getValue(storedRefNode: this[\"N\"]) {\n        if (!storedRefNode.isAlive) return undefined\n        const storedRef: StoredReference<IT> = storedRefNode.storedValue\n        return storedRef.resolvedValue as any\n    }\n\n    getSnapshot(storedRefNode: this[\"N\"]) {\n        const ref: StoredReference<IT> = storedRefNode.storedValue\n        return ref.identifier\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        const identifier = isStateTreeNode(initialValue)\n            ? getIdentifier(initialValue)!\n            : initialValue\n        const storedRef = new StoredReference(initialValue, this.targetType as any)\n        const storedRefNode: this[\"N\"] = createScalarNode(\n            this,\n            parent,\n            subpath,\n            environment,\n            storedRef as any\n        )\n        storedRef.node = storedRefNode\n        this.watchTargetNodeForInvalidations(storedRefNode, identifier as string, undefined)\n        return storedRefNode\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        if (!current.isDetaching && current.type === this) {\n            const compareByValue = isStateTreeNode(newValue)\n            const ref: StoredReference<IT> = current.storedValue\n            if (\n                (!compareByValue && ref.identifier === newValue) ||\n                (compareByValue && ref.resolvedValue === newValue)\n            ) {\n                current.setParent(parent, subpath)\n                return current\n            }\n        }\n        const newNode = this.instantiate(parent, subpath, undefined, newValue)\n        current.die() // noop if detaching\n        return newNode\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class CustomReferenceType<IT extends IAnyComplexType> extends BaseReferenceType<IT> {\n    constructor(\n        targetType: IT,\n        private readonly options: ReferenceOptionsGetSet<IT>,\n        onInvalidated?: OnReferenceInvalidated<ReferenceT<IT>>\n    ) {\n        super(targetType, onInvalidated)\n    }\n\n    getValue(storedRefNode: this[\"N\"]) {\n        if (!storedRefNode.isAlive) return undefined as any\n        const referencedNode = this.options.get(\n            storedRefNode.storedValue,\n            storedRefNode.parent ? storedRefNode.parent.storedValue : null\n        )\n        return referencedNode\n    }\n\n    getSnapshot(storedRefNode: this[\"N\"]) {\n        return storedRefNode.storedValue\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        newValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        const identifier = isStateTreeNode(newValue)\n            ? this.options.set(newValue as any, parent ? parent.storedValue : null)\n            : newValue\n        const storedRefNode: this[\"N\"] = createScalarNode(\n            this,\n            parent,\n            subpath,\n            environment,\n            identifier as any\n        )\n        this.watchTargetNodeForInvalidations(storedRefNode, identifier as string, this.options)\n        return storedRefNode\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        const newIdentifier = isStateTreeNode(newValue)\n            ? this.options.set(newValue as any, current ? current.storedValue : null)\n            : newValue\n        if (\n            !current.isDetaching &&\n            current.type === this &&\n            current.storedValue === newIdentifier\n        ) {\n            current.setParent(parent, subpath)\n            return current\n        }\n        const newNode = this.instantiate(parent, subpath, undefined, newIdentifier)\n        current.die() // noop if detaching\n        return newNode\n    }\n}\n\nexport interface ReferenceOptionsGetSet<IT extends IAnyComplexType> {\n    get(identifier: ReferenceIdentifier, parent: IAnyStateTreeNode | null): ReferenceT<IT>\n    set(value: ReferenceT<IT>, parent: IAnyStateTreeNode | null): ReferenceIdentifier\n}\n\nexport interface ReferenceOptionsOnInvalidated<IT extends IAnyComplexType> {\n    // called when the current reference is about to become invalid\n    onInvalidated: OnReferenceInvalidated<ReferenceT<IT>>\n}\n\nexport type ReferenceOptions<IT extends IAnyComplexType> =\n    | ReferenceOptionsGetSet<IT>\n    | ReferenceOptionsOnInvalidated<IT>\n    | (ReferenceOptionsGetSet<IT> & ReferenceOptionsOnInvalidated<IT>)\n\n/** @hidden */\nexport interface IReferenceType<IT extends IAnyComplexType>\n    extends IType<ReferenceIdentifier, ReferenceIdentifier, IT[\"TypeWithoutSTN\"]> {}\n\n/**\n * `types.reference` - Creates a reference to another type, which should have defined an identifier.\n * See also the [reference and identifiers](https://github.com/mobxjs/mobx-state-tree#references-and-identifiers) section.\n */\nexport function reference<IT extends IAnyComplexType>(\n    subType: IT,\n    options?: ReferenceOptions<IT>\n): IReferenceType<IT> {\n    assertIsType(subType, 1)\n    if (devMode()) {\n        if (arguments.length === 2 && typeof arguments[1] === \"string\") {\n            // istanbul ignore next\n            throw new MstError(\n                \"References with base path are no longer supported. Please remove the base path.\"\n            )\n        }\n    }\n\n    const getSetOptions = options ? (options as ReferenceOptionsGetSet<IT>) : undefined\n    const onInvalidated = options\n        ? (options as ReferenceOptionsOnInvalidated<IT>).onInvalidated\n        : undefined\n\n    if (getSetOptions && (getSetOptions.get || getSetOptions.set)) {\n        if (devMode()) {\n            if (!getSetOptions.get || !getSetOptions.set) {\n                throw new MstError(\n                    \"reference options must either contain both a 'get' and a 'set' method or none of them\"\n                )\n            }\n        }\n\n        return new CustomReferenceType(\n            subType,\n            {\n                get: getSetOptions.get,\n                set: getSetOptions.set\n            },\n            onInvalidated\n        )\n    } else {\n        return new IdentifierReferenceType(subType, onInvalidated)\n    }\n}\n\n/**\n * Returns if a given value represents a reference type.\n *\n * @param type\n * @returns\n */\nexport function isReferenceType(type: unknown): type is IReferenceType<IAnyComplexType> {\n    return isType(type) && (type.flags & TypeFlags.Reference) > 0\n}\n\nexport function safeReference<IT extends IAnyComplexType>(\n    subType: IT,\n    options: (ReferenceOptionsGetSet<IT> | {}) & {\n        acceptsUndefined: false\n        onInvalidated?: OnReferenceInvalidated<ReferenceT<IT>>\n    }\n): IReferenceType<IT>\nexport function safeReference<IT extends IAnyComplexType>(\n    subType: IT,\n    options?: (ReferenceOptionsGetSet<IT> | {}) & {\n        acceptsUndefined?: boolean\n        onInvalidated?: OnReferenceInvalidated<ReferenceT<IT>>\n    }\n): IMaybe<IReferenceType<IT>>\n/**\n * `types.safeReference` - A safe reference is like a standard reference, except that it accepts the undefined value by default\n * and automatically sets itself to undefined (when the parent is a model) / removes itself from arrays and maps\n * when the reference it is pointing to gets detached/destroyed.\n *\n * The optional options parameter object accepts a parameter named `acceptsUndefined`, which is set to true by default, so it is suitable\n * for model properties.\n * When used inside collections (arrays/maps), it is recommended to set this option to false so it can't take undefined as value,\n * which is usually the desired in those cases.\n * Additionally, the optional options parameter object accepts a parameter named `onInvalidated`, which will be called when the reference target node that the reference is pointing to is about to be detached/destroyed\n *\n * Strictly speaking it is a `types.maybe(types.reference(X))` (when `acceptsUndefined` is set to true, the default) and\n * `types.reference(X)` (when `acceptsUndefined` is set to false), both of them with a customized `onInvalidated` option.\n *\n * @param subType\n * @param options\n * @returns\n */\nexport function safeReference<IT extends IAnyComplexType>(\n    subType: IT,\n    options?: (ReferenceOptionsGetSet<IT> | {}) & {\n        acceptsUndefined?: boolean\n        onInvalidated?: OnReferenceInvalidated<ReferenceT<IT>>\n    }\n): IReferenceType<IT> | IMaybe<IReferenceType<IT>> {\n    const refType = reference(subType, {\n        ...options,\n        onInvalidated(ev) {\n            if (options && options.onInvalidated) {\n                options.onInvalidated(ev)\n            }\n            ev.removeRef()\n        }\n    })\n\n    if (options && options.acceptsUndefined === false) {\n        return refType\n    } else {\n        return maybe(refType)\n    }\n}\n"
  },
  {
    "path": "src/types/utility-types/refinement.ts",
    "content": "import {\n    isStateTreeNode,\n    getStateTreeNode,\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    isType,\n    TypeFlags,\n    IAnyType,\n    AnyObjectNode,\n    BaseType,\n    ExtractNodeType,\n    assertIsType,\n    devMode\n} from \"../../internal\"\nimport { assertIsString, assertIsFunction } from \"../../utils\"\n\nclass Refinement<IT extends IAnyType> extends BaseType<\n    IT[\"CreationType\"],\n    IT[\"SnapshotType\"],\n    IT[\"TypeWithoutSTN\"],\n    ExtractNodeType<IT>\n> {\n    get flags() {\n        return this._subtype.flags | TypeFlags.Refinement\n    }\n\n    constructor(\n        name: string,\n        private readonly _subtype: IT,\n        private readonly _predicate: (v: IT[\"CreationType\"]) => boolean,\n        private readonly _message: (v: IT[\"CreationType\"]) => string\n    ) {\n        super(name)\n    }\n\n    describe() {\n        return this.name\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        // create the child type\n        return this._subtype.instantiate(parent, subpath, environment, initialValue) as any\n    }\n\n    isAssignableFrom(type: IAnyType) {\n        return this._subtype.isAssignableFrom(type)\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        const subtypeErrors = this._subtype.validate(value, context)\n        if (subtypeErrors.length > 0) return subtypeErrors\n\n        const snapshot = isStateTreeNode(value) ? getStateTreeNode(value).snapshot : value\n\n        if (!this._predicate(snapshot)) {\n            return typeCheckFailure(context, value, this._message(value))\n        }\n\n        return typeCheckSuccess()\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        return this._subtype.reconcile(current, newValue, parent, subpath) as any\n    }\n\n    getSubTypes() {\n        return this._subtype\n    }\n}\n\nexport function refinement<IT extends IAnyType>(\n    name: string,\n    type: IT,\n    predicate: (snapshot: IT[\"CreationType\"]) => boolean,\n    message?: string | ((v: IT[\"CreationType\"]) => string)\n): IT\nexport function refinement<IT extends IAnyType>(\n    type: IT,\n    predicate: (snapshot: IT[\"CreationType\"]) => boolean,\n    message?: string | ((v: IT[\"CreationType\"]) => string)\n): IT\n\n/**\n * `types.refinement` - Creates a type that is more specific than the base type, e.g. `types.refinement(types.string, value => value.length > 5)` to create a type of strings that can only be longer then 5.\n *\n * @param name\n * @param type\n * @param predicate\n * @returns\n */\nexport function refinement(...args: any[]): IAnyType {\n    const name = typeof args[0] === \"string\" ? args.shift() : isType(args[0]) ? args[0].name : null\n    const type = args[0]\n    const predicate = args[1]\n    const message = args[2]\n        ? args[2]\n        : (v: any) => \"Value does not respect the refinement predicate\"\n    // ensures all parameters are correct\n    assertIsType(type, [1, 2])\n    assertIsString(name, 1)\n    assertIsFunction(predicate, [2, 3])\n    assertIsFunction(message, [3, 4])\n\n    return new Refinement(name, type, predicate, message)\n}\n\n/**\n * Returns if a given value is a refinement type.\n *\n * @param type\n * @returns\n */\nexport function isRefinementType(type: unknown): type is IAnyType {\n    return isType(type) && (type.flags & TypeFlags.Refinement) > 0\n}\n"
  },
  {
    "path": "src/types/utility-types/snapshotProcessor.ts",
    "content": "import {\n    IType,\n    IAnyType,\n    BaseType,\n    isStateTreeNode,\n    IValidationContext,\n    IValidationResult,\n    AnyObjectNode,\n    TypeFlags,\n    ExtractNodeType,\n    assertIsType,\n    isType,\n    getSnapshot,\n    devMode,\n    ComplexType,\n    typeCheckFailure,\n    isUnionType,\n    Instance,\n    ObjectNode,\n    MstError\n} from \"../../internal\"\n\n/** @hidden */\ndeclare const $mstNotCustomized: unique symbol\n\n/** @hidden */\nconst $preProcessorFailed: unique symbol = Symbol(\"$preProcessorFailed\")\n\n/** @hidden */\n// tslint:disable-next-line:class-name\nexport interface _NotCustomized {\n    // only for typings\n    readonly [$mstNotCustomized]: undefined\n}\n\n/** @hidden */\nexport type _CustomOrOther<Custom, Other> = Custom extends _NotCustomized ? Other : Custom\n\nclass SnapshotProcessor<IT extends IAnyType, CustomC, CustomS> extends BaseType<\n    _CustomOrOther<CustomC, IT[\"CreationType\"]>,\n    _CustomOrOther<CustomS, IT[\"SnapshotType\"]>,\n    IT[\"TypeWithoutSTN\"],\n    ExtractNodeType<IT>\n> {\n    get flags() {\n        return this._subtype.flags | TypeFlags.SnapshotProcessor\n    }\n\n    constructor(\n        private readonly _subtype: IT,\n        private readonly _processors: ISnapshotProcessors<IT, CustomC, CustomS>,\n        name?: string\n    ) {\n        super(name || _subtype.name)\n    }\n\n    describe() {\n        return `snapshotProcessor(${this._subtype.describe()})`\n    }\n\n    private preProcessSnapshot(sn: this[\"C\"]): IT[\"CreationType\"] {\n        if (this._processors.preProcessor) {\n            return this._processors.preProcessor.call(null, sn)\n        }\n        return sn as any\n    }\n\n    private preProcessSnapshotSafe(sn: this[\"C\"]): IT[\"CreationType\"] | typeof $preProcessorFailed {\n        try {\n            return this.preProcessSnapshot(sn)\n        } catch (e) {\n            return $preProcessorFailed\n        }\n    }\n\n    private postProcessSnapshot(sn: IT[\"SnapshotType\"], node: this[\"N\"]): this[\"S\"] {\n        if (this._processors.postProcessor) {\n            return this._processors.postProcessor!.call(null, sn, node.storedValue) as any\n        }\n        return sn\n    }\n\n    private _fixNode(node: this[\"N\"]): void {\n        // the node has to use these methods rather than the original type ones\n        proxyNodeTypeMethods(node.type, this, \"create\")\n\n        if (node instanceof ObjectNode) {\n            node.hasSnapshotPostProcessor = !!this._processors.postProcessor\n        }\n        const oldGetSnapshot = node.getSnapshot\n        node.getSnapshot = () => this.postProcessSnapshot(oldGetSnapshot.call(node), node) as any\n        if (!isUnionType(this._subtype)) {\n            node.getReconciliationType = () => {\n                return this\n            }\n        }\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        const processedInitialValue = isStateTreeNode(initialValue)\n            ? initialValue\n            : this.preProcessSnapshot(initialValue)\n        const node = this._subtype.instantiate(\n            parent,\n            subpath,\n            environment,\n            processedInitialValue\n        ) as any\n        this._fixNode(node)\n        return node\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        const node = this._subtype.reconcile(\n            current,\n            isStateTreeNode(newValue) ? newValue : this.preProcessSnapshot(newValue),\n            parent,\n            subpath\n        ) as any\n        if (node !== current) {\n            this._fixNode(node)\n        }\n        return node\n    }\n\n    getSnapshot(node: this[\"N\"], applyPostProcess: boolean = true): this[\"S\"] {\n        const sn = this._subtype.getSnapshot(node)\n        return applyPostProcess ? this.postProcessSnapshot(sn, node) : sn\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        const processedSn = this.preProcessSnapshotSafe(value)\n        if (processedSn === $preProcessorFailed) {\n            return typeCheckFailure(context, value, \"Failed to preprocess value\")\n        }\n        return this._subtype.validate(processedSn, context)\n    }\n\n    getSubTypes() {\n        return this._subtype\n    }\n\n    /**\n     * MST considers a given value to \"be\" of a subtype is the value is either:\n     *\n     * 1. And instance of the subtype\n     * 2. A valid snapshot *in* of the subtype\n     *\n     * Before v7, we used to also consider processed models (as in, SnapshotOut values of this).\n     * This is no longer the case, and is more in line with our overall \"is\" philosophy, which you can\n     * see in `src/core/type/type.ts:104` (assuming lines don't change too much).\n     *\n     * For additonal commentary, see discussion in https://github.com/mobxjs/mobx-state-tree/pull/2182\n     *\n     * The `is` function specifically checks for `SnapshotIn` or `Instance` of a given type.\n     *\n     * @param thing\n     * @returns\n     */\n    is(thing: any): thing is any {\n        const value = isType(thing)\n            ? this._subtype\n            : isStateTreeNode(thing)\n              ? thing\n              : this.preProcessSnapshotSafe(thing)\n        if (value === $preProcessorFailed) {\n            return false\n        }\n        return this._subtype.validate(value, [{ path: \"\", type: this._subtype }]).length === 0\n    }\n\n    isAssignableFrom(type: IAnyType): boolean {\n        return this._subtype.isAssignableFrom(type)\n    }\n\n    isMatchingSnapshotId(current: this[\"N\"], snapshot: this[\"C\"]): boolean {\n        if (!(this._subtype instanceof ComplexType)) {\n            return false\n        }\n        const processedSn = this.preProcessSnapshot(snapshot)\n        return this._subtype.isMatchingSnapshotId(current as any, processedSn)\n    }\n}\n\nfunction proxyNodeTypeMethods(\n    nodeType: any,\n    snapshotProcessorType: any,\n    ...methods: (keyof SnapshotProcessor<any, any, any>)[]\n) {\n    for (const method of methods) {\n        nodeType[method] = snapshotProcessorType[method].bind(snapshotProcessorType)\n    }\n}\n\n// public API\n\n/**\n * A type that has its snapshots processed.\n */\nexport interface ISnapshotProcessor<IT extends IAnyType, CustomC, CustomS>\n    extends IType<\n        _CustomOrOther<CustomC, IT[\"CreationType\"]>,\n        _CustomOrOther<CustomS, IT[\"SnapshotType\"]>,\n        IT[\"TypeWithoutSTN\"]\n    > {}\n\n/**\n * Snapshot processors.\n */\nexport interface ISnapshotProcessors<IT extends IAnyType, CustomC, CustomS> {\n    /**\n     * Function that transforms an input snapshot.\n     */\n    preProcessor?(snapshot: _CustomOrOther<CustomC, IT[\"CreationType\"]>): IT[\"CreationType\"]\n\n    /**\n     * Function that transforms an output snapshot.\n     * @param snapshot\n     */\n    postProcessor?(\n        snapshot: IT[\"SnapshotType\"],\n        node: Instance<IT>\n    ): _CustomOrOther<CustomS, IT[\"SnapshotType\"]>\n}\n\n/**\n * `types.snapshotProcessor` - Runs a pre/post snapshot processor before/after serializing a given type.\n *\n * [See known issue with `applySnapshot` and `preProcessSnapshot`](https://github.com/mobxjs/mobx-state-tree/issues/1317)\n *\n * Example:\n * ```ts\n * const Todo1 = types.model({ text: types.string })\n * // in the backend the text type must be null when empty\n * interface BackendTodo {\n *     text: string | null\n * }\n *\n * const Todo2 = types.snapshotProcessor(Todo1, {\n *     // from snapshot to instance\n *     preProcessor(snapshot: BackendTodo) {\n *         return {\n *             text: sn.text || \"\";\n *         }\n *     },\n *\n *     // from instance to snapshot\n *     postProcessor(snapshot, node): BackendTodo {\n *         return {\n *             text: !sn.text ? null : sn.text\n *         }\n *     }\n * })\n * ```\n *\n * @param type Type to run the processors over.\n * @param processors Processors to run.\n * @param name Type name, or undefined to inherit the inner type one.\n * @returns\n */\nexport function snapshotProcessor<\n    IT extends IAnyType,\n    CustomC = _NotCustomized,\n    CustomS = _NotCustomized\n>(\n    type: IT,\n    processors: ISnapshotProcessors<IT, CustomC, CustomS>,\n    name?: string\n): ISnapshotProcessor<IT, CustomC, CustomS> {\n    assertIsType(type, 1)\n    if (devMode()) {\n        if (processors.postProcessor && typeof processors.postProcessor !== \"function\") {\n            // istanbul ignore next\n            throw new MstError(\"postSnapshotProcessor must be a function\")\n        }\n        if (processors.preProcessor && typeof processors.preProcessor !== \"function\") {\n            // istanbul ignore next\n            throw new MstError(\"preSnapshotProcessor must be a function\")\n        }\n    }\n\n    return new SnapshotProcessor(type, processors, name)\n}\n"
  },
  {
    "path": "src/types/utility-types/union.ts",
    "content": "import {\n    IValidationContext,\n    IValidationResult,\n    typeCheckSuccess,\n    typeCheckFailure,\n    flattenTypeErrors,\n    isType,\n    TypeFlags,\n    IType,\n    MstError,\n    isPlainObject,\n    IAnyType,\n    IValidationError,\n    _NotCustomized,\n    AnyObjectNode,\n    BaseType,\n    devMode,\n    assertIsType,\n    assertArg\n} from \"../../internal\"\n\nexport type ITypeDispatcher<Types extends IAnyType[]> = (\n    snapshot: Types[number][\"SnapshotType\"]\n) => Types[number]\n\nexport interface UnionOptions<Types extends IAnyType[]> {\n    /**\n     * Whether or not to use eager validation.\n     *\n     * When `true`, the first matching type will be used. Otherwise, all types will be checked and the\n     * validation will pass if and only if a single type matches.\n     */\n    eager?: boolean\n\n    /**\n     * A function that returns the type to be used given an input snapshot.\n     */\n    dispatcher?: ITypeDispatcher<Types>\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class Union<Types extends IAnyType[]> extends BaseType<\n    _CustomCSProcessor<Types[number][\"CreationType\"]>,\n    _CustomCSProcessor<Types[number][\"SnapshotType\"]>,\n    Types[number][\"TypeWithoutSTN\"]\n> {\n    private readonly _dispatcher?: ITypeDispatcher<Types>\n    private readonly _eager: boolean = true\n\n    get flags() {\n        let result: TypeFlags = TypeFlags.Union\n\n        this._types.forEach(type => {\n            result |= type.flags\n        })\n\n        return result\n    }\n\n    constructor(\n        name: string,\n        private readonly _types: Types,\n        options?: UnionOptions<Types>\n    ) {\n        super(name)\n        options = {\n            eager: true,\n            dispatcher: undefined,\n            ...options\n        }\n        this._dispatcher = options.dispatcher\n        if (!options.eager) this._eager = false\n    }\n\n    isAssignableFrom(type: IAnyType) {\n        return this._types.some(subType => subType.isAssignableFrom(type))\n    }\n\n    describe() {\n        return \"(\" + this._types.map(factory => factory.describe()).join(\" | \") + \")\"\n    }\n\n    instantiate(\n        parent: AnyObjectNode | null,\n        subpath: string,\n        environment: any,\n        initialValue: this[\"C\"] | this[\"T\"]\n    ): this[\"N\"] {\n        const type = this.determineType(initialValue, undefined)\n        if (!type) throw new MstError(\"No matching type for union \" + this.describe()) // can happen in prod builds\n        return type.instantiate(parent, subpath, environment, initialValue)\n    }\n\n    reconcile(\n        current: this[\"N\"],\n        newValue: this[\"C\"] | this[\"T\"],\n        parent: AnyObjectNode,\n        subpath: string\n    ): this[\"N\"] {\n        const type = this.determineType(newValue, current.getReconciliationType())\n        if (!type) throw new MstError(\"No matching type for union \" + this.describe()) // can happen in prod builds\n        return type.reconcile(current, newValue, parent, subpath)\n    }\n\n    determineType(\n        value: this[\"C\"] | this[\"T\"],\n        reconcileCurrentType: IAnyType | undefined\n    ): IAnyType | undefined {\n        // try the dispatcher, if defined\n        if (this._dispatcher) {\n            return this._dispatcher(value)\n        }\n\n        // find the most accommodating type\n        // if we are using reconciliation try the current node type first (fix for #1045)\n        if (reconcileCurrentType) {\n            if (reconcileCurrentType.is(value)) {\n                return reconcileCurrentType\n            }\n            return this._types.filter(t => t !== reconcileCurrentType).find(type => type.is(value))\n        } else {\n            return this._types.find(type => type.is(value))\n        }\n    }\n\n    isValidSnapshot(value: this[\"C\"], context: IValidationContext): IValidationResult {\n        if (this._dispatcher) {\n            return this._dispatcher(value).validate(value, context)\n        }\n\n        const allErrors: IValidationError[][] = []\n        let applicableTypes = 0\n        for (let i = 0; i < this._types.length; i++) {\n            const type = this._types[i]\n            const errors = type.validate(value, context)\n            if (errors.length === 0) {\n                if (this._eager) return typeCheckSuccess()\n                else applicableTypes++\n            } else {\n                allErrors.push(errors)\n            }\n        }\n\n        if (applicableTypes === 1) return typeCheckSuccess()\n        return typeCheckFailure(context, value, \"No type is applicable for the union\").concat(\n            flattenTypeErrors(allErrors)\n        )\n    }\n\n    getSubTypes() {\n        return this._types\n    }\n}\n\n/**\n * Transform _NotCustomized | _NotCustomized... to _NotCustomized, _NotCustomized | A | B to A | B\n * @hidden\n */\nexport type _CustomCSProcessor<T> =\n    Exclude<T, _NotCustomized> extends never ? _NotCustomized : Exclude<T, _NotCustomized>\n\n/** @hidden */\nexport interface ITypeUnion<C, S, T>\n    extends IType<_CustomCSProcessor<C>, _CustomCSProcessor<S>, T> {}\n\nexport type IUnionType<Types extends IAnyType[]> = ITypeUnion<\n    Types[number][\"CreationType\"],\n    Types[number][\"SnapshotType\"],\n    Types[number][\"TypeWithoutSTN\"]\n>\n\nexport function union<Types extends IAnyType[]>(...types: Types): IUnionType<Types>\nexport function union<Types extends IAnyType[]>(\n    options: UnionOptions<Types>,\n    ...types: Types\n): IUnionType<Types>\n/**\n * `types.union` - Create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function of the form `(snapshot) => Type`.\n *\n * @param optionsOrType\n * @param otherTypes\n * @returns\n */\nexport function union<Types extends IAnyType[]>(\n    optionsOrType: UnionOptions<Types> | Types[number],\n    ...otherTypes: Types\n): IUnionType<Types> {\n    const options = isType(optionsOrType) ? undefined : optionsOrType\n    const types = (isType(optionsOrType) ? [optionsOrType, ...otherTypes] : otherTypes) as Types\n    const name = \"(\" + types.map(type => type.name).join(\" | \") + \")\"\n\n    // check all options\n    if (devMode()) {\n        if (options) {\n            assertArg(\n                options,\n                o => isPlainObject(o),\n                \"object { eager?: boolean, dispatcher?: Function }\",\n                1\n            )\n        }\n        types.forEach((type, i) => {\n            assertIsType(type, options ? i + 2 : i + 1)\n        })\n    }\n    return new Union(name, types, options)\n}\n\n/**\n * Returns if a given value represents a union type.\n *\n * @param type\n * @returns\n */\nexport function isUnionType(type: unknown): type is IUnionType<IAnyType[]> {\n    return isType(type) && (type.flags & TypeFlags.Union) > 0\n}\n"
  },
  {
    "path": "src/utils.ts",
    "content": "import {\n    isObservableArray,\n    isObservableObject,\n    _getGlobalState,\n    defineProperty as mobxDefineProperty\n} from \"mobx\"\nimport { Primitives } from \"./core/type/type\"\n\nconst plainObjectString = Object.toString()\n\n/**\n * @internal\n * @hidden\n */\nexport const EMPTY_ARRAY: ReadonlyArray<any> = Object.freeze([])\n\n/**\n * @internal\n * @hidden\n */\nexport const EMPTY_OBJECT: {} = Object.freeze({})\n\n/**\n * @internal\n * @hidden\n */\nexport const mobxShallow = _getGlobalState().useProxies\n    ? { deep: false }\n    : { deep: false, proxy: false }\nObject.freeze(mobxShallow)\n\n/**\n * A generic disposer.\n */\nexport type IDisposer = () => void\n\n/**\n * @internal\n * @hidden\n */\nexport class MstError extends Error {\n    constructor(message = \"Illegal state\") {\n        super(`[mobx-state-tree] ${message}`)\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function identity(_: any): any {\n    return _\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function noop() {}\n\n/**\n * @internal\n * @hidden\n */\nexport const isInteger = Number.isInteger\n\n/**\n * @internal\n * @hidden\n */\nexport function isFloat(val: any) {\n    return Number(val) === val && val % 1 !== 0\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isFinite(val: any) {\n    return Number.isFinite(val)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isArray(val: any): val is any[] {\n    return Array.isArray(val) || isObservableArray(val)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function asArray<T>(val: undefined | null | T | T[] | ReadonlyArray<T>): T[] {\n    if (!val) return EMPTY_ARRAY as any as T[]\n    if (isArray(val)) return val as T[]\n    return [val] as T[]\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function extend<A, B>(a: A, b: B): A & B\n/**\n * @internal\n * @hidden\n */\nexport function extend<A, B, C>(a: A, b: B, c: C): A & B & C\n/**\n * @internal\n * @hidden\n */\nexport function extend<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D\n/**\n * @internal\n * @hidden\n */\nexport function extend(a: any, ...b: any[]): any\n/**\n * @internal\n * @hidden\n */\nexport function extend(a: any, ...b: any[]) {\n    for (let i = 0; i < b.length; i++) {\n        const current = b[i]\n        for (let key in current) a[key] = current[key]\n    }\n    return a\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isPlainObject(value: any): value is { [k: string]: any } {\n    if (value === null || typeof value !== \"object\") return false\n    const proto = Object.getPrototypeOf(value)\n    if (proto == null) return true\n    return proto.constructor?.toString() === plainObjectString\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isMutable(value: any) {\n    return (\n        value !== null &&\n        typeof value === \"object\" &&\n        !(value instanceof Date) &&\n        !(value instanceof RegExp)\n    )\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isPrimitive(value: any, includeDate = true): value is Primitives {\n    return (\n        value === null ||\n        value === undefined ||\n        typeof value === \"string\" ||\n        typeof value === \"number\" ||\n        typeof value === \"boolean\" ||\n        typeof value === \"bigint\" ||\n        (includeDate && value instanceof Date)\n    )\n}\n\n/**\n * @internal\n * @hidden\n * Freeze a value and return it (if not in production)\n */\nexport function freeze<T>(value: T): T {\n    if (!devMode()) return value\n    return isPrimitive(value) || isObservableArray(value) ? value : Object.freeze(value)\n}\n\n/**\n * @internal\n * @hidden\n * Recursively freeze a value (if not in production)\n */\nexport function deepFreeze<T>(value: T): T {\n    if (!devMode()) return value\n    freeze(value)\n\n    if (isPlainObject(value)) {\n        Object.keys(value).forEach(propKey => {\n            if (\n                !isPrimitive((value as any)[propKey]) &&\n                !Object.isFrozen((value as any)[propKey])\n            ) {\n                deepFreeze((value as any)[propKey])\n            }\n        })\n    }\n\n    return value\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function isSerializable(value: any) {\n    return typeof value !== \"function\"\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function defineProperty(object: any, key: PropertyKey, descriptor: PropertyDescriptor) {\n    isObservableObject(object)\n        ? mobxDefineProperty(object, key, descriptor)\n        : Object.defineProperty(object, key, descriptor)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function addHiddenFinalProp(object: any, propName: string, value: any) {\n    defineProperty(object, propName, {\n        enumerable: false,\n        writable: false,\n        configurable: true,\n        value\n    })\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function addHiddenWritableProp(object: any, propName: string, value: any) {\n    defineProperty(object, propName, {\n        enumerable: false,\n        writable: true,\n        configurable: true,\n        value\n    })\n}\n\n/**\n * @internal\n * @hidden\n */\nexport type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never\n\n/**\n * @internal\n * @hidden\n */\nclass EventHandler<F extends Function> {\n    private handlers: F[] = []\n\n    get hasSubscribers(): boolean {\n        return this.handlers.length > 0\n    }\n\n    register(fn: F, atTheBeginning = false): IDisposer {\n        if (atTheBeginning) {\n            this.handlers.unshift(fn)\n        } else {\n            this.handlers.push(fn)\n        }\n        return () => {\n            this.unregister(fn)\n        }\n    }\n\n    has(fn: F): boolean {\n        return this.handlers.indexOf(fn) >= 0\n    }\n\n    unregister(fn: F) {\n        const index = this.handlers.indexOf(fn)\n        if (index >= 0) {\n            this.handlers.splice(index, 1)\n        }\n    }\n\n    clear() {\n        this.handlers.length = 0\n    }\n\n    emit(...args: ArgumentTypes<F>) {\n        // make a copy just in case it changes\n        const handlers = this.handlers.slice()\n        handlers.forEach(f => f(...args))\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport class EventHandlers<E extends { [k: string]: Function }> {\n    private eventHandlers?: { [k in keyof E]?: EventHandler<Function> }\n\n    hasSubscribers(event: keyof E): boolean {\n        const handler = this.eventHandlers && this.eventHandlers[event]\n        return !!handler && handler!.hasSubscribers\n    }\n\n    register<N extends keyof E>(event: N, fn: E[N], atTheBeginning = false): IDisposer {\n        if (!this.eventHandlers) {\n            this.eventHandlers = {}\n        }\n        let handler = this.eventHandlers[event]\n        if (!handler) {\n            handler = this.eventHandlers[event] = new EventHandler()\n        }\n        return handler.register(fn, atTheBeginning)\n    }\n\n    has<N extends keyof E>(event: N, fn: E[N]): boolean {\n        const handler = this.eventHandlers && this.eventHandlers[event]\n        return !!handler && handler!.has(fn)\n    }\n\n    unregister<N extends keyof E>(event: N, fn: E[N]) {\n        const handler = this.eventHandlers && this.eventHandlers[event]\n        if (handler) {\n            handler!.unregister(fn)\n        }\n    }\n\n    clear<N extends keyof E>(event: N) {\n        if (this.eventHandlers) {\n            delete this.eventHandlers[event]\n        }\n    }\n\n    clearAll() {\n        this.eventHandlers = undefined\n    }\n\n    emit<N extends keyof E>(event: N, ...args: ArgumentTypes<E[N]>) {\n        const handler = this.eventHandlers && this.eventHandlers[event]\n        if (handler) {\n            ;(handler!.emit as any)(...args)\n        }\n    }\n}\n\nconst prototypeHasOwnProperty = Object.prototype.hasOwnProperty\n\n/**\n * @internal\n * @hidden\n */\nexport function hasOwnProperty(object: Object, propName: string) {\n    return prototypeHasOwnProperty.call(object, propName)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function argsToArray(args: IArguments): any[] {\n    const res = new Array(args.length)\n    for (let i = 0; i < args.length; i++) res[i] = args[i]\n    return res\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function stringStartsWith(str: string, beginning: string) {\n    return str.indexOf(beginning) === 0\n}\n\n/**\n * @internal\n * @hidden\n */\nexport type DeprecatedFunction = Function & { ids?: { [id: string]: true } }\n\n/**\n * @internal\n * @hidden\n */\nexport const deprecated: DeprecatedFunction = function (id: string, message: string): void {\n    // skip if running production\n    if (!devMode()) return\n    // warn if hasn't been warned before\n    if (deprecated.ids && !deprecated.ids.hasOwnProperty(id)) {\n        warnError(\"Deprecation warning: \" + message)\n    }\n    // mark as warned to avoid duplicate warn message\n    if (deprecated.ids) deprecated.ids[id] = true\n}\ndeprecated.ids = {}\n\n/**\n * @internal\n * @hidden\n */\nexport function warnError(msg: string) {\n    console.warn(new Error(`[mobx-state-tree] ${msg}`))\n}\n/**\n * @internal\n * @hidden\n */\nexport function isTypeCheckingEnabled() {\n    return (\n        devMode() ||\n        (typeof process !== \"undefined\" && process.env && process.env.ENABLE_TYPE_CHECK === \"true\")\n    )\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function devMode() {\n    return process.env.NODE_ENV !== \"production\"\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertArg<T>(\n    value: T,\n    fn: (value: T) => boolean,\n    typeName: string,\n    argNumber: number | number[]\n) {\n    if (devMode()) {\n        if (!fn(value)) {\n            // istanbul ignore next\n            throw new MstError(\n                `expected ${typeName} as argument ${asArray(argNumber).join(\" or \")}, got ${value} instead`\n            )\n        }\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertIsFunction(value: Function, argNumber: number | number[]) {\n    assertArg(value, fn => typeof fn === \"function\", \"function\", argNumber)\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertIsNumber(\n    value: number,\n    argNumber: number | number[],\n    min?: number,\n    max?: number\n) {\n    assertArg(value, n => typeof n === \"number\", \"number\", argNumber)\n    if (min !== undefined) {\n        assertArg(value, n => n >= min, `number greater than ${min}`, argNumber)\n    }\n    if (max !== undefined) {\n        assertArg(value, n => n <= max, `number lesser than ${max}`, argNumber)\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function assertIsString(value: string, argNumber: number | number[], canBeEmpty = true) {\n    assertArg(value, s => typeof s === \"string\", \"string\", argNumber)\n    if (!canBeEmpty) {\n        assertArg(value, s => s !== \"\", \"not empty string\", argNumber)\n    }\n}\n\n/**\n * @internal\n * @hidden\n */\nexport function setImmediateWithFallback(fn: (...args: any[]) => void) {\n    if (typeof queueMicrotask === \"function\") {\n        queueMicrotask(fn)\n    } else if (typeof setImmediate === \"function\") {\n        setImmediate(fn)\n    } else {\n        setTimeout(fn, 1)\n    }\n}\n"
  },
  {
    "path": "test-results/.gitkeep",
    "content": ""
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"outDir\": \"lib/\",\n    \"target\": \"es5\",\n    \"sourceMap\": false,\n    \"declaration\": true,\n    \"module\": \"es2015\",\n    \"removeComments\": false,\n    \"moduleResolution\": \"node\",\n    \"experimentalDecorators\": true,\n    \"strict\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"noImplicitAny\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noImplicitReturns\": true,\n    \"noImplicitThis\": true,\n    \"importHelpers\": true,\n    \"stripInternal\": true,\n    \"downlevelIteration\": true,\n    \"lib\": [\"es6\"],\n    \"useDefineForClassFields\": true,\n    \"isolatedModules\": true,\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"**/*.js\", \"**/*.ts\"]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n    \"extends\": [\"tslint-config-prettier\"],\n    \"rules\": {\n        \"class-name\": true,\n        \"comment-format\": [true, \"check-space\"],\n        \"curly\": false,\n        \"indent\": [true, \"spaces\"],\n        \"interface-name\": false,\n        \"jsdoc-format\": true,\n        \"no-consecutive-blank-lines\": true,\n        \"no-debugger\": true,\n        \"no-duplicate-variable\": true,\n        \"no-eval\": true,\n        \"no-internal-module\": true,\n        \"no-shadowed-variable\": true,\n        \"no-switch-case-fall-through\": true,\n        \"no-unused-expression\": true,\n        \"no-use-before-declare\": false,\n        \"no-var-keyword\": true,\n        \"one-line\": [true, \"check-open-brace\", \"check-whitespace\", \"check-catch\"],\n        \"trailing-comma\": false,\n        \"triple-equals\": [true, \"allow-null-check\"],\n        \"variable-name\": [true, \"ban-keywords\"]\n    }\n}\n"
  },
  {
    "path": "typedocconfig.js",
    "content": "module.exports = {\n    src: [\"src/index.ts\"],\n    module: \"commonjs\",\n    excludeNotExported: true,\n    excludePrivate: true,\n    excludeProtected: true,\n    mode: \"file\",\n    readme: \"none\",\n    out: \"./docs/API\",\n    theme: \"docusaurus\",\n    tsconfig: \"tsconfig.json\",\n    listInvalidSymbolLinks: true,\n    mdHideSources: true\n    // Note: TypeDoc uses the current git remote (e.g. your fork) for \"Defined in\" source links.\n    // After generation, scripts/fix-docs-source-links.js rewrites them to https://github.com/mobxjs/mobx-state-tree/\n}\n"
  },
  {
    "path": "website/core/Footer.js",
    "content": "/**\n * Copyright (c) 2017-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nconst React = require(\"react\")\n\nclass Footer extends React.Component {\n    docUrl(doc, language) {\n        const baseUrl = this.props.config.baseUrl\n        const docsUrl = this.props.config.docsUrl\n        const docsPart = `${docsUrl ? `${docsUrl}/` : \"\"}`\n        const langPart = `${language ? `${language}/` : \"\"}`\n        return `${baseUrl}${docsPart}${langPart}${doc}`\n    }\n\n    pageUrl(doc, language) {\n        const baseUrl = this.props.config.baseUrl\n        return baseUrl + (language ? `${language}/` : \"\") + doc\n    }\n\n    render() {\n        return (\n            <footer className=\"nav-footer\" id=\"footer\">\n                <section className=\"sitemap\">\n                    <a href={this.props.config.baseUrl} className=\"nav-home\">\n                        {this.props.config.footerIcon && (\n                            <img\n                                src={this.props.config.baseUrl + this.props.config.footerIcon}\n                                alt={this.props.config.title}\n                                width=\"66\"\n                                height=\"58\"\n                            />\n                        )}\n                    </a>\n                    <div>\n                        <h5>Docs</h5>\n                        <a href={this.docUrl(\"intro/getting-started\")}>\n                            Getting Started\n                        </a>\n                        <a href={this.docUrl(\"concepts/trees\")}>\n                            Basic concepts\n                        </a>\n                        <a href={this.docUrl(\"concepts/patches\")}>\n                            Advanced concepts\n                        </a>\n                        <a href={this.docUrl(\"overview/types\")}>\n                            API Reference\n                        </a>\n                    </div>\n                    <div>\n                        <h5>Community</h5>\n                        <a\n                            href=\"https://github.com/mobxjs/mobx-state-tree/discussions\"\n                            target=\"_blank\"\n                            rel=\"noreferrer noopener\"\n                        >\n                            GitHub Discussions\n                        </a>\n\n                        <a\n                            href=\"https://stackoverflow.com/questions/tagged/mobx-state-tree\"\n                            target=\"_blank\"\n                            rel=\"noreferrer noopener\"\n                        >\n                            Stack Overflow\n                        </a>\n                    </div>\n                    <div>\n                        <h5>More</h5>\n                        <a href={`${this.props.config.baseUrl}blog`}>Blog</a>\n                        <a href=\"https://github.com/mobxjs/mobx-state-tree\">GitHub</a>\n                        <a\n                            className=\"github-button\"\n                            href={this.props.config.repoUrl}\n                            data-icon=\"octicon-star\"\n                            data-count-href=\"/facebook/docusaurus/stargazers\"\n                            data-show-count=\"true\"\n                            data-count-aria-label=\"# stargazers on GitHub\"\n                            aria-label=\"Star this project on GitHub\"\n                        >\n                            Star\n                        </a>\n\n                    </div>\n                </section>\n\n                <a\n                    href=\"https://opensource.facebook.com/\"\n                    target=\"_blank\"\n                    rel=\"noreferrer noopener\"\n                    className=\"fbOpenSource\"\n                >\n                    <img\n                        src={`${this.props.config.baseUrl}img/oss_logo.png`}\n                        alt=\"Facebook Open Source\"\n                        width=\"170\"\n                        height=\"45\"\n                    />\n                </a>\n                <section className=\"copyright\">{this.props.config.copyright}</section>\n            </footer>\n        )\n    }\n}\n\nmodule.exports = Footer\n"
  },
  {
    "path": "website/i18n/en.json",
    "content": "{\n  \"_comment\": \"This file is auto-generated by write-translations.js\",\n  \"localized-strings\": {\n    \"next\": \"Next\",\n    \"previous\": \"Previous\",\n    \"tagline\": \"Opinionated, transactional, MobX powered state container combining the best features of the immutable and mutable world for an optimal DX\",\n    \"docs\": {\n      \"API_header\": {\n        \"title\": \"API_header\"\n      },\n      \"API/index\": {\n        \"title\": \"mobx-state-tree - v7.0.2\",\n        \"sidebar_label\": \"Globals\"\n      },\n      \"API/interfaces/customtypeoptions\": {\n        \"title\": \"CustomTypeOptions\",\n        \"sidebar_label\": \"CustomTypeOptions\"\n      },\n      \"API/interfaces/functionwithflag\": {\n        \"title\": \"FunctionWithFlag\",\n        \"sidebar_label\": \"FunctionWithFlag\"\n      },\n      \"API/interfaces/iactioncontext\": {\n        \"title\": \"IActionContext\",\n        \"sidebar_label\": \"IActionContext\"\n      },\n      \"API/interfaces/iactionrecorder\": {\n        \"title\": \"IActionRecorder\",\n        \"sidebar_label\": \"IActionRecorder\"\n      },\n      \"API/interfaces/iactiontrackingmiddleware2call\": {\n        \"title\": \"IActionTrackingMiddleware2Call\",\n        \"sidebar_label\": \"IActionTrackingMiddleware2Call\"\n      },\n      \"API/interfaces/iactiontrackingmiddleware2hooks\": {\n        \"title\": \"IActionTrackingMiddleware2Hooks\",\n        \"sidebar_label\": \"IActionTrackingMiddleware2Hooks\"\n      },\n      \"API/interfaces/iactiontrackingmiddlewarehooks\": {\n        \"title\": \"IActionTrackingMiddlewareHooks\",\n        \"sidebar_label\": \"IActionTrackingMiddlewareHooks\"\n      },\n      \"API/interfaces/ianycomplextype\": {\n        \"title\": \"IAnyComplexType\",\n        \"sidebar_label\": \"IAnyComplexType\"\n      },\n      \"API/interfaces/ianymodeltype\": {\n        \"title\": \"IAnyModelType\",\n        \"sidebar_label\": \"IAnyModelType\"\n      },\n      \"API/interfaces/ianytype\": {\n        \"title\": \"IAnyType\",\n        \"sidebar_label\": \"IAnyType\"\n      },\n      \"API/interfaces/ihooks\": {\n        \"title\": \"IHooks\",\n        \"sidebar_label\": \"IHooks\"\n      },\n      \"API/interfaces/ijsonpatch\": {\n        \"title\": \"IJsonPatch\",\n        \"sidebar_label\": \"IJsonPatch\"\n      },\n      \"API/interfaces/imiddlewareevent\": {\n        \"title\": \"IMiddlewareEvent\",\n        \"sidebar_label\": \"IMiddlewareEvent\"\n      },\n      \"API/interfaces/imodelreflectiondata\": {\n        \"title\": \"IModelReflectionData\",\n        \"sidebar_label\": \"IModelReflectionData\"\n      },\n      \"API/interfaces/imodelreflectionpropertiesdata\": {\n        \"title\": \"IModelReflectionPropertiesData\",\n        \"sidebar_label\": \"IModelReflectionPropertiesData\"\n      },\n      \"API/interfaces/imodeltype\": {\n        \"title\": \"IModelType\",\n        \"sidebar_label\": \"IModelType\"\n      },\n      \"API/interfaces/ipatchrecorder\": {\n        \"title\": \"IPatchRecorder\",\n        \"sidebar_label\": \"IPatchRecorder\"\n      },\n      \"API/interfaces/ireversiblejsonpatch\": {\n        \"title\": \"IReversibleJsonPatch\",\n        \"sidebar_label\": \"IReversibleJsonPatch\"\n      },\n      \"API/interfaces/iserializedactioncall\": {\n        \"title\": \"ISerializedActionCall\",\n        \"sidebar_label\": \"ISerializedActionCall\"\n      },\n      \"API/interfaces/isimpletype\": {\n        \"title\": \"ISimpleType\",\n        \"sidebar_label\": \"ISimpleType\"\n      },\n      \"API/interfaces/isnapshotprocessor\": {\n        \"title\": \"ISnapshotProcessor\",\n        \"sidebar_label\": \"ISnapshotProcessor\"\n      },\n      \"API/interfaces/isnapshotprocessors\": {\n        \"title\": \"ISnapshotProcessors\",\n        \"sidebar_label\": \"ISnapshotProcessors\"\n      },\n      \"API/interfaces/itype\": {\n        \"title\": \"IType\",\n        \"sidebar_label\": \"IType\"\n      },\n      \"API/interfaces/ivalidationcontextentry\": {\n        \"title\": \"IValidationContextEntry\",\n        \"sidebar_label\": \"IValidationContextEntry\"\n      },\n      \"API/interfaces/ivalidationerror\": {\n        \"title\": \"IValidationError\",\n        \"sidebar_label\": \"IValidationError\"\n      },\n      \"API/interfaces/referenceoptionsgetset\": {\n        \"title\": \"ReferenceOptionsGetSet\",\n        \"sidebar_label\": \"ReferenceOptionsGetSet\"\n      },\n      \"API/interfaces/referenceoptionsoninvalidated\": {\n        \"title\": \"ReferenceOptionsOnInvalidated\",\n        \"sidebar_label\": \"ReferenceOptionsOnInvalidated\"\n      },\n      \"API/interfaces/unionoptions\": {\n        \"title\": \"UnionOptions\",\n        \"sidebar_label\": \"UnionOptions\"\n      },\n      \"compare/context-reducer-vs-mobx-state-tree\": {\n        \"title\": \"React Context vs. MobX-State-Tree\"\n      },\n      \"concepts/actions\": {\n        \"title\": \"Actions\"\n      },\n      \"concepts/async-actions\": {\n        \"title\": \"Asynchronous actions\"\n      },\n      \"concepts/dependency-injection\": {\n        \"title\": \"Dependency Injection\"\n      },\n      \"concepts/listeners\": {\n        \"title\": \"Listening to observables, snapshots, patches and actions\",\n        \"sidebar_label\": \"Listening to changes\"\n      },\n      \"concepts/middleware\": {\n        \"title\": \"Middleware\"\n      },\n      \"concepts/patches\": {\n        \"title\": \"Patches\"\n      },\n      \"concepts/using-react\": {\n        \"title\": \"React and MST\"\n      },\n      \"concepts/reconciliation\": {\n        \"title\": \"Reconciliation\"\n      },\n      \"concepts/references\": {\n        \"title\": \"Identifiers and references\"\n      },\n      \"concepts/snapshots\": {\n        \"title\": \"Snapshots\"\n      },\n      \"concepts/trees\": {\n        \"title\": \"Types, models, trees & state\"\n      },\n      \"concepts/views\": {\n        \"title\": \"Derived values\"\n      },\n      \"concepts/volatiles\": {\n        \"title\": \"Volatile state\"\n      },\n      \"intro/examples\": {\n        \"title\": \"Examples\"\n      },\n      \"intro/getting-started\": {\n        \"title\": \"Getting Started Tutorial\"\n      },\n      \"intro/installation\": {\n        \"title\": \"Installation\"\n      },\n      \"intro/philosophy\": {\n        \"title\": \"Overview & Philosophy\"\n      },\n      \"intro/welcome\": {\n        \"title\": \"Welcome to MobX-State-Tree!\"\n      },\n      \"overview/hooks\": {\n        \"title\": \"Lifecycle hooks overview\"\n      },\n      \"overview/types\": {\n        \"title\": \"Types overview\"\n      },\n      \"overview/api\": {\n        \"title\": \"API overview\"\n      },\n      \"recipes/auto-generated-property-setter-actions\": {\n        \"title\": \"Auto-Generated Property Setter Actions\"\n      },\n      \"recipes/mst-query\": {\n        \"title\": \"Manage Asynchronous Data with mst-query\"\n      },\n      \"recipes/pre-built-form-types-with-mst-form-type\": {\n        \"title\": \"Pre-built Form Types with MST Form Type\"\n      },\n      \"tips/circular-deps\": {\n        \"title\": \"Handle circular dependencies between files and types using `late`\",\n        \"sidebar_label\": \"Circular dependencies\"\n      },\n      \"tips/faq\": {\n        \"title\": \"Frequently Asked Questions\"\n      },\n      \"tips/inheritance\": {\n        \"title\": \"Simulate inheritance by using type composition\",\n        \"sidebar_label\": \"Simulating inheritance\"\n      },\n      \"tips/more-tips\": {\n        \"title\": \"Miscellaneous Tips\"\n      },\n      \"tips/resources\": {\n        \"title\": \"Talks & Blogs\"\n      },\n      \"tips/snapshots-as-values\": {\n        \"title\": \"Using snapshots as values\"\n      },\n      \"tips/typescript\": {\n        \"title\": \"TypeScript and MST\"\n      }\n    },\n    \"links\": {\n      \"Documentation\": \"Documentation\",\n      \"TypeDocs\": \"TypeDocs\",\n      \"Sponsor\": \"Sponsor\",\n      \"GitHub\": \"GitHub\"\n    },\n    \"categories\": {\n      \"Introduction\": \"Introduction\",\n      \"Basic Concepts\": \"Basic Concepts\",\n      \"Advanced Concepts\": \"Advanced Concepts\",\n      \"API Overview\": \"API Overview\",\n      \"Tips\": \"Tips\",\n      \"Compare\": \"Compare\",\n      \"Recipes\": \"Recipes\",\n      \"Interfaces\": \"Interfaces\"\n    }\n  },\n  \"pages-strings\": {\n    \"Help Translate|recruit community translators for your project\": \"Help Translate\",\n    \"Edit this Doc|recruitment message asking to edit the doc source\": \"Edit\",\n    \"Translate this Doc|recruitment message asking to translate the docs\": \"Translate\"\n  }\n}\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"scripts\": {\n    \"examples\": \"docusaurus-examples\",\n    \"start\": \"docusaurus-start\",\n    \"build\": \"docusaurus-build\",\n    \"publish-gh-pages\": \"docusaurus-publish\",\n    \"write-translations\": \"docusaurus-write-translations\",\n    \"version\": \"docusaurus-version\",\n    \"rename-version\": \"docusaurus-rename-version\"\n  },\n  \"devDependencies\": {\n    \"docusaurus\": \"^1.14.0\"\n  }\n}\n"
  },
  {
    "path": "website/sidebars.json",
    "content": "{\n  \"docs\": {\n    \"Introduction\": [\n      \"intro/welcome\",\n      \"intro/installation\",\n      \"intro/getting-started\",\n      \"intro/examples\",\n      \"intro/philosophy\"\n    ],\n    \"Basic Concepts\": [\n      \"concepts/trees\",\n      \"concepts/actions\",\n      \"concepts/views\",\n      \"concepts/using-react\",\n      \"concepts/snapshots\",\n      \"concepts/references\",\n      \"concepts/async-actions\"\n    ],\n    \"Advanced Concepts\": [\n      \"concepts/patches\",\n      \"concepts/listeners\",\n      \"concepts/dependency-injection\",\n      \"concepts/middleware\",\n      \"concepts/reconciliation\",\n      \"concepts/volatiles\"\n    ],\n    \"API Overview\": [\n      \"overview/types\",\n      \"overview/api\",\n      \"overview/hooks\"\n    ],\n    \"Tips\": [\n      \"tips/resources\",\n      \"tips/contributing\",\n      \"tips/faq\",\n      \"tips/typescript\",\n      \"tips/circular-deps\",\n      \"tips/inheritance\",\n      \"tips/snapshots-as-values\",\n      \"tips/more-tips\"\n    ],\n    \"Compare\": [\n      \"compare/context-reducer-vs-mobx-state-tree\"\n    ],\n    \"Recipes\": [\n      \"recipes/auto-generated-property-setter-actions\",\n      \"recipes/pre-built-form-types-with-mst-form-type\",\n      \"recipes/mst-query\"\n    ]\n  },\n  \"mobx-state-tree\": {\n    \"Introduction\": [\n      \"API/index\"\n    ],\n    \"Interfaces\": [\n      \"API/interfaces/customtypeoptions\",\n      \"API/interfaces/functionwithflag\",\n      \"API/interfaces/iactioncontext\",\n      \"API/interfaces/iactionrecorder\",\n      \"API/interfaces/iactiontrackingmiddleware2call\",\n      \"API/interfaces/iactiontrackingmiddleware2hooks\",\n      \"API/interfaces/iactiontrackingmiddlewarehooks\",\n      \"API/interfaces/ianycomplextype\",\n      \"API/interfaces/ianymodeltype\",\n      \"API/interfaces/ianytype\",\n      \"API/interfaces/ihooks\",\n      \"API/interfaces/ijsonpatch\",\n      \"API/interfaces/imiddlewareevent\",\n      \"API/interfaces/imodelreflectiondata\",\n      \"API/interfaces/imodelreflectionpropertiesdata\",\n      \"API/interfaces/imodeltype\",\n      \"API/interfaces/ipatchrecorder\",\n      \"API/interfaces/ireversiblejsonpatch\",\n      \"API/interfaces/iserializedactioncall\",\n      \"API/interfaces/isimpletype\",\n      \"API/interfaces/isnapshotprocessor\",\n      \"API/interfaces/isnapshotprocessors\",\n      \"API/interfaces/itype\",\n      \"API/interfaces/ivalidationcontextentry\",\n      \"API/interfaces/ivalidationerror\",\n      \"API/interfaces/referenceoptionsgetset\",\n      \"API/interfaces/referenceoptionsoninvalidated\",\n      \"API/interfaces/unionoptions\"\n    ]\n  }\n}"
  },
  {
    "path": "website/siteConfig.js",
    "content": "/**\n * Copyright (c) 2017-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// See https://docusaurus.io/docs/site-config for all the possible\n// site configuration options.\n\nconst siteConfig = {\n    algolia: {\n        apiKey: \"b7b0cfe7d1c8fa6db6089df94a3128f1\",\n        indexName: \"mobx-state-tree\"\n    },\n    title: \"MobX-state-tree\", // Title for your website.\n    tagline:\n        \"Opinionated, transactional, MobX powered state container combining the best features of the immutable and mutable world for an optimal DX\",\n    url: \"https://mobx-state-tree.js.org/\", // Your website URL\n    // baseUrl: \"/mobx-state-tree/\",\n    baseUrl: \"/\", // Base URL for your project */\n    docsUrl: \"\",\n    cname: \"mobx-state-tree.js.org\",\n    editUrl: \"https://github.com/mobxjs/mobx-state-tree/edit/master/docs/\",\n    // For github.io type URLs, you would set the url and baseUrl like:\n    //   url: 'https://facebook.github.io',\n    //   baseUrl: '/test-site/',\n\n    // Used for publishing and more\n    projectName: \"mobx-state-tree\",\n    organizationName: \"mobxjs\",\n    gaTrackingId: \"UA-65632006-4\",\n    // For top-level user or org sites, the organization is still the same.\n    // e.g., for the https://JoelMarcey.github.io site, it would be set like...\n    //   organizationName: 'JoelMarcey'\n\n    // For no header links in the top nav bar -> headerLinks: [],\n    headerLinks: [\n        {\n            doc: \"intro/welcome\",\n            label: \"Documentation\"\n        },\n        {\n            doc: \"API/index\",\n            label: \"TypeDocs\"\n        },\n        { href: \"https://opencollective.com/mobx\", label: \"Sponsor\" },\n\n        { href: \"https://github.com/mobxjs/mobx-state-tree\", label: \"GitHub\" }\n        // {doc: \"support\", label: \"Support mobx-state-tree\"}\n    ],\n\n    /* path to images for header/footer */\n    headerIcon: \"img/favicon.ico\",\n    footerIcon: \"img/favicon.ico\",\n    favicon: \"img/favicon.ico\",\n\n    /* Colors for website */\n    colors: {\n        primaryColor: \"#000\",\n        secondaryColor: \"#ff7000\"\n    },\n\n    /* Custom fonts for website */\n    /*\n  fonts: {\n    myFont: [\n      \"Times New Roman\",\n      \"Serif\"\n    ],\n    myOtherFont: [\n      \"-apple-system\",\n      \"system-ui\"\n    ]\n  },\n  */\n\n    // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.\n    copyright: `Copyright © ${new Date().getFullYear()} Michel Weststrate`,\n\n    highlight: {\n        // Highlight.js theme to use for syntax highlighting in code blocks.\n        theme: \"dracula\"\n    },\n\n    // Add custom scripts here that would be placed in <script> tags.\n    scripts: [\n        \"https://buttons.github.io/buttons.js\",\n        \"https://codefund.io/properties/635/funder.js\"\n    ],\n\n    // On page navigation for the current documentation page.\n    onPageNav: \"separate\",\n    // No .html extensions for paths.\n    cleanUrl: true,\n\n    // Open Graph and Twitter card images.\n    // ogImage: \"img/undraw_online.svg\",\n    // twitterImage: \"img/undraw_tweetstorm.svg\"\n\n    // For sites with a sizable amount of content, set collapsible to true.\n    // Expand/collapse the links and subcategories under categories.\n    docsSideNavCollapsible: true,\n\n    // Show documentation's last contributor's name.\n    // enableUpdateBy: true,\n\n    // Show documentation's last update time.\n    // enableUpdateTime: true,\n\n    // You may provide arbitrary config keys to be used as needed by your\n    // template. For example, if you need your repo's URL...\n    repoUrl: \"https://github.com/mobxjs/mobx-state-tree\"\n}\n\nmodule.exports = siteConfig\n"
  },
  {
    "path": "website/static/css/custom.css",
    "content": "/* your custom css */\n@media only screen and (min-device-width: 360px) and (max-device-width: 736px) {\n}\n\n@media only screen and (min-width: 1024px) {\n}\n\n@media only screen and (max-width: 1023px) {\n}\n\n@media only screen and (min-width: 1400px) {\n}\n\n@media only screen and (min-width: 1500px) {\n}\n\na {\n    color: #ff7000;\n}\n"
  },
  {
    "path": "website/static/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta http-equiv=\"refresh\" content=\"0; url=intro/welcome\" />\n\t\t<script type=\"text/javascript\">\n\t\t\twindow.location.href = \"intro/welcome\"\n\t\t</script>\n\t\t<title>MobX-state-tree</title>\n\t</head>\n\t<body>\n\t\tIf you are not redirected automatically, follow this\n\t\t<a href=\"intro/welcome\">link</a>.\n\t</body>\n</html>\n"
  }
]