[
  {
    "path": ".codesandbox/ci.json",
    "content": "{\n  \"packages\": [\"dist\"],\n  \"sandboxes\": [\n    \"new\",\n    \"react-typescript-react-ts\",\n    \"simple-react-browserify-x9yni\",\n    \"simple-snowpack-react-o1gmx\",\n    \"react-parcel-onewf\"\n  ],\n  \"node\": \"18\"\n}\n"
  },
  {
    "path": ".github/DISCUSSION_TEMPLATE/bug-report.yml",
    "content": "labels: ['bug']\nbody:\n  - type: markdown\n    attributes:\n      value: If you don't have a reproduction link, please choose a different category.\n  - type: textarea\n    attributes:\n      label: Bug Description\n      description: Describe the bug you encountered\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Reproduction Link\n      description: A link to a [TypeScript Playground](https://www.typescriptlang.org/play), a [StackBlitz Project](https://stackblitz.com/) or something else with a minimal reproduction.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [dai-shi] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: ['https://daishi.gumroad.com/l/learn-valtio'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Assigned issue\nabout: This is to create a new issue that already has an assignee. Please open a new discussion otherwise.\ntitle: ''\nlabels: ''\nassignees: ''\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Bug Reports\n    url: https://github.com/pmndrs/valtio/discussions/new?category=bug-report\n    about: Please post bug reports here.\n  - name: Questions\n    url: https://github.com/pmndrs/valtio/discussions/new?category=q-a\n    about: Please post questions here.\n  - name: Other Discussions\n    url: https://github.com/pmndrs/valtio/discussions/new/choose\n    about: Please post ideas and general discussions here.\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "## Related Bug Reports or Discussions\n\nFixes #\n\n## Summary\n\n## Check List\n\n- [ ] `pnpm run fix` for formatting and linting code and docs\n"
  },
  {
    "path": ".github/workflows/compressed-size.yml",
    "content": "name: Compressed Size\n\non: [pull_request]\n\njobs:\n  compressed_size:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - uses: preactjs/compressed-size-action@49c7ff02f46adc39a83c24e91f6110ba8138a19d # v3\n        with:\n          pattern: './dist/**/*.{js,mjs}'\n"
  },
  {
    "path": ".github/workflows/preview-release.yml",
    "content": "name: Preview Release\n\non: [push, pull_request]\n\njobs:\n  preview_release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - run: pnpm dlx pkg-pr-new publish './dist' --compact --template './examples/*'\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  release:\n    types: [published]\n\npermissions:\n  id-token: write\n  contents: read\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          registry-url: 'https://registry.npmjs.org'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - run: npm publish\n        working-directory: dist\n"
  },
  {
    "path": ".github/workflows/test-multiple-builds.yml",
    "content": "name: Test Multiple Builds\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test_multiple_builds:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        build: [cjs, esm]\n        env: [development] # [development, production]\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - name: Patch for DEV-ONLY\n        if: ${{ matrix.env == 'development' }}\n        run: |\n          sed -i~ \"s/it[.a-zA-Z]*('\\[DEV-ONLY\\]/it('/\" tests/*.ts tests/*.tsx\n          sed -i~ \"s/it[.a-zA-Z]*('\\[PRD-ONLY\\]/it.skip('/\" tests/*.ts tests/*.tsx\n      - name: Patch for PRD-ONLY\n        if: ${{ matrix.env == 'production' }}\n        run: |\n          sed -i~ \"s/it[.a-zA-Z]*('\\[PRD-ONLY\\]/it('/\" tests/*.ts tests/*.tsx\n          sed -i~ \"s/it[.a-zA-Z]*('\\[DEV-ONLY\\]/it.skip('/\" tests/*.ts tests/*.tsx\n      - name: Patch for CJS\n        if: ${{ matrix.build == 'cjs' }}\n        run: |\n          sed -i~ \"s/resolve('\\.\\/src\\(.*\\)\\.ts')/resolve('\\.\\/dist\\1.js')/\" vitest.config.mts\n      - name: Patch for ESM\n        if: ${{ matrix.build == 'esm' }}\n        run: |\n          sed -i~ \"s/resolve('\\.\\/src\\(.*\\)\\.ts')/resolve('\\.\\/dist\\/esm\\1.mjs')/\" vitest.config.mts\n          sed -i~ \"1s/^/import.meta.env.MODE='${NODE_ENV}';/\" tests/*.ts tests/*.tsx\n        env:\n          NODE_ENV: ${{ matrix.env }}\n      - name: Test ${{ matrix.build }} ${{ matrix.env }}\n        run: |\n          pnpm run test:spec\n        env:\n          NODE_ENV: ${{ matrix.env }}\n"
  },
  {
    "path": ".github/workflows/test-multiple-versions.yml",
    "content": "name: Test Multiple Versions\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test_multiple_versions:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        react:\n          - 18.0.0\n          - 18.1.0\n          - 18.2.0\n          - 18.3.1\n          - 19.0.0\n          - 19.1.0\n          - 19.2.0\n          - 19.3.0-canary-e0cc7202-20260227\n          - 0.0.0-experimental-e0cc7202-20260227\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - name: Install legacy testing-library\n        if: ${{ startsWith(matrix.react, '16.') || startsWith(matrix.react, '17.') }}\n        run: pnpm add -D @testing-library/react@12.1.4\n      - name: Patch for React 16\n        if: ${{ startsWith(matrix.react, '16.') }}\n        run: |\n          sed -i~ '1s/^/import React from \"react\";/' tests/*.tsx\n          sed -i~ 's/\"jsx\": \"react-jsx\"/\"jsx\": \"react\"/' tsconfig.json\n          sed -i~ 's/import\\.meta\\.env[?]\\.MODE/\"DEVELOPMENT\".toLowerCase()/' src/*.ts src/*/*.ts src/*/*/*.ts\n      - name: Test ${{ matrix.react }}\n        run: |\n          pnpm add -D react@${{ matrix.react }} react-dom@${{ matrix.react }}\n          pnpm run test:spec\n"
  },
  {
    "path": ".github/workflows/test-old-typescript.yml",
    "content": "name: Test Old TypeScript\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test_old_typescript:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        typescript:\n          - 5.9.3\n          - 5.8.3\n          - 5.7.3\n          - 5.6.3\n          - 5.5.4\n          - 5.4.5\n          - 5.3.3\n          - 5.2.2\n          - 5.1.6\n          - 5.0.4\n          - 4.9.5\n          - 4.8.4\n          - 4.7.4\n          - 4.6.4\n          - 4.5.5\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run build\n      - name: Patch for all TS\n        run: |\n          sed -i~ 's/\"isolatedDeclarations\": true,//' tsconfig.json\n      - name: Patch for v4/v3 TS\n        if: ${{ startsWith(matrix.typescript, '4.') || startsWith(matrix.typescript, '3.') }}\n        run: |\n          sed -i~ 's/\"verbatimModuleSyntax\": true,//' tsconfig.json\n      - name: Patch for Old TS\n        if: ${{ matrix.typescript == '5.5.4' || matrix.typescript == '5.4.5' || matrix.typescript == '5.3.3' || matrix.typescript == '5.2.2' || matrix.typescript == '5.1.6' || matrix.typescript == '5.0.4' || matrix.typescript == '4.9.5' || matrix.typescript == '4.8.4' || matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' }}\n        run: |\n          sed -i~ 's/\"moduleResolution\": \"bundler\",/\"moduleResolution\": \"node\",/' tsconfig.json\n          sed -i~ 's/\"allowImportingTsExtensions\": true,//' tsconfig.json\n          sed -i~ 's/\"valtio\": \\[\"\\.\\/src\\/index\\.ts\"\\],/\"valtio\": [\".\\/dist\\/index.d.ts\"],/' tsconfig.json\n          sed -i~ 's/\"valtio\\/\\*\": \\[\"\\.\\/src\\/\\*\\.ts\"\\]/\"valtio\\/*\": [\".\\/dist\\/*.d.ts\"]/' tsconfig.json\n          sed -i~ 's/\"include\": .*/\"include\": [\"src\\/types.d.ts\", \"dist\\/**\\/*\", \"tests\\/**\\/*\"],/' tsconfig.json\n      - name: Patch for Older TS\n        if: ${{ matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' }}\n        run: |\n          pnpm json -I -f package.json -e \"this.resolutions={}; this.resolutions['@types/node']='18.13.0';\"\n          pnpm add -D @types/node@18.13.0\n          pnpm add -D vitest@3.2.4 @vitest/coverage-v8@3.2.4 @vitest/ui@3.2.4\n      - name: Install old TypeScript\n        run: pnpm add -D typescript@${{ matrix.typescript }}\n      - name: Test ${{ matrix.typescript }}\n        run: pnpm run test:types\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 'lts/*'\n          cache: 'pnpm'\n      - run: pnpm install\n      - run: pnpm run test:format\n      - run: pnpm run test:types\n      - run: pnpm run test:lint\n      - run: pnpm run test:spec\n      - run: pnpm run build # we don't have any other workflows to test build\n"
  },
  {
    "path": ".gitignore",
    "content": "# dependencies\nnode_modules\n.pnp\n.pnp.js\n\n# testing\ncoverage\n\n# production\ndist\nbuild\n\n# dotenv environment variables file\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# logs\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# misc\n.DS_Store\n.idea\n\n# examples\nexamples/**/*/package-lock.json\nexamples/**/*/yarn.lock\nexamples/**/*/pnpm-lock.yaml\nexamples/**/*/bun.lockb\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist\npnpm-lock.yaml\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## General Guideline\n\n### Reporting Issues\n\nIf you have found what you think is a bug, please [start a discussion](https://github.com/pmndrs/valtio/discussions/new?category=bug-report).\n\nFor any usage questions, please [start a discussion](https://github.com/pmndrs/valtio/discussions/new?category=q-a).\n\n### Suggesting New Features\n\nIf you are here to suggest a feature, first [start a discussion](https://github.com/pmndrs/valtio/discussions/new?category=ideas) if it does not already exist. From there, we will discuss use-cases for the feature and then finally discuss how it could be implemented.\n\n### Committing\n\nWe are applying [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/) here. In short, that means a commit has to be one of the following types:\n\nYour commit type must be one of the following:\n\n- **feat**: A new feature.\n- **fix**: A bug fix.\n- **refactor**: A code change that neither fixes a bug nor adds a feature.\n- **chore**: Changes to the build process, configuration, dependencies, CI/CD pipelines, or other auxiliary tools and libraries.\n- **docs**: Documentation-only changes.\n- **test**: Adding missing or correcting existing tests.\n\nIf you are unfamiliar with the usage of conventional commits,\nthe short version is to simply specify the type as a first word,\nand follow it with a colon and a space, then start your message\nfrom a lowercase letter, like this:\n\n```\nfeat: add a 'foo' type support\n```\n\nYou can also specify the scope of the commit in the parentheses after a type:\n\n```\nfix(react): change the 'bar' parameter type\n```\n\n### Development\n\nIf you would like to contribute by fixing an open issue or developing a new feature you can use this suggested workflow:\n\n#### General\n\n1. Fork this repository.\n2. Create a new feature branch based off the `main` branch.\n3. Follow the [Core](#Core) and/or the [Documentation](#Documentation) guide below and come back to this once done.\n4. Run `pnpm run fix:format` to format the code.\n5. Git stage your required changes and commit (review the commit guidelines below).\n6. Submit the PR for review.\n\n##### Core\n\n1. Run `pnpm install` to install dependencies.\n2. Create failing tests for your fix or new feature in the [`tests`](./tests/) folder.\n3. Implement your changes.\n4. Run `pnpm run build` to build the library. _(Pro-tip: `pnpm run build-watch` runs the build in watch mode)_\n5. Run the tests by running `pnpm run test` and ensure that they pass.\n6. You can use `pnpm link` to sym-link this package and test it locally on your own project. Alternatively, you may use CodeSandbox CI's canary releases to test the changes in your own project. (requires a PR to be created first)\n7. Follow step 4 and onwards from the [General](#General) guide above to bring it to the finish line.\n\n### Pull Requests\n\nPlease try to keep your pull request focused in scope and avoid including unrelated commits.\n\nAfter you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or request improvements, therefore, please check ✅ [\"Allow edits from maintainers\"](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) on your PR.\n\n## Valtio-specific Guideline\n\n##### Documentation\n\n1. Navigate to the [`website`](./website/) folder. (e.g., `cd website`).\n2. Run `pnpm install` to install dependencies in the `website` folder.\n3. Run `pnpm run dev` to start the dev server.\n4. Navigate to [`http://localhost:3000`](http://localhost:3000) to view the documents.\n5. Navigate to the [`docs`](./docs/) folder and make necessary changes to the documents.\n6. Add your changes to the documents and see them live reloaded in the browser.\n7. Follow step 4 and onwards from the [General](#General) guide above to bring it to the finish line.\n\nThank you for contributing! :heart:\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Poimandres\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=\"logo.svg\" alt=\"valtio\">\n<br />\n<br />\n\n<code>npm install valtio</code> makes proxy-state simple\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/pmndrs/valtio/test.yml?branch=main&style=flat&colorA=000000&colorB=000000)](https://github.com/pmndrs/valtio/actions/workflows/test.yml?query=branch%3Amain)\n[![Build Size](https://img.shields.io/bundlephobia/minzip/valtio?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=valtio)\n[![Version](https://img.shields.io/npm/v/valtio?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/valtio)\n[![Downloads](https://img.shields.io/npm/dt/valtio.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/valtio)\n[![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/poimandres)\n\n#### Wrap your state object\n\nValtio turns the object you pass it into a self-aware proxy.\n\n```jsx\nimport { proxy, useSnapshot } from 'valtio'\n\nconst state = proxy({ count: 0, text: 'hello' })\n```\n\n#### Mutate from anywhere\n\nYou can make changes to it in the same way you would to a normal js-object.\n\n```jsx\nsetInterval(() => {\n  ++state.count\n}, 1000)\n```\n\n#### React via useSnapshot\n\nCreate a local snapshot that catches changes. Rule of thumb: read from snapshots in render function, otherwise use the source. The component will only re-render when the parts of the state you access have changed, it is render-optimized.\n\n```jsx\n// This will re-render on `state.count` change but not on `state.text` change\nfunction Counter() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      {snap.count}\n      <button onClick={() => ++state.count}>+1</button>\n    </div>\n  )\n}\n```\n\n<details>\n<summary>Note for TypeScript users: Return type of useSnapshot can be too strict.</summary>\n\nThe `snap` variable returned by `useSnapshot` is a (deeply) read-only object.\nIts type has `readonly` attribute, which may be too strict for some use cases.\n\nTo mitigate typing difficulties, you might want to loosen the type definition:\n\n```ts\ndeclare module 'valtio' {\n  function useSnapshot<T extends object>(p: T): T\n}\n```\n\nSee [#327](https://github.com/pmndrs/valtio/issues/327) for more information.\n\n</details>\n\n<details>\n<summary>Note: useSnapshot returns a new proxy for render optimization.</summary>\n\nInternally, `useSnapshot` calls `snapshot` in valtio/vanilla,\nand wraps the snapshot object with another proxy to detect property access.\nThis feature is based on [proxy-compare](https://github.com/dai-shi/proxy-compare).\n\nTwo kinds of proxies are used for different purposes:\n\n- `proxy()` from `valtio/vanilla` is for mutation tracking or write tracking.\n- `createProxy()` from `proxy-compare` is for usage tracking or read tracking.\n</details>\n\n<details>\n<summary>Use of <code>this</code> is for expert users.</summary>\n\nValtio tries best to handle `this` behavior\nbut it's hard to understand without familiarity.\n\n```js\nconst state = proxy({\n  count: 0,\n  inc() {\n    ++this.count\n  },\n})\nstate.inc() // `this` points to `state` and it works fine\nconst snap = useSnapshot(state)\nsnap.inc() // `this` points to `snap` and it doesn't work because snapshot is frozen\n```\n\nTo avoid this pitfall, the recommended pattern is not to use `this` and prefer arrow function.\n\n```js\nconst state = proxy({\n  count: 0,\n  inc: () => {\n    ++state.count\n  },\n})\n```\n\n</details>\n\nIf you are new to this, it's highly recommended to use\n[eslint-plugin-valtio](https://github.com/pmndrs/eslint-plugin-valtio).\n\n#### Subscribe from anywhere\n\nYou can access state outside of your components and subscribe to changes.\n\n```jsx\nimport { subscribe } from 'valtio'\n\n// Subscribe to all state changes\nconst unsubscribe = subscribe(state, () =>\n  console.log('state has changed to', state),\n)\n// Unsubscribe by calling the result\nunsubscribe()\n```\n\nYou can also subscribe to a portion of state.\n\n```jsx\nconst state = proxy({ obj: { foo: 'bar' }, arr: ['hello'] })\n\nsubscribe(state.obj, () => console.log('state.obj has changed to', state.obj))\nstate.obj.foo = 'baz'\n\nsubscribe(state.arr, () => console.log('state.arr has changed to', state.arr))\nstate.arr.push('world')\n```\n\nTo subscribe to a primitive value of state, consider `subscribeKey` in utils.\n\n```jsx\nimport { subscribeKey } from 'valtio/utils'\n\nconst state = proxy({ count: 0, text: 'hello' })\nsubscribeKey(state, 'count', (v) =>\n  console.log('state.count has changed to', v),\n)\n```\n\nThere is another util `watch` which might be convenient in some cases.\n\n```jsx\nimport { watch } from 'valtio/utils'\n\nconst state = proxy({ count: 0 })\nconst stop = watch((get) => {\n  console.log('state has changed to', get(state)) // auto-subscribe on use\n})\n```\n\n#### Suspend your components\n\nValtio is compatible with React 19 `use` hook. This eliminates all the async back-and-forth, you can access your data directly while the parent is responsible for fallback state and error handling.\n\n```jsx\nimport { use } from 'react' // React 19\n// import { use } from 'react18-use' // React 18\n\nconst state = proxy({ post: fetch(url).then((res) => res.json()) })\n\nfunction Post() {\n  const snap = useSnapshot(state)\n  return <div>{use(snap.post).title}</div>\n}\n\nfunction App() {\n  return (\n    <Suspense fallback={<span>waiting...</span>}>\n      <Post />\n    </Suspense>\n  )\n}\n```\n\nIt still suffers from \"de-opt\", which prevents `useTransition` to work well. To mitigate it, there is a third-party library [use-valtio](https://github.com/valtiojs/use-valtio).\n\n#### Holding objects in state without tracking them\n\nThis may be useful if you have large, nested objects with accessors that you don't want to proxy. `ref` allows you to keep these objects inside the state model.\n\nSee [#61](https://github.com/pmndrs/valtio/issues/61) and [#178](https://github.com/pmndrs/valtio/issues/178) for more information.\n\n```js\nimport { proxy, ref } from 'valtio'\n\nconst state = proxy({\n  count: 0,\n  dom: ref(document.body),\n})\n```\n\n#### Update transiently (for often occurring state-changes)\n\nYou can read state in a component without causing re-render.\n\n```jsx\nfunction Foo() {\n  const { count, text } = state\n  // ...\n```\n\nOr, you can have more control with subscribing in useEffect.\n\n```jsx\nfunction Foo() {\n  const total = useRef(0)\n  useEffect(() => subscribe(state.arr, () => {\n    total.current = state.arr.reduce((p, c) => p + c)\n  }), [])\n  // ...\n```\n\n#### Update synchronously\n\nBy default, state mutations are batched before triggering re-render.\nSometimes, we want to disable the batching.\nThe known use case of this is `<input>` [#270](https://github.com/pmndrs/valtio/issues/270).\n\n```jsx\nfunction TextBox() {\n  const snap = useSnapshot(state, { sync: true })\n  return (\n    <input value={snap.text} onChange={(e) => (state.text = e.target.value)} />\n  )\n}\n```\n\n#### Dev tools\n\nYou can use [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools) for plain objects and arrays.\n\n```jsx\nimport { devtools } from 'valtio/utils'\n\nconst state = proxy({ count: 0, text: 'hello' })\nconst unsub = devtools(state, { name: 'state name', enabled: true })\n```\n\n<details>\n  <summary>Manipulating state with Redux DevTools</summary>\nThe screenshot below shows how to use Redux DevTools to manipulate state. First select the object from the instances drop down. Then type in a JSON object to dispatch. Then click \"Dispatch\". Notice how it changes the state.\n\n<br/>\n<img width=\"564\" alt=\"image\" src=\"https://user-images.githubusercontent.com/6372489/141134955-26e9ffce-1e2a-4c8c-a9b3-d9da739610fe.png\">\n</details>\n\n#### Use it vanilla\n\nValtio is not tied to React, you can use it in vanilla-js.\n\n```jsx\nimport { proxy, subscribe, snapshot } from 'valtio/vanilla'\n// import { ... } from 'valtio/vanilla/utils'\n\nconst state = proxy({ count: 0, text: 'hello' })\n\nsubscribe(state, () => {\n  console.log('state is mutated')\n  const obj = snapshot(state) // A snapshot is an immutable object\n})\n```\n\n#### `useProxy` util\n\nWhile the separation of proxy state and its snapshot is important,\nit's confusing for beginners.\nWe have a convenient util to improve developer experience. useProxy returns shallow proxy state and its snapshot, meaning you can only mutate on root level.\n\n```js\nimport { useProxy } from 'valtio/utils'\n\nconst state = proxy({ count: 1 })\n\nconst Component = () => {\n  // useProxy returns a special proxy that can be used both in render and callbacks\n  // The special proxy has to be used directly in a function scope. You can't destructure it outside the scope.\n  const $state = useProxy(state)\n  return (\n    <div>\n      {$state.count}\n      <button onClick={() => ++$state.count}>+1</button>\n    </div>\n  )\n}\n```\n\n#### Computed properties\n\nYou can define computed properties with object getters.\n\n```js\nconst state = proxy({\n  count: 1,\n  get doubled() {\n    return this.count * 2\n  },\n})\n```\n\nConsider it as an advanced usage, because the behavior of `this` is sometimes confusing.\n\nFor more information, check out [this guide](./docs/guides/computed-properties.mdx).\n\n#### `proxySet` util\n\nThis is to create a proxy which mimic the native Set behavior. The API is the same as Set API\n\n```js\nimport { proxySet } from 'valtio/utils'\n\nconst state = proxySet([1, 2, 3])\n//can be used inside a proxy as well\n//const state = proxy({\n//    count: 1,\n//    set: proxySet()\n//})\n\nstate.add(4)\nstate.delete(1)\nstate.forEach((v) => console.log(v)) // 2,3,4\n```\n\n#### `proxyMap` util\n\nThis is to create a proxy which emulate the native Map behavior. The API is the same as Map API\n\n```js\nimport { proxyMap } from 'valtio/utils'\n\nconst state = proxyMap([\n  ['key', 'value'],\n  ['key2', 'value2'],\n])\nstate.set('key', 'value')\nstate.delete('key')\nstate.get('key') // ---> value\nstate.forEach((value, key) => console.log(key, value)) // ---> \"key\", \"value\", \"key2\", \"value2\"\n```\n\n#### Compatibility\n\nValtio v2 works with React 18 and up.\nIt only depends on `react` and works with any\nrenderers such as `react-dom`, `react-native`, `react-three-fiber`, and so on.\n\nValtio works on Node.js, Next.js and other frameworks.\n\nValtio also works without React. See [vanilla](#use-it-vanilla).\n\n#### Plugins\n\n- [eslint-plugin-valtio](https://github.com/pmndrs/eslint-plugin-valtio)\n\n#### Recipes\n\nValtio is unopinionated about best practices.\nThe community is working on recipes.\n\n- [How to organize actions](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-organize-actions.mdx)\n- [How to persist states](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-persist-states.mdx)\n- [How to use with context](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-use-with-context.mdx)\n- [How to split and compose states](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-split-and-compose-states.mdx)\n"
  },
  {
    "path": "docs/api/advanced/ref.mdx",
    "content": "---\ntitle: 'ref'\nsection: 'API'\nsubSection: 'Advanced'\ndescription: 'Create a ref object'\n---\n\n# `ref`\n\n## A `ref` allows unproxied state in a Valtio proxy\n\nA `ref` is useful in the rare instances you need to nest an object in a `proxy` that is not wrapped in an inner proxy and, therefore, is not tracked.\n\n```js\nconst store = proxy({\n    users: [\n        { id: 1, name: 'Juho', uploads: ref([]) },\n    ]\n  })\n})\n```\n\nOnce an object is wrapped in a `ref`, it should be mutated without reassigning the object or rewrapping in a new `ref`.\n\n```js\n// ✅ do mutate\nstore.users[0].uploads.push({ id: 1, name: 'Juho' })\n// ✅ do reset\nstore.users[0].uploads.splice(0)\n\n// ❌ don't reassign\nstore.users[0].uploads = []\n```\n\nA `ref` should also not be used as the only state in a proxy, making the proxy usage pointless.\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/valtio-file-load-demo-oo2yzn\n"
  },
  {
    "path": "docs/api/advanced/snapshot.mdx",
    "content": "---\ntitle: 'snapshot'\nsection: 'API'\nsubSection: 'Advanced'\ndescription: 'Create a snapshot of current state'\n---\n\n# `snapshot`\n\n`snapshot` takes a proxy and returns an immutable object, unwrapped from the proxy.\n\nImmutability is achieved by _efficiently_ deep copying & freezing the object (see the [Copy on Write](#copy-on-write) section for details).\n\nBriefly, in sequential `snapshot` calls, when the values in the proxy have not changed, the previous snapshot's object reference is returned. This allows for shallow comparison in render functions, preventing spurious renders.\n\nSnapshots also throw promises, making them work with React Suspense.\n\n```js\nimport { proxy, snapshot } from 'valtio'\n\nconst store = proxy({ name: 'Mika' })\nconst snap1 = snapshot(store) // an efficient copy of the current store values, unproxied\nconst snap2 = snapshot(store)\nconsole.log(snap1 === snap2) // true, no need to re-render\n\nstore.name = 'Hanna'\nconst snap3 = snapshot(store)\nconsole.log(snap1 === snap3) // false, should re-render\n```\n\n## Copy on Write\n\nEven though snapshots are a deep copy of the entire state, they use a lazy copy-on-write mechanism for updates, so in practice they are quick to maintain.\n\nFor example, if we have a nested object of:\n\n```js\nconst author = proxy({\n  firstName: 'f',\n  lastName: 'l',\n  books: [{ title: 't1' }, { title: 't2' }],\n})\n\nconst s1 = snapshot(author)\n```\n\nThe first `snapshot` call creates four new instances:\n\n- one for the author,\n- one for the books array, and\n- two for the book objects.\n\nWhen we mutate the 2nd book, and take a new `snapshot`:\n\n```js\nauthor.books[1].title = 't2b'\nconst s2 = snapshot(author)\n```\n\nThen `s2` will have a new copy of the 2nd book, but reuse the existing snapshot of the unchanged 1st book.\n\n```js\nconsole.log(s1 === s2) // false\nconsole.log(s1.books === s2.books) // false\nconsole.log(s1.books[0] === s2.books[0]) // true\nconsole.log(s1.books[1] === s2.books[1]) // false\n```\n\nEven though this example only reused one of the four existing snapshot instances, it shows that the cost of maintaining snapshots is based on the _depth_ of your state tree (which is typically low, like author to book to book reviews is three levels), and not the _breadth_ (1000s of books).\n\n## Classes\n\nSnapshots maintain the original objects' prototypes, so methods and getters work, and correctly evaluate against the snapshot's frozen state.\n\n```js\nimport { proxy, snapshot } from 'valtio'\n\nclass Author {\n  firstName = 'f'\n  lastName = 'l'\n  fullName() {\n    return `${this.firstName} ${this.lastName}`\n  }\n}\n\nconst state = proxy(new Author())\nconst snap = snapshot(state)\n\n// the snapshot has the Author prototype\nconsole.log(snap instanceof Author) // true\n\nstate.firstName = 'f2'\n\n// Invocations use the snapshot's state, e.g. this is still 'f' because\n// inside `fullName`, `this` will be the frozen snapshot instance and not\n// the mutable state proxy\nconsole.log(snap.fullName()) // 'f l'\n```\n\nNote that the results of getters and methods are not cached, and are re-evaluated on every call.\n\nThis should be fine, because the expectation is that they execute very quickly (faster than the overhead of caching them would be worth) and are also deterministic, so the return value is based only on the already-frozen snapshot state.\n\n## Vanilla JavaScript\n\nIn VanillaJS, `snapshot` is not necessary to access proxied object values, inside or outside of subscribe. However, it is useful, for example, to keep a serializable list of un-proxied objects or check if objects have changed. It also resolves promises.\n\n<br />\n\n<blockquote className=\"tip\">\n💡 Tip\n\nIf you are using valtio outside of react, import from `valtio/vanilla`\n\n```js\nimport { proxy, snapshot } from 'valtio/vanilla'\n```\n\n</blockquote>\n"
  },
  {
    "path": "docs/api/advanced/subscribe-ops.mdx",
    "content": "---\ntitle: 'subscribe-ops'\nsection: 'API'\nsubSection: 'Advanced'\ndescription: 'Fine-grained mutation tracking with operational transformations (Ops)'\n---\n\n# Subscribe Ops\n\nBy default, Valtio's `subscribe` notify you that _something_ has changed in the state proxy. However, you can opt-in to receiving **Ops** (Operations), which are detailed descriptions of exactly what was modified.\n\n## What are Ops?\n\nOps are granular mutation records. When a proxy is updated, Valtio can generate a description of the change as a tuple.\n\n### Op Types\n\n- **`set`**: `[op: 'set', path: Path, value: unknown, prevValue: unknown]`\n  - Triggered when a property is assigned a new value.\n- **`delete`**: `[op: 'delete', path: Path, prevValue: unknown]`\n  - Triggered when a property is deleted.\n- **`resolve`**: `[op: 'resolve', path: Path, value: unknown]`\n  - Triggered when a promise in the state is fulfilled.\n- **`reject`**: `[op: 'reject', path: Path, error: unknown]`\n  - Triggered when a promise in the state is rejected.\n\nThe `Path` is an array of strings or symbols representing the nested location of the property (e.g., `['user', 'profile', 'name']`).\n\n## How to use Ops\n\nThe \"ops\" feature is an **opt-in** feature because it introduces a small performance overhead for tracking and allocating these operation objects.\n\n### 1. Enabling Ops\n\nYou must explicitly enable op-tracking using the unstable API:\n\n```javascript\nimport { unstable_enableOp } from 'valtio'\n\n// Enable globally\nunstable_enableOp(true)\n```\n\n### 2. Receiving Ops in `subscribe`\n\nOnce enabled, the `subscribe` callback receives an array of these operations as its first argument.\n\n```javascript\nimport { proxy, subscribe, unstable_enableOp } from 'valtio'\n\nunstable_enableOp(true)\n\nconst state = proxy({ count: 0, text: 'hello' })\n\nsubscribe(state, (ops) => {\n  ops.forEach((op) => {\n    const [action, path, value, prevValue] = op\n    console.log(`Action: ${action} at ${path.join('.')}`)\n    console.log(`New value:`, value)\n    console.log(`Previous value:`, prevValue)\n  })\n})\n\nstate.count++\n// Output:\n// Action: set at count\n// New value: 1\n// Previous value: 0\n```\n\n> **Note**: If `unstable_enableOp(true)` is not called, the `ops` argument will be an empty array or `undefined`.\n\n## Use Cases\n\nWhile standard `subscribe` is sufficient for most React UI updates, Ops are useful for specific advanced scenarios:\n\n1.  **Network Synchronization**: Instead of sending the entire state over the wire, you can send only the `ops` (patches). This significantly reduces bandwidth consumption in distributed applications.\n2.  **Undo/Redo History**: Use the `prevValue` provided in `set` and `delete` ops to easily revert state changes.\n3.  **Audit Logs & Debugging**: Track a sequence of user-driven mutations for analytics or time-travel debugging.\n4.  **Devtools Integration**: Powering custom development tools that need to visualize state transitions.\n\n## Performance Considerations\n\nEnabling Ops has a small overhead cost. For every mutation, Valtio must:\n\n- Detect the change type.\n- Construct the `Path` array.\n- Allocate the `Op` tuple.\n\nIn high-frequency update scenarios (e.g., animations, canvas interactions moving hundreds of objects per frame), this can lead to:\n\n- Increased garbage collection (GC) pressure due to object allocations.\n- A measurable drop in frame rates (FPS).\n\n**Recommendation**: Only enable `unstable_enableOp` if your application actually consumes the granular `ops` data.\n"
  },
  {
    "path": "docs/api/advanced/subscribe.mdx",
    "content": "---\ntitle: 'subscribe'\nsection: 'API'\nsubSection: 'Advanced'\ndescription: 'Subscribe to a current state/object'\n---\n\n# `subscribe`\n\n## Subscribe from anywhere\n\nYou can access state outside your components and subscribe to changes.\n\n```jsx\nimport { proxy, subscribe } from 'valtio'\n\nconst state = proxy({ count: 0 })\n\n// Subscribe to all changes to the state proxy (and its child proxies)\nconst unsubscribe = subscribe(state, () =>\n  console.log('state has changed to', state),\n)\n// Unsubscribe by calling the result\nunsubscribe()\n```\n\nYou can also subscribe to a portion of state.\n\n```jsx\nconst state = proxy({ obj: { foo: 'bar' }, arr: ['hello'] })\n\nsubscribe(state.obj, () => console.log('state.obj has changed to', state.obj))\nstate.obj.foo = 'baz'\nsubscribe(state.arr, () => console.log('state.arr has changed to', state.arr))\nstate.arr.push('world')\n```\n\n## Codesandbox demo in VanillaJS\n\nhttps://codesandbox.io/s/valtio-photo-booth-demo-forked-xp8hs?file=/src/main.js\n\n## Advanced: Listening to \"Ops\"\n\nThe `subscribe` callback can also receive **Ops** (Operations), which are detailed records of exactly what changed (e.g., which property was set or deleted). This is useful for advanced scenarios like synchronization or undo/redo.\n\nBecause tracking these operations has a small performance cost, they are disabled by default. For more details on how to enable and use them, check out [Subscribe Ops](./subscribe-ops).\n"
  },
  {
    "path": "docs/api/basic/proxy.mdx",
    "content": "---\ntitle: 'proxy'\nsection: 'API'\nsubSection: 'Basic'\ndescription: 'Create a proxy object.'\n---\n\n# `proxy`\n\nThe `proxy` tracks changes to the original object and all nested objects, notifying listeners when an object is modified.\n\n```js\nimport { proxy } from 'valtio'\n\nconst state = proxy({ count: 0, text: 'hello' })\n```\n\n## Mutate from anywhere\n\nYou can make changes to it in the same way you would to a normal js-object.\n\n```js\nsetInterval(() => {\n  ++state.count\n}, 1000)\n```\n\n## Optimizations: noop and batching\n\nUpdates that set the value of a property to the same value are ignored. Subscribers will not be informed of a version change.\n\n```js\nconst state = proxy({ count: 0 })\n\nstate.count = 0 // has no effect\n```\n\nMultiple changes in the same event loop tick will be batched together. Subscribers will be notified of a single version change.\n\n```js\nconst state = proxy({ count: 0, text: 'hello' })\n// subscribers will be notified once after both mutations\nstate.count = 1\nstate.text = 'world'\n```\n\n## Nested proxies\n\nProxies can be nested in other `proxy` objects and updated as a whole.\n\n```jsx\nimport { proxy, useSnapshot } from 'valtio'\n\nconst personState = proxy({ name: 'Timo', role: 'admin' })\nconst authState = proxy({ status: 'loggedIn', user: personState })\n\nauthState.user.name = 'Nina'\n```\n\n## Promises in proxies\n\nSee [`async`](../../guides/async) for more details.\n\n```jsx\nimport { proxy } from 'valtio'\n\nconst bombState = proxy({\n  explosion: new Promise((resolve) => setTimeout(() => resolve('Boom!'), 3000)),\n})\n```\n\n## Gotchas\n\nIf you reassign the proxy to an entirely new object, it will stop working because you are replacing the proxied object with a new object reference.\n\n```jsx\nlet state = proxy({ user: { name: 'Timo' } })\n\nsubscribe(state, () => {\n  console.log(state.user.name)\n})\n// will not notify subscribers\nstate = { user: { name: 'Nina' } }\n\n// instead\nlet state = proxy({ user: { name: 'Timo' } })\n\nsubscribe(state, () => {\n  console.log(state.user.name) // logs \"Nina\"\n})\n// will notify subscribers\nstate.user.name = 'Nina'\n```\n\nNot everything can be proxied. Generally, you are safe if it is serializable. Classes can also be proxied. But avoid special objects.\n\n```jsx\n// these won't work - changes to these objects won't cause updates\n// to store state that is unproxied see the docs on ref\nconst state = proxy({\n  chart: d3.select('#chart'),\n  component: React.createElement('div'),\n  map: new Map(), // see proxyMap\n  storage: localStorage,\n})\n\n// this will work\nclass User {\n  first = null\n  last = null\n  constructor(first, last) {\n    this.first = first\n    this.last = last\n  }\n  greet() {\n    return `Hi ${this.first}!`\n  }\n  get fullName() {\n    return `${this.first} ${this.last}`\n  }\n}\nconst state = proxy(new User('Timo', 'Kivinen'))\n```\n"
  },
  {
    "path": "docs/api/basic/useSnapshot.mdx",
    "content": "---\ntitle: 'useSnapshot'\nsection: 'API'\nsubSection: 'Basic'\ndescription: 'Create a local snapshot that catches changes.'\n---\n\n# `useSnapshot`\n\nCreate a local `snapshot` that catches changes.\n\nNormally, Valtio's snapshots (created via <a href=\"/docs/api/advanced/snapshot\">`snapshot()`</a>) are recreated on _any_ change to a proxy, or any of its child proxies.\n\nHowever `useSnapshot` wraps the Valtio snapshot in an access-tracking proxy. This is to make sure your component is render optimized, i.e. it will only re-render if keys that it (or its child components) specifically accessed has changed, and not on every single change to the proxy.\n\n## Usage\n\n### Read from snapshots in render, use the proxy in callbacks\n\nSnapshots are read-only to render the JSX from their consistent view of the data.\n\nMutations, and also any reads in callbacks that make mutations, need to be made via the proxy, so that the callback reads & writes the latest value.\n\n```jsx\nfunction Counter() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      {snap.count}\n      <button\n        onClick={() => {\n          // also read from the state proxy in callbacks\n          if (state.count < 10) {\n            ++state.count\n          }\n        }}\n      >\n        +1\n      </button>\n    </div>\n  )\n}\n```\n\n### Parent/Child Components\n\nIf you have a parent component use `useSnapshot`, it can pass snapshots to child components and the parent & children will re-render when the snapshot changes.\n\nFor example:\n\n```jsx\nconst state = proxy({\n  books: [\n    { id: 1, title: 'b1' },\n    { id: 2, title: 'b2' },\n  ],\n})\n\nfunction AuthorView() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      {snap.books.map((book) => (\n        <Book key={book.id} book={book} />\n      ))}\n    </div>\n  )\n}\n\nfunction BookView({ book }) {\n  // book is a snapshot\n  return <div>{book.title}</div>\n}\n```\n\nIf book 2's title is changed, a new `snap` is created and the `AuthorView` and `BookView` components will re-render.\n\nNote if `BookView` is `React.memo`d, the 1st `BookView` will not re-render, b/c the 1st `Book` snapshot will be the same instance, as only the 2nd `Book` was mutated (the root `Author` snapshot will also be updated since the list of `books` has changed).\n\n### Child Components Making Mutations\n\nThe above approach works if `BookView` is read-only; if your child component needs to make mutations, then you'll need to pass the proxy:\n\n```jsx\nfunction AuthorView() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      {snap.books.map((book, i) => (\n        <Book key={book.id} book={state.books[i]} />\n      ))}\n    </div>\n  )\n}\n\nfunction BookView({ book }) {\n  // book is the proxy, so we can re-snap it + mutate it\n  const snap = useSnapshot(book)\n  return <div onClick={() => book.updateTitle()}>{snap.title}</div>\n}\n```\n\nOr you can pass both the snapshot and proxy together, if you don't want to call `useSnapshot` in the child component:\n\n```jsx\nfunction AuthorView() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      {snap.books.map((book, i) => (\n        <Book key={book.id} bookProxy={state.books[i]} bookSnapshot={book} />\n      ))}\n    </div>\n  )\n}\n```\n\nThere should be no performance difference between these two approaches.\n\n### Read only what you need\n\nEvery object inside your proxy also becomes a proxy (if you don't use <a href=\"/docs/advanced/ref\">`ref()`</a>). So you can also use them to create\na local snapshot.\n\n```jsx\nfunction ProfileName() {\n  const snap = useSnapshot(state.profile)\n  return <div>{snap.name}</div>\n}\n```\n\n## Gotchas\n\nBeware of replacing the child proxy with something else, breaking your `snapshot`. This will replace the reference of the proxy with what you assign it to which removes the proxy's traps. You can see an example below.\n\n```js\nconsole.log(state)\n{\n  profile: {\n    name: 'valtio'\n  }\n}\nchildState = state.profile\nconsole.log(childState)\n{\n  name: 'valtio'\n}\nstate.profile.name = 'react'\nconsole.log(childState)\n{\n  name: 'react'\n}\nstate.profile = { name: 'new name' }\nconsole.log(childState)\n{\n  name: 'react'\n}\nconsole.log(state)\n{\n  profile: {\n    name: 'new name'\n  }\n}\n```\n\n`useSnapshot()` depends on the original reference of the child proxy so if you replace it with a new one, the component\nthat is subscribed to the old proxy won't receive new updates because it is still subscribed to the old one.\n\nIn this case, we recommend one of the approaches below. In neither example do you need to worry about re-renders because it is render-optimized.\n\n```jsx\nconst snap = useSnapshot(state)\n\nreturn <div>{snap.profile.name}</div>\n```\n\n```jsx\nconst { profile } = useSnapshot(state)\n\nreturn <div>{profile.name}</div>\n```\n\n## Dev Mode Debug Values\n\nIn dev mode, `useSnapshot` uses React's `useDebugValue` to output a list of fields that were accessed during rendering, i.e. which specific fields will trigger re-render when the tracking proxy changes.\n\n<br />\n\n<blockquote className=\"important\">\n!! &nbsp; There are two disclaimers to using the debug value\n\n1. Due to the way `useSnapshot` uses a proxy to recorded accesses _after_ `useSnapshot` has returned, the fields listed in `useDebugValue` are technically from the _previous_ render.\n2. Object getter and class getter calls are not included in the `useDebugValue` output, but don't worry, they are actually correctly tracked internally and correctly trigger re-renders when changed.\n\n</blockquote>\n\n<br />\n\n<br />\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/ping-pong-with-valtio-wb25s?file=/src/App.js\n"
  },
  {
    "path": "docs/api/hacks/getVersion.mdx",
    "content": "---\ntitle: 'getVersion'\nsection: 'API'\nsubSection: 'Hacks'\ndescription: ''\n---\n\n# `getVersion`\n\nIn Valtio, updates to proxied objects are tracked internally with a version number.\n\nEvery mutation to a proxy increases a global version number, and assigns the just mutated proxy, and any parent proxies (which automatically subscribe to their child proxies), to the latest version number.\n\nThis is how `snapshot` knows whether a new snapshot is necessary: has my proxy's version number changed since the last snapshot?\n\nGiven its importance to valtio's internal behavior, the `getVersion` helper can be used to check if a proxied object has been updated, but this is not typically useful or recommended to use in application code because `snapshot` and `useSnapshot` already handle version tracking internally.\n\n```js\ngetVersion(proxyObject)\n```\n"
  },
  {
    "path": "docs/api/hacks/internals.mdx",
    "content": "---\ntitle: 'Internals'\nsection: 'API'\nsubSection: 'Hacks'\ndescription: ''\n---\n\nValtio exposes some internal capabilities.\nThis isn't for app developers. Use it at your own risk.\n\n# `unstable_getInternalStates`\n\nThis function exposes the internal states. Modifying such states may result in wrong behaviors. You need to understand the [source code](https://github.com/pmndrs/valtio/blob/main/src/vanilla.ts) thoroughly to use it.\n\n# `unstable_replaceInternalFunction`\n\nThis function exposes a way to replace some internal functions. It can easily break things. You need to understand the [source code](https://github.com/pmndrs/valtio/blob/main/src/vanilla.ts) thoroughly to use it.\n"
  },
  {
    "path": "docs/api/utils/derive.mdx",
    "content": "---\ntitle: 'derive'\nsection: 'API'\nsubSection: 'Utils'\ndescription: 'create a new proxy derived from others'\n---\n\n## `derive`\n\n> **⚠️ Deprecated**\n>\n> This package is no longer maintained. Please migrate to\n> [valtio-reactive](https://github.com/valtiojs/valtio-reactive).\n\n## Installation\n\n```bash\nnpm install derive-valtio\n```\n\n#### create a new proxy derived from others\n\nYou can subscribe to some proxies using `get` to create snapshots used to compute new values.\n\n```js\nimport { derive } from 'derive-valtio'\n\n// create a base proxy\nconst state = proxy({\n  count: 1,\n})\n\n// create a derived proxy\nconst derived = derive({\n  doubled: (get) => get(state).count * 2,\n})\n\n// alternatively, attach derived properties to an existing proxy\nderive(\n  {\n    tripled: (get) => get(state).count * 3,\n  },\n  {\n    proxy: state,\n  },\n)\n```\n\n## `underive`\n\n## Stop evaluating\n\nIn some cases you may want to unsubscribe after deriving a proxy. To do so, use the `underive` util. You may also pass keys to indicate which properties you want to unsubscribe. If you specify `delete` option, it will delete the properties\nand you can attach new derived properties.\n\n```js\nimport { derive, underive } from 'derive-valtio'\nconst state = proxy({\n  count: 1,\n})\n\nconst derivedState = derive({\n  doubled: (get) => get(state).count * 2,\n})\n\nunderive(derivedState)\n```\n\n## Usage patterns\n\n### Re-computes when unrelated properties change\n\nWith `derive`, the computation will occur if any property of the base proxy changes. Example:\n\n```javascript\nconst baseProxy = proxy({\n  counter1: 0,\n  counter2: 0,\n  counter3: 0,\n  counter4: 0,\n})\n\nconst countersOneAndTwoSelectors = derive({\n  sum: (get) => get(baseProxy).counter1 + get(baseProxy).counter2,\n})\n```\n\nIn this example if `baseProxy.counter3` or `baseProxy.counter4` are changed, `countersOneAndTwoSelectors` will re-compute all of the keys in it.\n\n### `get` sub-objects due to update of unrelated proxies on parent proxy\n\nAs noted on this page, re-computation occurs when unrelated properties change. It is possible to use `get` on sub-objects. This has the benefit of not re-computing when properties of the parent object are updated. Example:\n\n```javascript\nconst baseProxy = proxy({\n  counter1And2: {\n    counter1: 0,\n    counter2: 0,\n  },\n  counter3: 0,\n  counter4: 0,\n})\n\nconst countersOneAndTwoSelectors = derive({\n  sum: (get) =>\n    get(baseProxy.counter1And2).counter1 + get(baseProxy.counter1And2).counter2,\n})\n```\n\nNow even if `counter3` and `counter4` are updated, it will not cause re-computation of `countersOneAndTwoSelectors`.\n"
  },
  {
    "path": "docs/api/utils/devtools.mdx",
    "content": "---\ntitle: 'devtools'\nsection: 'API'\nsubSection: 'Utils'\ndescription: 'Use the Redux DevTools extension with Valtio'\n---\n\n# devtools\n\n#### Dev tools\n\nYou can use [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools) for plain objects and arrays.\n\n```jsx\nimport { devtools } from 'valtio/utils'\n\nconst state = proxy({ count: 0, text: 'hello' })\nconst unsub = devtools(state, { name: 'state name', enabled: true })\n```\n\n<details>\n  <summary>Manipulating state with Redux DevTools</summary>\nThe screenshot below shows how to use Redux DevTools to manipulate state. First select the object from the instances drop down. Then type in a JSON object to dispatch. Then click \"Dispatch\". Notice how it changes the state.\n\n<img width=\"564\" alt=\"image\" src=\"https://user-images.githubusercontent.com/6372489/141134955-26e9ffce-1e2a-4c8c-a9b3-d9da739610fe.png\"/>\n</details>\n\n#### Use it with vanilla JS\n\nValtio is not tied to React, you can use it in vanillaJS.\n\n```jsx\nimport { proxy, subscribe, snapshot } from 'valtio/vanilla'\n\nconst state = proxy({ count: 0, text: 'hello' })\n\nsubscribe(state, () => {\n  console.log('state is mutated')\n  const obj = snapshot(state) // A snapshot is an immutable object\n})\n```\n\n#### Use it with TypeScript\n\nIt's recommended to install and import types from `@redux-devtools/extension` to get types correctly.\n\n```ts\nimport type {} from '@redux-devtools/extension'\nimport { devtools } from 'valtio/utils'\n```\n"
  },
  {
    "path": "docs/api/utils/proxyMap.mdx",
    "content": "---\ntitle: 'proxyMap'\nsection: 'API'\nsubSection: 'Utils'\ndescription: ''\n---\n\n# `proxyMap`\n\n## Reasoning\n\nNative `Maps` store their data in internal slots which are not observable. This means that `valtio` cannot track changes to the data inside of a native `Map`. `proxyMap` is a utility that allows you to create a proxy that mimics the behavior of a `Map` while still allowing valtio to track changes to the data.\n\n## When to use `proxyMap`\n\n`proxyMap` is useful when you need the flexibility of a `Map` but still want to track changes to the data. It can be useful if you don't know the structure of the data you'll be working with and this data may have non-primitive values as keys (e.g. objects, arrays, etc.). In this case, you can use `proxyMap` to create a proxy that mimics the behavior of a `Map` while still allowing valtio to track changes to the data. If your data can be represented as a simple object, you should use `proxy` with a simple object instead. It is more performant and easier to use.\n\n## Use a js Map with Valtio\n\nThis utility creates a proxy which mimics the native Map behavior. The API is the same as the Map API.\n\n```js\nimport { proxyMap } from 'valtio/utils'\n\nconst state = proxyMap()\nstate.size // ---> 0\n\nstate.set(1, 'hello')\nstate.size // ---> 1\n\nstate.delete(1)\nstate.size // ---> 0\n```\n\n## Nesting\n\nIt can be used inside a `proxy` as well.\n\n```js\nimport { proxyMap } from 'valtio/utils'\n\nconst state = proxy({\n  count: 1,\n  map: proxyMap(),\n})\n```\n\nWhen using an object as a key, you can wrap it with `ref` so it's not proxied. This is useful if you want to preserve the key equality\n\n```js\nimport { proxyMap } from 'valtio/utils'\n\n// with ref\nconst key = ref({})\nstate.set(key, 'hello')\nstate.get(key) //hello\n\n// without ref\nconst key = {}\nstate.set(key, 'value')\nstate.get(key) //undefined\n```\n\n## `isProxyMap`\n\nIf you want to check if an object is a proxyMap, you can use the `isProxyMap` function.\n\n```js\nimport { proxy, ref } from 'valtio'\nimport { proxyMap, isProxyMap } from 'valtio/utils'\n\nconst state = proxy({\n  nativeMap: ref(new Map(/*...*/)),\n  proxyMap: proxyMap(),\n})\n\nisProxyMap(state.nativeMap) // false\nisProxyMap(state.proxyMap) // true\n```\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/github/pmndrs/valtio/tree/main/examples/todo-with-proxyMap\n"
  },
  {
    "path": "docs/api/utils/proxySet.mdx",
    "content": "---\ntitle: 'proxySet'\nsection: 'API'\nsubSection: 'Utils'\ndescription: ''\n---\n\n# `proxySet`\n\n## Reasoning\n\nNative `Sets` store their data in internal slots which are not observable. This means that `valtio` cannot track changes to the data inside of a native `Set`. `proxySet` is a utility that allows you to create a proxy that mimics the behavior of a `Set` while still allowing valtio to track changes to the data.\n\n## When to use `proxySet`\n\n`proxySet` is useful when you need the functionality of a `Set` but still want to track changes to the data. `proxySet` can be useful if you're wanting to store unique values or if you want to perform mathematical `Set` operations on the data, such as union, intersection, or difference. `proxySet` supports all of the new methods introduced to `Set`:\n\n- `intersection`\n- `union`\n- `difference`\n- `symmetricDifference`\n- `isSubsetOf`\n- `isSupersetOf`\n- `isDisjointFrom`\n\nYou can see a full list of the methods supported by `proxySet` in the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).\n\nIf your data can be represented as a simple array or object, and you have no need for the additional functionality provided by `proxySet`, you should use `proxy` with a simple array or object instead. It is more performant and easier to use.\n\n## Use a js `Set` with Valtio\n\nThis utility creates a proxy which mimics the native `Set` behavior. The API is the same as the native `Set` API.\n\n```js\nimport { proxySet } from 'valtio/utils'\n\nconst state = proxySet([1, 2, 3])\n\nstate.add(4)\nstate.delete(1)\nstate.forEach((v) => console.log(v)) // --->  2,3,4\n```\n\n## Nesting\n\nIt can be used inside a `proxy` as well.\n\n```js\nimport { proxySet } from 'valtio/utils'\n\nconst state = proxy({\n  count: 1,\n  set: proxySet(),\n})\n```\n\n## `isProxySet`\n\nIf you want to check if an object is a proxyMap, you can use the `isProxySet` function.\n\n```js\nimport { proxy, ref } from 'valtio'\nimport { proxySet, isProxySet } from 'valtio/utils'\n\nconst state = proxy({\n  nativeSet: ref(new Set(/*...*/)),\n  proxySet: proxySet(),\n})\n\nisProxySet(state.nativeSet) // false\nisProxySet(state.proxySet) // true\n```\n"
  },
  {
    "path": "docs/api/utils/proxyWithHistory.mdx",
    "content": "---\ntitle: 'proxyWithHistory'\nsection: 'API'\nsubSection: 'Utils'\ndescription: ''\n---\n\n# `proxyWithHistory`\n\n## Keep a history of snapshots\n\nThis is a utility function to create a proxy with snapshot history.\n\n```js\nimport { proxyWithHistory } from 'valtio-history'\n\nconst state = proxyWithHistory({ count: 0 })\nconsole.log(state.value) // ---> { count: 0 }\nstate.value.count += 1\nconsole.log(state.value) // ---> { count: 1 }\nstate.undo()\nconsole.log(state.value) // ---> { count: 0 }\nstate.redo()\nconsole.log(state.value) // ---> { count: 1 }\n```\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/valtio-history-example-v0-m353xc?file=/src/App.tsx\n"
  },
  {
    "path": "docs/api/utils/subscribeKey.mdx",
    "content": "---\ntitle: 'subscribeKey'\nsection: 'API'\nsubSection: 'Utils'\ndescription: ''\n---\n\n# `subscribeKey`\n\nTo subscribe to a primitive value in proxy state, consider `subscribeKey`.\n\n```js\nimport { subscribeKey } from 'valtio/utils'\n\nconst state = proxy({ count: 0, text: 'hello' })\nsubscribeKey(state, 'count', (v) =>\n  console.log('state.count has changed to', v),\n)\n```\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/dynamic-ui-with-valtio-9gme46?file=/src/ComplexCounter.tsx\n"
  },
  {
    "path": "docs/api/utils/unstable_deepProxy.mdx",
    "content": "---\ntitle: 'unstable_deepProxy'\nsection: 'API'\nsubSection: 'Utils'\ndescription: ''\n---\n\n# `unstable_deepProxy`\n\nYou can use this utility to recursively go through an object and proxy all proxiable objects with the exception of anything marked with `ref()`. `Map`s will become `proxyMap`s and `Set`s will become `proxySet`s.\n\n```js\nimport {\n  unstable_deepProxy as deepProxy,\n  isProxyMap,\n  isProxySet,\n} from 'valtio/utils'\n\nconst obj = {\n  mySet: new Set(),\n  myMap: new Map(),\n  sub: {\n    foo: 'bar',\n  },\n}\n\nconst clonedProxy = deepProxy(obj)\n\nconsole.log(isProxyMap(clonedProxy.myMap)) // true\nconsole.log(isProxySet(clonedProxy.mySet)) // true\n```\n"
  },
  {
    "path": "docs/api/utils/watch.mdx",
    "content": "---\ntitle: 'watch'\nsection: 'API'\nsubSection: 'Utils'\ndescription: 'watch for changes.'\n---\n\n# `watch`\n\n> **⚠️ Deprecated**\n>\n> This util is no longer maintained. Please migrate to\n> [valtio-reactive](https://github.com/valtiojs/valtio-reactive).\n\n## Subscription via a getter\n\nThis utility supports subscribing to multiple proxy objects (unlike `subscribe` which listens to only a single proxy). Proxy objects are subscribed with a `get` function passed to the callback.\n\nAny changes to the proxy object (or its child proxies) will rerun the callback.\n\nAlso note the callback will run once immediately when `watch` is called, even if the proxies have not been yet mutated, to establish the initial subscriptions.\n\n```js\nimport { proxy } from 'valtio'\nimport { watch } from 'valtio/utils'\n\nconst userState = proxy({ user: { name: 'Juuso' } })\nconst sessionState = proxy({ expired: false })\n\nwatch((get) => {\n  // `get` adds `sessionState` to this callback's watched proxies\n  get(sessionState)\n  const expired = sessionState.expired\n  // Or call it inline\n  const name = get(userState).user.name\n  console.log(`${name}'s session is ${expired ? 'expired' : 'valid'}`)\n})\n// 'Juuso's session is valid'\nsessionState.expired = true\n// 'Juuso's session is expired'\n```\n\n## Cleanup\n\nYou may return a cleanup function that runs both:\n\n- Before each re-invocation of the callback (i.e. due to a watched proxy changing)\n- When the `watch` itself is stopped (by calling the cleanup function returned by `watch`)\n\n```js\nwatch((get) => {\n  const expired = get(sessionState).expired\n  const name = get(userState).user.name\n  console.log(`${name}'s session is ${expired ? 'expired' : 'valid'}`)\n  return () => {\n    if (expired) {\n      console.log('Cleaning up')\n    }\n  }\n})\n// Output from 1st immediate invocation of the callbcak\n// 'Juuso's session is valid'\n\n// Changing a dependency will invoke the cleanup callback first,\n// but the captured `expired` is false, so we only see output from the\n// 2nd invocation of the callback.\nsessionState.expired = true\n// 'Juuso's session is expired'\n\n// Changing a dependency will again invoke the cleanup callback,\n// and `expired` is true now, so we output from both our cleanup\n// function as well as the 3rd invocation of the callback.\nsetTimeout(() => {\n  userState.user.name = 'Anonymous'\n}, 200)\n// 200ms -> 'Anonymous's session is expired'\n// 'Cleaning up' logged\n```\n\n## Gotchas\n\nIf you remove the setTimeout in the example above, `'Juuso's session is expired'` will become `'Anonymous's session is expired'` and it will be logged twice. Valtio will batch updates by default. You may pass `{sync: true}` as a second argument to `watch` to disable batching.\n\n## No usage tracking\n\n`watch` currently does not implement the usage tracking of `useSnapshot`, so the callback will rerun anytime a watched proxy (or child proxy) has any fields mutated, regardless of the fields accessed within the `callback`'s code.\n\nAnd the return value of `get` is just the proxy itself, not a snapshot.\n\nThis is because `watch` is a vanilla primitive built on top of `subscribe`. It is potentially possible for `watch`, or a new `watch`-like method, to implement usage tracking in the future, see [this discussion](https://github.com/pmndrs/valtio/discussions/640) if interested.\n"
  },
  {
    "path": "docs/guides/async.mdx",
    "content": "---\ntitle: 'Async'\nsection: 'Advanced'\ndescription: 'Working with promises and Suspense'\n---\n\n# `async`\n\n## Promises\n\nPromises may be values in a proxied object. They will be resolved in calls to `snapshot`.\n\n```jsx\n// vanillajs example\nconst countDiv: HTMLElement | null = document.getElementById('count')\nif (countDiv) countDiv.innerText = '0'\n\nconst store = proxy({\n  count: new Promise((r) => setTimeout(() => r(1), 1000)),\n})\n\nsubscribe(store, () => {\n  const value = snapshot(store).count\n  if (countDiv && typeof value === 'number') {\n    countDiv.innerText = String(value)\n    store.count = new Promise((r) => setTimeout(() => r(value + 1), 1000))\n  }\n})\n```\n\n## Suspend your React components\n\nValtio is compatible with React 19 `use` hook. This eliminates all the async back-and-forth, you can access your data directly while the parent is responsible for fallback state and error handling.\n\n```jsx\nimport { use } from 'react' // React 19\n// import { use } from 'react18-use' // React 18\n\nconst state = proxy({ post: fetch(url).then((res) => res.json()) })\n\nfunction Post() {\n  const snap = useSnapshot(state)\n  return <div>{use(snap.post).title}</div>\n}\n\nfunction App() {\n  return (\n    <Suspense fallback=\"Loading...\">\n      <Post />\n    </Suspense>\n  )\n}\n```\n\nIt still suffers from \"de-opt\", which prevents `useTransition` to work well. To mitigate it, there is a third-party library [use-valtio](https://github.com/valtiojs/use-valtio).\n\n## Codesandbox Pokemon fetch demo\n\nhttps://codesandbox.io/s/valtio-pokemon-fetch-x1lkbj?file=/src/App.tsx\n\n## Codesandbox auth demo\n\nhttps://codesandbox.io/s/valtio-async-1pstl1?file=/src/App.tsx\n"
  },
  {
    "path": "docs/guides/component-state.mdx",
    "content": "---\ntitle: 'Component State'\nsection: 'Advanced'\ndescription: 'Isolate component state with useRef'\n---\n\n# Component State\n\nTo isolate component state for reusability, Valtio must live in the React lifecycle. You can wrap a `proxy` in a ref, passing it with props or context.\n\n```jsx\nimport { createContext, useContext } from 'react'\nimport { proxy, useSnapshot } from 'valtio'\n\nconst MyContext = createContext()\n\nconst MyProvider = ({ children }) => {\n  const state = useRef(proxy({ count: 0 })).current\n  return <MyContext.Provider value={state}>{children}</MyContext.Provider>\n}\n\nconst MyCounter = () => {\n  const state = useContext(MyContext)\n  const snap = useSnapshot(state)\n  return (\n    <>\n      {snap.count} <button onClick={() => ++state.count}>+1</button>\n    </>\n  )\n}\n```\n\n## Codesandbox example\n\nhttps://codesandbox.io/s/valtio-component-ye5tbg?file=/src/App.tsx\n\n## Alternatives\n\nIf you are not happy with `useRef` usage, consider:\n\n- [use-constant](https://www.npmjs.com/package/use-constant)\n- [bunshi](https://www.bunshi.org/recipes/valtio/)\n- You may also create custom hooks wrapping useContext and optionally useSnapshot.\n\n### Bunshi example\n\nhttps://codesandbox.io/s/77r53c?file=/molecules.ts\n"
  },
  {
    "path": "docs/guides/computed-properties.mdx",
    "content": "---\ntitle: 'Computed Properties'\nsection: 'Advanced'\ndescription: 'Use object getters and setters'\n---\n\n# Computed Properties\n\nIn Valtio you can use object & class getters and setters to create computed properties.\n\n<br />\n\n<blockquote className=\"note\">\nℹ️ &nbsp; Note\n\nGetters in JavaScript are a more advanced feature of the language, so Valtio recommends using them with caution. That said, if you are a more advanced JavaScript programmer, they should work as you expect; see the \"Note about using `this`\" section below.\n\n</blockquote>\n<br />\n\n<br />\n\n## Simple object getter\n\n```js\nconst state = proxy({\n  count: 1,\n  get doubled() {\n    return this.count * 2\n  },\n})\nconsole.log(state.doubled) // 2\n\n// Getter calls on the snapshot work as expected\nconst snap = snapshot(state)\nconsole.log(snap.doubled) // 2\n\n// When count changes in the state proxy\nstate.count = 10\n// Then snapshot's computed property does not change\nconsole.log(snap.doubled) // 2\n```\n\nWhen you call `state.doubled` on the `state` proxy, it is not cached, and will be re-calculated on every call (if you must cache this result, see the section below on `proxy-memoize`).\n\nHowever, when you make a snapshot, calls to `snap.doubled` are effectively cached, because the value of an object getter is copied during the snapshot process.\n\n<br />\n\n<blockquote className=\"note\">\nℹ️ &nbsp; Note\n\nIn the current implementation a computed property should only reference \\*\\*\\_sibling\\*\\*\\* properties, otherwise you'll encounter weird bugs. For example:\n\n</blockquote>\n\n<br />\n\n```js\nconst user = proxy({\n  name: 'John',\n  // OK - can reference sibling props via `this`\n  get greetingEn() {\n    return 'Hello ' + this.name\n  },\n})\n```\n\n```js\nconst state = proxy({\n  // could be nested\n  user: {\n    name: 'John',\n    // OK - can reference sibling props via `this`\n    get greetingEn() {\n      return 'Hello ' + this.name\n    },\n  },\n})\n```\n\n```js\nconst state = proxy({\n  user: {\n    name: 'John',\n  },\n  greetings: {\n    // WRONG - `this` points to `state.greetings`.\n    get greetingEn() {\n      return 'Hello ' + this.user.name\n    },\n  },\n})\n```\n\n```js\nconst user = proxy({\n  name: 'John',\n})\nconst greetings = proxy({\n  // WRONG - `this` points to `greetings`.\n  get greetingEn() {\n    return 'Hello ' + this.name\n  },\n})\n```\n\nA workaround to it is to attach the related object as a property.\n\n```js\nconst user = proxy({\n  name: 'John',\n})\nconst greetings = proxy({\n  user, // attach the `user` proxy object\n  // OK - can reference user props via `this`\n  get greetingEn() {\n    return 'Hello ' + this.user.name\n  },\n})\n```\n\nAnother method would be to create a separate proxy and\nsynchronize with `subscribe`.\n\n```js\nconst user = proxy({\n  name: 'John',\n})\nconst greetings = proxy({\n  greetingEn: 'Hello ' + user.name,\n})\nsubscribe(user, () => {\n  greetings.greetingEn = 'Hello ' + user.name\n})\n```\n\nOr with `watch`.\n\n```js\nconst user = proxy({\n  name: 'John',\n})\nconst greetings = proxy({})\nwatch((get) => {\n  greetings.greetingEn = 'Hello ' + get(user).name\n})\n```\n\n## Object getter and setter\n\nSetters are also supported:\n\n```js\nconst state = proxy({\n  count: 1,\n  get doubled() {\n    return state.count * 2\n  },\n  set doubled(newValue) {\n    state.count = newValue / 2\n  },\n})\n\n// Setter calls on the state work as expected\nstate.doubled = 4\nconsole.log(state.count) // 2\n\n// Getter calls on the snapshot work as expected\nconst snap = snapshot(state)\nconsole.log(snap.doubled) // 4\n\n// And setter calls on the snapshot fail as expected\n// compile error: Cannot assign to 'doubled' because it is a read-only property.\n// runtime error: TypeError: Cannot assign to read only property 'doubled' of object '#<Object>'\nsnap.doubled = 2\n```\n\nAs with getters, setter calls within `set doubled` (i.e. `this.count = newValue / 2`) are themselves invoked against the `state` proxy, so the new `count` value will be correctly updated (and subscribers/snapshots notified of the new change).\n\nIf you make a snapshot, all properties become readonly, so `snap.doubled = 2` will be a compile error, and will also fail at runtime because `snapshot` objects are frozen.\n\n## Class getters and setters\n\nClass getters and setters work effectively like object getters and setters:\n\n```js\nclass Counter {\n  count = 1\n  get doubled() {\n    return this.count * 2\n  }\n  set doubled(newValue) {\n    this.count = newValue / 2\n  }\n}\n\nconst state = proxy(new Counter())\nconst snap = snapshot(state)\n\n// Changing the state works as expected\nstate.doubled = 4\nconsole.log(state.count) // 2\n// And the snapshot value doesn't change\nconsole.log(snap.doubled) // 2\n```\n\nSimilar to object getters, class getters on the `state` proxy are not cached.\n\nHowever, unlike object getters, class getters on the `snapshot` object are not cached, and are re-evaulated on every access to `snap.doubled`. As mentioned in the `snapshot` docs, this should be fine because the expectation is that getters are as cheap to evaluate as they would be to cache.\n\nAlso unlike object setters on snapshots (which immediately fail at runtime when called), class setters on snapshots will technically start evaluating, but any mutations they do internally (i.e. `this.count = newValue / 2`) will then fail at runtime because `this` will be a snapshot instance and the snapshots are frozen by `Object.freeze`.\n\n## State usage tracking with `proxy-memoize`\n\nIf you need to cache getter results even for the `state` proxy itself, you can use Valtio's sister project [proxy-memoize](https://github.com/dai-shi/proxy-memoize).\n\n`proxy-memoize` uses a similar usage-based tracking approach as Valtio's `snapshot` function, so it will only re-calculate the getter if fields accessed by the getter logic have actually changed.\n\n```js\nimport { memoize } from 'proxy-memoize'\n\nconst memoizedDoubled = memoize((snap) => snap.count * 2)\n\nconst state = proxy({\n  count: 1,\n  text: 'hello',\n  get doubled() {\n    return memoizedDoubled(snapshot(state))\n  },\n})\n```\n\nWith this implementation, when `text` property changes (but `count` has not), the memoized function won't be re-executed.\n\n## Note about using `this`\n\nYou can use `this` inside of getters and setters, however you should be familiar with how JS `this` works: basically that `this` is whatever object you invoked the call against.\n\nSo if you call `state.doubled`, then `this` will be the `state` proxy.\n\nAnd if you call `snap.doubled`, then `this` will be the snapshot object (except for object getters & setters, where the current value is copied during the snapshot process, so object getters & setters are never invoked on snapshots).\n\nDespite this nuance, you should be able to use `this` as you would expect, and things will \"just work\".\n"
  },
  {
    "path": "docs/guides/migrating-to-v2.mdx",
    "content": "---\ntitle: 'How to Migrate to v2 from v1'\n---\n\n# How to Migrate to v2 from v1\n\n## Changes in v2\n\nReact 19 officially introduces the `use` hook to handle promises.\nValtio v1 internally handled promises, which is no longer recommended.\nIn Valtio v2, promises are not handled internally,\nand developers should explicitly use the `use` hook to manage promises.\n\nNote: If you are still using React 18, you can use [the `use` hook shim](https://github.com/dai-shi/react18-use).\n\nValtio v2 also introduces two subtle changes in its design choices:\n\nFirst, the behavior of `proxy(obj)` has changed. In v1, it was a pure function and deeply copied `obj`. In v2, it is an impure function and deeply modifies `obj`. Generally, reusing `obj` is not recommended. Unless you reuse `obj`, nothing will break.\n\nSecond, the behavior of `useSnapshot()` has been altered. Although it is a subtle change, it is less optimized to ensure compatibility with `useMemo` and the upcoming React compiler. The change may lead to extra re-renders in some edge cases, but it might not be noticeable.\n\nOther notable changes to keep things updated and fresh include:\n\n- Removal of all deprecated features\n- Requirement of React version 18 and above\n- Requirement of TypeScript version 4.5 and above\n- The build target updated to ES2018\n\n## Migration for breaking changes\n\n### Resolving promises\n\n```js\n// v1\nimport { proxy, useSnapshot } from 'valtio'\n\nconst state = proxy({ data: fetch(...).then((res) => res.json()) })\n\nconst Component = () => {\n  const snap = useSnapshot(state)\n  return <>{JSON.stringify(snap.data)}</>\n}\n```\n\n```js\n// v2\nimport { use } from 'react'\nimport { proxy, useSnapshot } from 'valtio'\n\nconst state = proxy({ data: fetch(...).then((res) => res.json()) })\n\nconst Component = () => {\n  const snap = useSnapshot(state)\n  return <>{JSON.stringify(use(snap.data))}</>\n  // If `data` is not an object, you can directly embed it in JSX.\n  // return <>{snap.data}</>\n}\n```\n\n### Impure `proxy(obj)`\n\nIf you don't reuse the object you pass to the proxy, nothing will break.\n\n```js\nimport { proxy } from 'valtio'\n\n// This works in both v1 and v2\nconst state = proxy({ count: 1, obj: { text: 'hi' } })\n\n// This works in both v1 and v2\nstate.obj = { text: 'hello' }\n```\n\nThat's the recommended way to use `proxy`.\n\nFor some reason, if you reuse the object, you need to use `deepClone` explicitly in v2 to keep the same behavior as v1.\n\n```js\n// v1\nimport { proxy } from 'valtio'\n\nconst initialObj = { count: 1, obj: { text: 'hi' } }\nconst state = proxy(initialObj)\n// and do something later with `initialObj`\n\nconst newObj = { text: 'hello' }\nstate.obj = newObj\n// and do something later with `newObj`\n```\n\n```js\n// v2\nimport { proxy } from 'valtio'\nimport { deepClone } from 'valtio/utils'\n\nconst initialObj = { count: 1, obj: { text: 'hi' } }\nconst state = proxy(deepClone(initialObj))\n// and do something later with `initialObj`\n\nconst newObj = { text: 'hello' }\nstate.obj = deepClone(newObj)\n// and do something later with `newObj`\n```\n\n### `useLayoutEffect` server warning\n\nIf you're using React 18 with SSR, add this conditional to prevent excessive warnings.\n\n```js\nimport { snapshot, useSnapshot as useSnapshotOrig } from 'valtio'\n\nconst isSSR = typeof window === 'undefined'\nexport const useSnapshot = isSSR ? (p) => snapshot(p) : useSnapshotOrig\n\n// render with `useSnapshot` as usual\n```\n\n## Links\n\n- https://github.com/pmndrs/valtio/discussions/703\n- https://github.com/pmndrs/valtio/pull/810\n"
  },
  {
    "path": "docs/how-tos/how-to-avoid-rerenders-manually.mdx",
    "content": "---\ntitle: 'How to avoid rerenders manually'\n---\n\n# How to avoid rerenders manually\n\n## `useSnapshot` optimizes re-renders automatically\n\nThis is the basic usage.\n\n```jsx\nconst Component = () => {\n  const { count } = useSnapshot(state) // this is reactive\n  return <>{count}</>\n}\n```\n\n## Reading state is valid but not recommended for general use cases\n\n```jsx\nconst Component = () => {\n  const { count } = state // this is not reactive\n  return <>{count}</>\n}\n```\n\nThis will not trigger re-render, but it doesn't follow the react rule like with any other global variables.\n\n## Subscribe and set local state conditionally\n\n```jsx\nconst Component = () => {\n  const [count, setCount] = useState(state.count)\n  useEffect(\n    () =>\n      subscribe(state, () => {\n        if (state.count % 2 === 0) {\n          // conditionally update local state\n          setCount(state.count)\n        }\n      }),\n    [],\n  )\n  return <>{count}</>\n}\n```\n\nThis should work mostly.\n\nTheoretically, state can be changed before the subscription. A fix would be the following.\n\n```jsx\nconst Component = () => {\n  const [count, setCount] = useState(state.count)\n  useEffect(() => {\n    const callback = () => {\n      if (state.count % 2 === 0) {\n        // conditionally update local state\n        setCount(state.count)\n      }\n    }\n    const unsubscribe = subscribe(state, callback)\n    callback()\n    return unsubscribe\n  }), [])\n  return <>{count}</>\n}\n```\n\nFor some use cases, using [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore) could be easier.\n"
  },
  {
    "path": "docs/how-tos/how-to-easily-access-the-state-from-anywhere-in-the-application.mdx",
    "content": "---\ntitle: 'How to easily access the state from anywhere in the application'\n---\n\n# How to easily access the state from anywhere in the application\n\nWhen working with large applications organizing code in separate files and directories is the go-to way and the Valtio **state** is no exception. In some ways you may want to put the state object in its own file. After being separated in its own file we need a way to access it easily from anywhere in our application.\n\n## Access the state using Path Aliases\n\nImagine that the state is put in `/src/state.js` and you are working with a file in `/src/really/deep/nested/file/myfile.js` the importing of the state will be something like this:\n`import state from '../../../../state';` which can cause too much brain calculation specially if it is used in different places inside the application.\n\nA solution to that is using **Path Aliases** which maps path to a simpler string and the import will look like something similar to that throughout the whole application:\n\n`import { state } from '@state';`\n\n## Using JS Config and Babel Config\n\n1. Create the file `/src/state` and put the Valtio **state** into it:\n\n```js\nimport { proxy, useSnapshot, subscribe } from 'valtio'\nconst state = proxy({\n  foos: [],\n  bar: { ... },\n  boo: false\n})\nexport { state, useSnapshot, subscribe }\n```\n\n2. Create the file `/jsconfig.json` (or `/tsconfig.json` if you're using typescript):\n\n```json\n{\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\",\n    \"paths\": {\n      \"@state/*\": [\"./state/*\"],\n      \"@mypath/*\": [\"./my/deep/path*\"],\n      \"@anotherpath/*\": [\"./my/another/deep/path*\"]\n    }\n  },\n  \"exclude\": [\"node_modules\"]\n}\n```\n\n<br />\n\n<blockquote className=\"tip\">\n 💡 &nbsp; Using TypeScript? Here are some links for reference\n\nhttps://www.totaltypescript.com/tsconfig-cheat-sheet<br />\nhttps://github.com/tsconfig/bases<br />\nhttps://www.typescriptlang.org/tsconfig/\n\n</blockquote>\n\n<br />\n\n<br />\n\n3. Add the **Module Resolver** plugin the plugins in your `babel.config.js`:\n\n```js\nmodule.exports = {\n  // ...\n  plugins: [\n    // The other existing plugins\n    [\n      'module-resolver',\n      {\n        root: ['./src'],\n        extensions: ['.js', '.jsx', '.json', '.svg', '.png'],\n        alias: {\n          '@state': './src/state',\n        },\n      },\n    ],\n    // ...\n  ],\n}\n```\n\n4. Install the the Babel Plugin Module Resolver:\n\n- Using NPM: `npm install babel-plugin-module-resolver`\n- Using Yarn: `yarn add babel-plugin-module-resolver`\n\n5. Restart the application server\n   <br />\n   That's it you will now be able to do `import\n   {(state, useSnapshot, subscribe)} from '@state';` from anywhere inside your\n   application.\n\n## Using a third party library\n\nYou can use a third party library to create aliases and achieve the same result.\n\nExample of libraries:\n\n- [Module Alias](https://www.npmjs.com/package/module-alias)\n"
  },
  {
    "path": "docs/how-tos/how-to-organize-actions.mdx",
    "content": "---\ntitle: 'How to organize actions'\n---\n\n# How to organize actions\n\nValtio is unopinionated about organizing actions.\nHere's some recipes to show various patterns are possible.\n\n## Action functions defined in module\n\n<br />\n\n<blockquote className=\"note\">\nℹ️ &nbsp; Note\n\nThis way is preferred as it is better for code splitting.\n\n</blockquote>\n\n<br />\n\n<br />\n\n```js\nimport { proxy } from 'valtio'\n\nexport const state = proxy({\n  count: 0,\n  name: 'foo',\n})\n\nexport const inc = () => {\n  ++state.count\n}\n\nexport const setName = (name) => {\n  state.name = name\n}\n```\n\n## Action object defined in module\n\n```js\nimport { proxy } from 'valtio'\n\nexport const state = proxy({\n  count: 0,\n  name: 'foo',\n})\n\nexport const actions = {\n  inc: () => {\n    ++state.count\n  },\n  setName: (name) => {\n    state.name = name\n  },\n}\n```\n\n## Action methods defined in state\n\n```js\nexport const state = proxy({\n  count: 0,\n  name: 'foo',\n  inc: () => {\n    ++state.count\n  },\n  setName: (name) => {\n    state.name = name\n  },\n})\n```\n\n## Action methods using `this`\n\n```js\nexport const state = proxy({\n  count: 0,\n  name: 'foo',\n  inc() {\n    ++this.count\n  },\n  setName(name) {\n    this.name = name\n  },\n})\n```\n\n## Using class\n\n```js\nclass State {\n  count = 0\n  name = 'foo'\n  inc() {\n    ++this.count\n  }\n  setName(name) {\n    this.name = name\n  }\n}\n\nexport const state = proxy(new State())\n```\n"
  },
  {
    "path": "docs/how-tos/how-to-persist-states.mdx",
    "content": "---\ntitle: 'How to persist states'\n---\n\n# How to persist states\n\n## persist with localStorage\n\nIf your state is JSON serializable, it should be pretty straightforward.\n\n```js\nconst state = proxy(\n  JSON.parse(localStorage.getItem('foo')) || {\n    count: 0,\n    text: 'hello',\n  },\n)\n\nsubscribe(state, () => {\n  localStorage.setItem('foo', JSON.stringify(state))\n})\n```\n\nIf you have non serializable values, attach them after deserialization and exclude them for serialization.\n\n**_[valtio-persist](https://github.com/Noitidart/valtio-persist) is a library that can help with this._**\n"
  },
  {
    "path": "docs/how-tos/how-to-reset-state.mdx",
    "content": "---\ntitle: 'How to reset state'\n---\n\n# How to reset state\n\nIn some cases, you might want to reset the state in your proxy instance to its initial values. For example, you are storing form values or some other ephemeral UI state that you want to reset. It turns out this is quite simple to do!\n\n```js\nimport { proxy } from 'valtio'\nimport { deepClone } from 'valtio/utils'\n\nconst initialObj = {\n  text: 'hello',\n  arr: [1, 2, 3],\n  obj: { a: 'b' },\n}\n\nconst state = proxy(deepClone(initialObj))\n\nconst reset = () => {\n  const resetObj = deepClone(initialObj)\n  Object.keys(resetObj).forEach((key) => {\n    state[key] = resetObj[key]\n  })\n}\n```\n\nNote that we're using the `deepClone()` utility function from `valtio/utils` to copy the initial object in _both_ the `reset` function and the `state` proxy. Using deepClone in the proxy function is a new requirement in v2. Valtio no longer clones the initial state by default. If you reuse the object you pass into the proxy function, you may get unexpected results.\n\nAlternatively, you can store the object in another object, which make the reset logic easier:\n\n```js\nconst state = proxy({ obj: initialObj })\n\nconst reset = () => {\n  state.obj = deepClone(initialObj)\n}\n```\n\n<br />\n\n<blockquote className=\"note\">\nℹ️ &nbsp; Note\n\nUsing `structuredClone()`\n\n<br />\n\nIn 2022, there was a new global function added called `structuredClone` that is widely available in most modern browsers. You can use `structuredClone` in the same way as `deepClone` above, however `deepClone` is preferred as it will be aware of any `ref`s in your state.\n\n</blockquote>\n\n> Note: deepClone will convert proxyMap and proxySet back to plain objects. If you have an object that has these within its tree, consider using `unstable_deepProxy` instead.\n"
  },
  {
    "path": "docs/how-tos/how-to-split-and-compose-states.mdx",
    "content": "---\ntitle: 'How to split and compose states'\n---\n\n# How to split and compose states\n\n## You can split states\n\nCreating a state with nested object.\n\n```js\nconst state = proxy({\n  obj1: { a: 1 },\n  obj2: { b: 2 },\n})\n```\n\nYou can then split the state into pieces. They are both proxies.\n\n```js\nconst obj1State = state.obj1\nconst ojb2State = state.obj2\n```\n\n## You can combine states\n\nYou can create states and then combine them.\n\n```js\nconst obj1State = proxy({ a: 1 })\nconst obj2State = proxy({ a: 2 })\n\nconst state = proxy({\n  obj1: obj1State,\n  obj2: obj2State,\n})\n```\n\nThis works equivalently to the previous example.\n\n## You can create circular states\n\nWhile there would be less use cases, you could create a circular structure.\n\n```js\nconst state = proxy({\n  obj: { foo: 3 },\n})\n\nstate.obj.bar = state.obj // 🤯\n```\n"
  },
  {
    "path": "docs/how-tos/how-to-update-values-inside-arrays.mdx",
    "content": "---\ntitle: 'How to update values inside arrays'\n---\n\n# How to update values inside arrays\n\nAvoid unnecessary re-renders when updating values inside arrays.\n\n## Basic approach (triggers full list re-render)\n\nIn this example, iterating over the snapshot items causes the entire list to re-render whenever any item changes.\n\n```js\nimport { proxy, useSnapshot } from 'valtio'\n\nconst state = proxy({\n  title: 'My Counter list',\n  items: [\n    { id: 1, count: 0 },\n    { id: 2, count: 0 },\n  ],\n})\n\nfunction Counter({ item }) {\n  return (\n    <div>\n      <span>{item.count}</span>\n      <button onClick={() => item.count++}>+1</button>\n    </div>\n  )\n}\n\nfunction CounterList() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      <h1>{snap.title}</h1>\n      {/* Touching snap.items causes the whole list to re-render when any child updates */}\n      {snap.items.map((item) => (\n        <Counter key={item.id} item={item} />\n      ))}\n    </div>\n  )\n}\n```\n\n## Optimized approach (only changed items re-render)\n\nPass the proxy item (not snapshot) to children and let each child subscribe to its own item.\nDo not access the entire array from the snapshot in the parent component.\n\n```js\nimport { proxy, useSnapshot } from 'valtio'\n\nconst state = proxy({\n  title: 'My Counter list',\n  items: [\n    { id: 1, count: 0 },\n    { id: 2, count: 0 },\n  ],\n})\n\nfunction Counter({ item }) {\n  const snap = useSnapshot(item)\n  return (\n    <div>\n      <span>{snap.count}</span>\n      <button onClick={() => item.count++}>+1</button>\n    </div>\n  )\n}\n\nfunction CounterList() {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      <h1>{snap.title}</h1>\n      {/* Only the length is accessed from snap, so only updated children re-render */}\n      {Array.from({ length: snap.items.length }, (_, index) => (\n        {/* Note that we are passing the proxy object (state) instead of the snapshot (snap) to the child component */}\n        <Counter key={state.items[index].id} item={state.items[index]} />\n      ))}\n    </div>\n  )\n}\n```\n\n## Why this works\n\nBecause the proxy (in this case: `state.items[index]`) is a proxy object instead of a snapshot object, accessing it does not track usage. This prevents the parent component from \"subscribing\" to the item's internal changes, effectively isolating updates to the child component.\n\nWhen you call `snap.items.map()`, you access every item in the array, causing Valtio to re-render whenever _any_ item changes. By only accessing `snap.items.length`, Valtio only re-renders when the array length changes. Using `Array.from({ length: n }, (_, i) => ...)` creates an array by index without touching snapshot items, letting you access the proxy directly.\n"
  },
  {
    "path": "docs/how-tos/how-to-use-with-context.mdx",
    "content": "---\ntitle: 'How to use with context'\n---\n\n# How to use with context\n\nTo make a valtio state only live in React lifecycle, you can create a state in a ref, and you can pass it with props or context.\n\n## A basic pattern with context\n\n```jsx\nimport { createContext, useContext } from 'react'\nimport { proxy, useSnapshot } from 'valtio'\n\nconst MyContext = createContext()\n\nconst MyProvider = ({ children }) => {\n  const state = useRef(proxy({ count: 0 })).current\n  return <MyContext.Provider value={state}>{children}</MyContext.Provider>\n}\n\nconst MyCounter = () => {\n  const state = useContext(MyContext)\n  const snap = useSnapshot(state)\n  return (\n    <>\n      {snap.count} <button onClick={() => ++state.count}>+1</button>\n    </>\n  )\n}\n```\n\n## Alternatives\n\nIf you are not happy with `useRef` usage, consider:\n\n- [use-constant](https://www.npmjs.com/package/use-constant)\n- [bunshi](https://www.bunshi.org/recipes/valtio/)\n- You can create custom hooks to `useContext` and optionally `useSnapshot`\n\n### Bunshi example\n\nhttps://codesandbox.io/s/77r53c?file=/molecules.ts\n"
  },
  {
    "path": "docs/how-tos/how-valtio-works.mdx",
    "content": "---\ntitle: 'How valtio works'\n---\n\n# How valtio works\n\nRef: https://github.com/pmndrs/valtio/issues/171\n\nThis is to describe the high level abstraction of valtio.\n\n## Articles\n\n- [How Valtio Proxy State Works (Vanilla Part)](https://blog.axlight.com/posts/how-valtio-proxy-state-works-vanilla-part/)\n- [How Valtio Proxy State Works (React Part)](https://blog.axlight.com/posts/how-valtio-proxy-state-works-react-part/)\n\n## Examples\n\n### `proxy()` by examples\n\n```js\nimport { proxy, subscribe } from 'valtio'\n\nconst s1 = proxy({})\nsubscribe(s1, () => {\n  console.log('s1 is changed!')\n})\ns1.a = 1 // s1 is changed!\n++s1.a // s1 is changed!\ndelete s1.a // s1 is changed!\ns1.b = 2 // s1 is changed!\ns1.b = 2 // (not changed)\ns1.obj = {} // s1 is changed!\ns1.obj.c = 3 // s1 is changed!\nconst s2 = s1.obj\nsubscribe(s2, () => {\n  console.log('s2 is changed!')\n})\ns1.obj.d = 4 // s1 is changed! and s2 is changed!\ns2.d = 5 // s1 is changed! and s2 is changed!\nconst s3 = proxy({})\nsubscribe(s3, () => {\n  console.log('s3 is changed!')\n})\ns1.o = s3\ns3.p = 'hello' // s1 is changed! and s3 is changed!\ns2.q = s3\ns3.p = 'hi' // s1 is changed! s2 is changed! and s3 is changed!\ns1.x = s1\ns1.a += 1 // s1 is changed!\n```\n\n### `snapshot()` by examples\n\n```js\nimport { proxy, snapshot } from 'valtio'\n\nconst p = proxy({})\nconst s1 = snapshot(p) // is {} but not wrapped by a proxy\nconst s2 = snapshot(p)\ns1 === s2 // is true because p wasn't changed\np.a = 1 // mutate the proxy\nconst s3 = snapshot(p) // is { a: 1 }\np.a = 1 // mutation bails out and proxy is not updated\nconst s4 = snapshot(p)\ns3 === s4 // is still true\np.a = 2 // mutate it\nconst s5 = snapshot(p) // is { a: 2 }\np.a = 1 // mutate it back\nconst s6 = snapshot(p) // creates a new snapshot\ns3 !== s6 // is true (different snapshots, even though they are deep equal)\np.obj = { b: 2 } // attaching a new object, which will be wrapped by a proxy\nconst s7 = snapshot(p) // is { a: 1, obj: { b: 2 } }\np.a = 2 // mutating p\nconst s8 = snapshot(p) // is { a: 2, obj: { b: 2 } }\ns7 !== s8 // is true because a is different\ns7.obj === s8.obj // is true because obj is not changed\n```\n\n### `useSnapshot()` by examples\n\n```jsx\nimport { proxy, useSnapshot } from 'valtio'\n\nconst s1 = proxy({\n  counter: 0,\n  text: 'Good morning from valtio',\n  foo: {\n    boo: 'baz'\n  }\n})\n\nconst MyComponent = () => {\n  // Using destructuring\n  const { text, counter } = useSnapshot(state)\n\n  // Multilevel destructiong works as well\n  const { text, counter, { foo }} = useSnapshot(state)\n\n  // Assigning to a snapshot obeject\n  const snap = useSnapshot(state)\n\n\n  return (() => {\n    <div id=\"main\">\n      <h1>{ `${foo} - ${text}` }</h1>\n      {/* - or - */}\n      <h1>{ `${snap.foo.bar} = `${snap.text}}</h1>\n      <div>\n        <input\n          type=\"input\"\n\n          {/* we use snapshot for reading */}\n          value={text}\n\n          {/* the line above equivalent to this */}\n          value={snap.text}\n\n          {/* we use the proxy (s1) for mutations */}\n          onChange={e => {\n            s1.text = e.target.value\n          }}\n        />\n      </div>\n      <div>\n        { counter }\n        <button onClick={() => s1.counter++}> + </button>\n        <button onClick={() => s1.counter--}> - </button>\n      </di>\n    </div>\n  })\n\n}\n```\n\n## Unorganized Notes\n\n### two kinds of proxies\n\nvaltio has two kinds of proxies, for write and read. We intentionally separate them for hooks and concurrent react.\n\n`proxy()` creates a proxy object to detect mutation, \"proxy for write\"\n`snapshot()` creates an immutable object from the proxy object\n`useSnapshot()` wraps the snapshot object again with another proxy (with `proxy-compare`) to detect property access, \"proxy for read\"\n\n### snapshot creation is optimized\n\n```js\nconst state = proxy({ a: { aa: 1 }, b: { bb: 2 } })\nconst snap1 = snapshot(state)\nconsole.log(snap1) // ---> { a: { aa: 1 }, b: { bb: 2 } }\n++state.a.aa\nconst snap2 = snapshot(state)\nconsole.log(snap2) // ---> { a: { aa: 2 }, b: { bb: 2 } }\nsnap1.b === snap2.b // this is `true`, it doesn't create a new snapshot because no properties are changed.\n```\n\n### Some notes about valtio implementation in deep\n\nvaltio's proxy has only one goal: create an immutable snapshot object\n\nsome design principles:\n\n1. snapshot is created on demand\n2. changes are tracked only with version number\n3. subscription is used for notifying update (version)\n4. version number is hidden as implementation detail\n5. proxies are basically used only for version and subscription\n6. snapshot creation is optimized with version number\n\nsome notes about the implementation:\n\n1. proxy can be nested (created at the initialization)\n2. proxy can have circular structure (globalVersion to detect it)\n\nsome notes about promise handling:\n\n1. proxy can have a promise but does nothing\n2. when creating a snapshot, it will store the resolved value\n3. if it's not resolved, a special object will throw a promise/error\n"
  },
  {
    "path": "docs/how-tos/some-gotchas.mdx",
    "content": "---\ntitle: 'Some gotchas'\n---\n\n# Some gotchas\n\n## `useSnapshot(state)` without property access will always trigger re-render\n\nRef: https://github.com/pmndrs/valtio/issues/209#issuecomment-896859395\n\nSuppose we have this state (or store).\n\n```js\nconst state = proxy({\n  obj: {\n    count: 0,\n    text: 'hello',\n  },\n})\n```\n\nIf using the snapshot with accessing count,\n\n```js\nconst snap = useSnapshot(state)\nsnap.obj.count\n```\n\nit will re-render only if `count` changes.\n\nIf the property access is obj,\n\n```js\nconst snap = useSnapshot(state)\nsnap.obj\n```\n\nthen, it will re-render if `obj` changes. This includes `count` changes and `text` changes.\n\nNow, we can subscribe to the portion of the state.\n\n```js\nconst snapObj = useSnapshot(state.obj)\nsnapObj\n```\n\nThis is technically same as the previous one. It doesn't touch the property of `snapObj`, so it will re-render if `obj` changes.\n\nIn summary, if a snapshot object (nested or not) is not accessed with any properties, it assumes the entire object is accessed, so any change inside the object will trigger re-render.\n\n## Using `React.memo` with object props may result in unexpected behavior (v1 only)\n\n⚠️ This behavior is fixed in v2.\n\nThe `snap` variable returned by `useSnapshot(state)` is tracked for render optimization.\nIf you pass the `snap` or some objects in `snap` to a component with `React.memo`,\nit may not work as expected because `React.memo` can skip touching object properties.\n\nSide note: [react-tracked](https://react-tracked.js.org) has a special `memo` exported as a workaround.\n\nWe have some options:\n\n<ol type=\"a\">\n  <li>Do not use `React.memo`.</li>\n  <li>\n    Do not pass objects to components with `React.memo` (pass primitive values\n    instead).\n  </li>\n  <li>\n    Pass in the proxy of that element, and then `useSnapshot` on that proxy.\n  </li>\n</ol>\n\n### Example of (b)\n\n```jsx\nconst ChildComponent = React.memo(\n  ({\n    title, // string or any primitive values are fine.\n    description, // string or any primitive values are fine.\n    // obj, // objects should be avoided.\n  }) => (\n    <div>\n      {title} - {description}\n    </div>\n  ),\n)\n\nconst ParentComponent = () => {\n  const snap = useSnapshot(state)\n  return (\n    <div>\n      <ChildComponent\n        title={snap.obj.title}\n        description={snap.obj.description}\n      />\n    </div>\n  )\n}\n```\n\n### Example of (c)\n\n```jsx\nconst state = proxy({\n  objects: [\n    { id: 1, label: 'foo' },\n    { id: 2, label: 'bar' },\n  ],\n})\n\nconst ObjectList = React.memo(() => {\n  const stateSnap = useSnapshot(state)\n\n  return stateSnap.objects.map((object, index) => (\n    <Object key={object.id} objectProxy={state.objects[index]} />\n  ))\n})\n\nconst Object = React.memo(({ objectProxy }) => {\n  const objectSnap = useSnapshot(objectProxy)\n\n  return objectSnap.bar\n})\n```\n\n## When to use `state` and when to use `snap` in functional components\n\n- snap should be used in render function, every other cases state.\n- callback functions are not in the render body and therefore state must be used.\n\n```javascript\nconst Component = () => {\n  // this is in render body\n  const handleClick = () => {\n    // this is NOT in render body\n  }\n  return <button onClick={handleClick}>button</button>\n}\n```\n\n- deps in useEffect should be used extracting primitive values from snap. For example: `const { num, string, bool } = snap.watchObj`.\n- changing a state value based on other state values (without involving values like props in a component), should preferably done outside react.\n\n```javascript\nsubscribe(state.subscribeData, async () => {\n  state.results = await load(state.someData)\n})\n```\n\n## Controlled inputs may lose caret position without `sync: true`\n\nRef: https://github.com/pmndrs/valtio/issues/270\n\nWhen using Valtio state with controlled `<input>` elements, you might notice the text caret jumping to the end while typing in the middle of the existing text.\n\nThis happens because Valtio batches state updates causing React to re-render after the input event. React resets the DOM value and loses the caret position.\n\n**Use `{ sync: true }` to update synchronously and preserve the caret:**\n\n```jsx\nfunction Input() {\n  const snap = useSnapshot(state, { sync: true })\n\n  return (\n    <input\n      value={snap.text}\n      onChange={(e) => {\n        state.text = e.target.value\n      }}\n    />\n  )\n}\n```\n\n`sync: true` disables batching so React re-renders within the same event loop tick, skipping the DOM update and preserving the caret position.\n\n## Issue with `array` `proxy`\n\nThe following use case can occur unexpected results on `arr` subscription:\n\n```javascript\nconst byId = {}\narr.forEach((item) => {\n  byId[item.id] = item\n})\narr.splice(0, arr.length)\narr.push(newValue())\nsomeUpdateFunc(byId)\nObject.keys(byId).forEach((key) => arr.push(byId[key]))\n```\n\n[Issues](https://github.com/pmndrs/valtio/issues/712) may arise when handling the array proxy reference in the subsequent steps:\n\n<ol type=\"a\">\n  <li>Subscribe array proxy</li>\n  <li>Use the proxy as snapshot</li>\n  <li>Assign temp variable for updating</li>\n  <li>Remove proxy from the array</li>\n  <li>Update temp</li>\n  <li>Push temp in the original array</li>\n</ol>\n\n**Example issue case:**\n\n```javascript\nconst a = proxy([\n  {\n    nested: {\n      nested: {\n        test: 'apple',\n      },\n    },\n  },\n])\n\nconst sa = snapshot(a) // b.\n\n// a.\nsubscribe(a, () => {\n  const updated = snapshot(a)\n  console.log('this is updated proxy. test is Banana', a)\n  console.log('however, for the snapshot of a, test is still apple', updated)\n})\n\nfunction handle() {\n  const temp = a[0] // c.\n  a.splice(0, 1) // d.\n  temp.nested.nested.test = 'Banana' // e.\n  a.push(temp) // f.\n  console.log(Object.is(temp, a[0])) // this will be true\n}\n```\n\n**To work around this, swap d and e:**\n\n```javascript\n// ...\n\nfunction handle() {\n  const temp = a[0]\n  temp.nested.nested.test = 'Banana' // Update first remove from array\n  a.splice(0, 1)\n  a.push(temp)\n}\n// ...\n```\n\nIf the workaround is not applied and you are using react with [devtools()](https://valtio.pmnd.rs/docs/api/utils/devtools), the redux devtools will notify a value update, but the snapshot will remain the same within the devtools' subscription.\n\nAs a result, the devtools will not display any state change.\n\nAdditionally, this issue involved not only updating devtools, but also triggering `re-render`.\n\n## Issue with imports when using a library other than `react` (i.e. solidjs)\n\nValtio does not have to work within react, however it was built with react in mind. This being the case, the main `valtio` module exports the react modules alongside the vanilla modules for convenience. This means if you are attempting to import from the main `valtio` module or the `valtio/utils` submodule into a non-react project, you may end up with build errors like this:\n\n```\n node_modules/.pnpm/valtio@2.1.4/node_modules/valtio/esm/react.mjs (2:18): \"useRef\" is not exported by \"__vite-optional-peer-dep:react:valtio\", imported by \"node_modules/.pnpm/valtio@2.1.4/node_modules/valtio/esm/react.mjs\".\n```\n\nThis occurs because the main valtio module exports both framework-agnostic and React-specific functionality, causing build tools like Rollup to look for React dependencies even when they're not needed.\n\nThere is a simple fix for this, however. Instead of importing from the main `valtio` module like this:\n\n```ts\nimport { proxy, snapshot, subscribe } from 'valtio'\n```\n\nyou can import directly from the framework-agnostic `vanilla` submodule:\n\n```ts\nimport { proxy, snapshot, subsribe } from 'valtio/vanilla'\n// this also applies for the utils\nimport { proxyMap, deepClone } from 'valtio/vanilla/utils'\n```\n"
  },
  {
    "path": "docs/introduction/getting-started.mdx",
    "content": "---\ntitle: 'Getting Started'\ndescription: 'Get started with valtio.'\n---\n\n![Valtio](./logo.svg 'Valtio')\n\n# Valtio\n\n### Proxy state made simple.\n\nThe Valtio API is minimal, flexible, unopinionated and a touch magical. Valtio's proxy turns the object you pass it into a self-aware proxy, allowing fine-grained subscription and reactivity when making state updates. In React, Valtio shines at render optimization. It is compatible with Suspense and React 18. Valtio is also a viable option in vanilla javascript applications.\n\n#### Installation\n\n```bash\nnpm install valtio\n```\n\n#### The to-do app example\n\n#### 1. proxy\n\nLet's learn Valtio by coding a simple to-do app in React and Typescript. We'll start by creating some state using [`proxy`](../api/basic/proxy).\n\n```ts\nimport { proxy, useSnapshot } from 'valtio'\n\ntype Status = 'pending' | 'completed'\ntype Filter = Status | 'all'\ntype Todo = {\n  description: string\n  status: Status\n  id: number\n}\n\nexport const store = proxy<{ filter: Filter; todos: Todo[] }>({\n  filter: 'all',\n  todos: [],\n})\n```\n\n#### 2. useSnapshot\n\nTo access the data in this store, we'll use [`useSnapshot`](../api/basic/useSnapshot). The Todos component will rerender when the \"todos\" or \"filter\" properties are updated. Any other data we add to the proxy will be ignored.\n\n```tsx\nconst Todos = () => {\n  const snap = useSnapshot(store)\n  return (\n    <ul>\n      {snap.todos\n        .filter(({ status }) => status === snap.filter || snap.filter === 'all')\n        .map(({ description, status, id }) => {\n          return (\n            <li key={id}>\n              <span data-status={status} className=\"description\">\n                {description}\n              </span>\n              <button className=\"remove\">x</button>\n            </li>\n          )\n        })}\n    </ul>\n  )\n}\n```\n\n#### 3. actions\n\nFinally, we need to create, update, and delete our todos. To do so, we simply mutate properties on the store we created, not the snap. Commonly, these mutations are wrapped up in functions called actions.\n\n```ts\nconst addTodo = (description: string) => {\n  store.todos.push({\n    description,\n    status: 'pending',\n    id: Date.now(),\n  })\n}\n\nconst removeTodo = (id: number) => {\n  const index = store.todos.findIndex((todo) => todo.id === id)\n  if (index >= 0) {\n    store.todos.splice(index, 1)\n  }\n}\n\nconst toggleDone = (id: number, currentStatus: Status) => {\n  const nextStatus = currentStatus === 'pending' ? 'completed' : 'pending'\n  const todo = store.todos.find((todo) => todo.id === id)\n  if (todo) {\n    todo.status = nextStatus\n  }\n}\n\nconst setFilter = (filter: Filter) => {\n  store.filter = filter\n}\n```\n\nFinally, we wire up these actions to our inputs and buttons - check the demo below for the full code.\n\n```tsx\n<button className=\"remove\" onClick={() => removeTodo(id)}>\n  x\n</button>\n```\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/valtio-to-do-list-forked-6w9h3z\n\n#### Mutating state outside of components\n\nIn our first to-do app, Valtio enabled mutations without worrying about performance or \"breaking\" React. `useSnapshot` turned these mutations into immutable snapshots and optimized renders. But we could have easily used React's own state handling. Let's add a bit of complexity to our to-do app to see what else Valtio offers.\n\nWe will add a \"timeLeft\" property to a todo. Now each todo will tick down to zero and become \"overdue\" if not completed in time.\n\n```ts\ntype Todo = {\n  description: string\n  status: Status\n  id: number\n  timeLeft: number\n}\n```\n\nAmong other changes, we will add a Countdown component to display the ticking time for each todo. We are using an advanced technique of passing a nested proxy object to [`useSnapshot`](../api/basic/useSnapshot). Alternatively, make this a dumb component by passing the todo's \"timeLeft\" as a prop.\n\n```tsx\nimport { useSnapshot } from 'valtio'\nimport { formatTimeDelta, calcTimeDelta } from './utils'\nimport { store } from './App'\n\nexport const Countdown = ({ index }: { index: number }) => {\n  const snap = useSnapshot(store.todos[index])\n  const delta = calcTimeDelta(snap.timeLeft)\n  const { days, hours, minutes, seconds } = formatTimeDelta(delta)\n  return (\n    <span className=\"countdown-time\">\n      {delta.total < 0 ? '-' : ''}\n      {days}\n      {days ? ':' : ''}\n      {hours}:{minutes}:{seconds}\n    </span>\n  )\n}\n```\n\n#### Mutate in module scope\n\nInstead of managing multiple timers inside of a React component, let's move these updates outside of React altogether by defining a recursive `countdown` function in module scope which will mutate the todos.\n\n```tsx\nconst countdown = (index: number) => {\n  const todo = store.todos[index]\n  // user removed todo case\n  if (!todo) return\n  // todo done of overdue case\n  if (todo.status !== 'pending') {\n    return\n  }\n  // time over\n  if (todo.timeLeft < 1000) {\n    todo.timeLeft = 0\n    todo.status = 'overdue'\n    return\n  }\n  setTimeout(() => {\n    todo.timeLeft -= 1000\n    countdown(index)\n  }, 1000)\n}\n```\n\nWe can start the recursive countdown from an enhanced `addTodo` action.\n\n```tsx\nconst addTodo = (e: React.SyntheticEvent, reset: VoidFunction) => {\n  e.preventDefault()\n  const target = e.target as typeof e.target & {\n    deadline: { value: Date }\n    description: { value: string }\n  }\n  const deadline = target.deadline.value\n  const description = target.description.value\n  const now = Date.now()\n  store.todos.push({\n    description,\n    status: 'pending',\n    id: now,\n    timeLeft: new Date(deadline).getTime() - now,\n  })\n  // clear the form\n  reset()\n  countdown(store.todos.length - 1)\n}\n```\n\nPlease see the rest of the changes in the demo below.\n\n#### Subscribe in module scope\n\nBeing able to mutate state outside of components is a huge benefit. We can also [`subscribe`](../api/advanced/subscribe) to state changes in module scope. We will leave it to you to try this out. You might, for instance, persist your todos to local storage as in [this example](https://github.com/pmndrs/valtio/wiki/How-to-persist-states#persist-with-localstorage).\n\n## Codesandbox demo\n\nhttps://codesandbox.io/s/valtio-countdown-to-do-list-xkgmri\n"
  },
  {
    "path": "docs/introduction.mdx",
    "content": "# Introduction\n"
  },
  {
    "path": "docs/readme.md",
    "content": "## How to contribute\n\n### Basic things to know before adding docs\n\n- Docs live in `docs/` folder.\n- Website lives in `website/` folder.\n- Docs are written in `mdx` format.\n- Docs filename shouldn't have spaces.\n- Website would generate title and other metadata from graymatter in the file.\n- You should be able to render condesandbox inside `mdx` files by simply adding the url for the same\n- Once you have a doc, you can add it to the sidebar section by adding it to the nav in `getDocsNav` function inside `website/lib/mdx.ts`\n"
  },
  {
    "path": "docs/resources/community.mdx",
    "content": "---\ntitle: 'Community'\ndescription: 'Discussion, wikis, and other community resources'\nsection: 'Resources'\n---\n\n# Community\n\n## Recipes wiki\n\nValtio is unopinionated about best practices.\nThe community is working on recipes on wiki pages.\n\n- [How to organize actions](https://github.com/pmndrs/valtio/wiki/How-to-organize-actions)\n- [How to persist states](https://github.com/pmndrs/valtio/wiki/How-to-persist-states)\n- [How to use with context](https://github.com/pmndrs/valtio/wiki/How-to-use-with-context)\n- [How to split and compose states](https://github.com/pmndrs/valtio/wiki/How-to-split-and-compose-states)\n\n## Discussion Forums\n\n- [Pomandres collective](https://discord.gg/poimandres) - discord around React-three and related projects like Valtio\n\n- [React Fan](https://discord.gg/MrQdmzd) - discord maintained by Daishi Kato\n"
  },
  {
    "path": "docs/resources/learn.mdx",
    "content": "---\ntitle: 'Learn'\ndescription: 'Courses, tutorials, and more'\nsection: 'Resources'\n---\n\n# Learn\n\n- [learn valtio](https://daishi.gumroad.com/l/learn-valtio) - a code walkthrough by the library author\n\n- [7GUIs with Valtio](https://stackblitz.com/edit/vitejs-vite-mxacfhmz?file=src%2Fguis%2FCircleDrawer%2Findex.tsx) - learn Valtio by [building 7 GUIs](https://eugenkiss.github.io/7guis/)\n"
  },
  {
    "path": "docs/resources/libraries.mdx",
    "content": "---\ntitle: 'Libraries'\ndescription: 'Libraries related to Valtio'\nsection: 'Resources'\n---\n\n# Libraries\n\nValtio provides bare necessities for proxy state management which is great for most projects; however, some users wish to extend the library's feature set. This can be done using 3rd-party libraries created by the community.\n\n<br />\n\n<blockquote className=\"warning\">\n⚠️ &nbsp; Warning\n\nDisclaimer: These libraries may have bugs, limited maintenance, or other limitations and are not officially recommended by pmndrs or the valtio maintainers. This list is to provide a good starting point for someone looking to extend valtio's feature set.\n\n</blockquote>\n\n<br />\n\n- [electron-valtio](https://github.com/water-a/electron-valtio) - Share state between the Electron main process and various renderer windows via valtio\n- [eslint-plugin-valtio](https://github.com/pmndrs/eslint-plugin-valtio) - Eslint plugin for valtio\n- [storybook-valtio-auto-bind](https://github.com/CosPie/storybook-valtio-auto-bind) - Automatically sync your Storybook args bidirectionally with the Valtio store\n- [sveltio](https://github.com/wobsoriano/sveltio) - State management solution for Svelte using proxies. Powered by valtio.\n- [swc-plugin-valtio](https://github.com/sosukesuzuki/swc-plugin-valtio) - Valtio useProxy transformer for SWC.\n- [tauri-plugin-valtio](https://github.com/ferreira-tb/tauri-store/tree/main/packages/plugin-valtio) - Persistent valtio state for Tauri, accessible from both JavaScript and Rust.\n- [use-valtio](https://github.com/dai-shi/use-valtio) - Another custom hook to use Valtio proxy state\n- [valtio-element](https://github.com/lxsmnsyc/valtio-element) - Create reactive, declarative custom elements with valtio\n- [valtio-factory](https://github.com/mfellner/valtio-factory) - Create valtio state using the factory pattern\n- [valtio-fsm](https://github.com/valtiojs/valtio-fsm) - A a simple and chainable TypeScript-first finite state machine library powered by Valtio's reactivity system.\n- [valtio-persist](https://github.com/valtiojs/valtio-persist) - Flexible and performant saving of state to disk.\n  - [valtio-auto-persist](https://github.com/valtiojs/valtio-auto-persist) - Experimental fork of valtio-persist that allows storing your state objects without the need for attaching a key to identify it. It uses [structure-id](https://github.com/overthemike/structure-id) behind the scenes.\n- [valtio-plugin](https://github.com/valtiojs/valtio-plugin) - A brand new lifecycle plugin system that makes it much easier to customize how you use valtio.\n- [valtio-reactive](https://github.com/valtiojs/valtio-reactive) - Valtio-reactive makes valtio a reactive library.\n- [valtio-signal](https://github.com/dai-shi/valtio-signal) - Another React binding for Valtio proxy state\n- [valtio-yjs](https://github.com/dai-shi/valtio-yjs) - Valtio-yjs makes yjs state easy\n- [valtio-zod](https://github.com/valtiojs/valtio-zod) - Validate your valtio state updates with [Zod](https://zod.dev)\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import eslint from '@eslint/js'\nimport vitest from '@vitest/eslint-plugin'\nimport { defineConfig, globalIgnores } from 'eslint/config'\nimport importPlugin from 'eslint-plugin-import'\nimport jestDom from 'eslint-plugin-jest-dom'\nimport react from 'eslint-plugin-react'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport testingLibrary from 'eslint-plugin-testing-library'\nimport tseslint from 'typescript-eslint'\n\nexport default defineConfig(\n  globalIgnores(['dist/', 'examples/', 'website/', 'coverage/']),\n  eslint.configs.recommended,\n  importPlugin.flatConfigs.recommended,\n  tseslint.configs.recommended,\n  react.configs.flat.recommended,\n  react.configs.flat['jsx-runtime'],\n  reactHooks.configs.flat.recommended,\n  {\n    languageOptions: {\n      parserOptions: {\n        project: true,\n      },\n    },\n    settings: {\n      react: {\n        version: 'detect',\n      },\n      'import/resolver': {\n        typescript: true,\n      },\n    },\n    rules: {\n      eqeqeq: 'error',\n      curly: ['warn', 'multi-line', 'consistent'],\n      'sort-imports': [\n        'error',\n        {\n          ignoreDeclarationSort: true,\n        },\n      ],\n      'import/no-unresolved': ['error', { commonjs: true, amd: true }],\n      'import/named': 'off',\n      'import/namespace': 'off',\n      'import/no-named-as-default-member': 'off',\n      'import/no-duplicates': 'error',\n      'import/extensions': ['error', 'always', { ignorePackages: true }],\n      'import/order': [\n        'error',\n        {\n          alphabetize: { order: 'asc', caseInsensitive: true },\n          groups: [\n            'builtin',\n            'external',\n            'internal',\n            'parent',\n            'sibling',\n            'index',\n            'object',\n          ],\n          'newlines-between': 'never',\n          pathGroups: [\n            {\n              pattern: 'react',\n              group: 'builtin',\n              position: 'before',\n            },\n          ],\n          pathGroupsExcludedImportTypes: ['builtin'],\n        },\n      ],\n      '@typescript-eslint/no-explicit-any': 'off',\n      '@typescript-eslint/no-unused-vars': [\n        'error',\n        { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },\n      ],\n    },\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    ...testingLibrary.configs['flat/react'],\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    ...jestDom.configs['flat/recommended'],\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    ...vitest.configs.recommended,\n    settings: { vitest: { typecheck: true } },\n  },\n  {\n    files: ['tests/**/*.{ts,tsx}'],\n    rules: {\n      'import/extensions': ['error', 'never'],\n      'vitest/consistent-test-it': [\n        'error',\n        { fn: 'it', withinDescribe: 'it' },\n      ],\n    },\n  },\n  {\n    files: ['src/vanilla/utils/proxyMap.ts', 'src/vanilla/utils/proxySet.ts'],\n    rules: {\n      '@typescript-eslint/no-unused-expressions': 'off',\n    },\n  },\n  {\n    files: ['*.config.*'],\n    languageOptions: {\n      parserOptions: {\n        project: null,\n      },\n    },\n  },\n)\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\n## Simple examples\n\n- [Starter](https://github.com/pmndrs/valtio/tree/main/examples/starter)\n- [Counter](https://github.com/pmndrs/valtio/tree/main/examples/counter)\n- [Todo](https://github.com/pmndrs/valtio/tree/main/examples/todo)\n- [Todo with proxyMap](https://github.com/pmndrs/valtio/tree/main/examples/todo-with-proxyMap)\n"
  },
  {
    "path": "examples/counter/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/styles/github-dark.min.css\"\n    />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/counter/package.json",
    "content": "{\n  \"name\": \"counter\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@types/lowlight\": \"^0.0.7\",\n    \"highlight.js\": \"^11.11.0\",\n    \"lowlight\": \"^3.3.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"react-lowlight\": \"^3.0.1\",\n    \"valtio\": \"^2.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@vitejs/plugin-react\": \"^4.3.1\",\n    \"typescript\": \"^5.5.3\",\n    \"vite\": \"^5.4.1\"\n  }\n}\n"
  },
  {
    "path": "examples/counter/src/App.tsx",
    "content": "import { proxy, useSnapshot } from 'valtio'\nimport Lowlight from 'react-lowlight'\nimport typescript from 'highlight.js/lib/languages/typescript'\n\n// You wrap your state\nconst state = proxy<{\n  number: number\n  nested?: {\n    ticks: number\n  }\n}>({ number: 0 })\n\n// You can freely mutate it from anywhere you want ...\nstate.nested = { ticks: 0 }\nsetInterval(() => state.nested && state.nested.ticks++, 200)\n\nconst Figure = () => {\n  const snap = useSnapshot(state)\n  // This component *only* renders when state.number changes ...\n  return <div className=\"figure\">{snap.number}</div>\n}\n\nconst Ticks = () => {\n  const snap = useSnapshot(state)\n  // This component *only* renders when state.nested.ticks changes ...\n  return <div className=\"ticks\">{snap?.nested?.ticks} —</div>\n}\n\nconst Controls = () => {\n  // This component simply mutates the state model, just like that ...\n  return (\n    <div className=\"logo\">\n      <ButtonUp onClick={() => state.number++} />\n      <ButtonDown onClick={() => state.number--} />\n    </div>\n  )\n}\n\nconst ButtonUp = ({ onClick }: { onClick: () => void }) => (\n  <svg viewBox=\"0 0 430 452\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path\n      onClick={onClick}\n      d=\"M214.83 0.95459C82.4432 0.95459 0.0568237 91.2955 0.0568237 226.523C0.0568237 272.651 9.76549 313.624 27.8727 347.545L340.5 36.3569C306.7 13.5435 264.249 0.95459 214.83 0.95459Z\"\n      fill=\"#A5FFCE\"\n    />\n  </svg>\n)\n\nconst ButtonDown = ({ onClick }: { onClick: () => void }) => (\n  <svg viewBox=\"0 0 430 452\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path\n      onClick={onClick}\n      d=\"M214.83 451.523C347.216 451.523 429.602 360.898 429.602 226.523C429.602 187.816 422.852 152.786 410.112 122.5L106 426.214C136.689 442.604 173.299 451.523 214.83 451.523Z\"\n      fill=\"#FFBEC2\"\n    />\n  </svg>\n)\n\nconst code = `import { proxy, useSnapshot } from 'valtio'\n\n// You wrap your state\nconst state = proxy({ number: 0 })\n\n// You can freely mutate it from anywhere you want ...\nstate.nested = { ticks: 0 }\nsetInterval(() => state.nested.ticks++, 200)\n\nconst Figure = () => {\n  const snap = useSnapshot(state)\n  // This component *only* renders when state.number changes ...\n  return <div className=\"figure\">{snap.number}</div>\n}\n\nconst Ticks = () => {\n  const snap = useSnapshot(state)\n  // This component *only* renders when state.nested.ticks changes ...\n  return <div className=\"ticks\">{snap.nested.ticks} —</div>\n}\n\nconst Controls = () => {\n  // This component simply mutates the state model, just like that ...\n  return (\n    <div className=\"logo\">\n      <ButtonUp onClick={() => state.number++} />\n      <ButtonDown onClick={() => state.number--} />\n    </div>\n  )\n}\n\nconst ButtonUp = ({ onClick }) => (\n  <svg viewBox=\"0 0 430 452\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path\n      onClick={onClick}\n      d=\"M214.83 0.95459C82.4432 0.95459 0.0568237 91.2955 0.0568237 226.523C0.0568237 272.651 9.76549 313.624 27.8727 347.545L340.5 36.3569C306.7 13.5435 264.249 0.95459 214.83 0.95459Z\"\n      fill=\"#A5FFCE\"\n    />\n  </svg>\n)\n\nconst ButtonDown = ({ onClick }) => (\n  <svg viewBox=\"0 0 430 452\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path\n      onClick={onClick}\n      d=\"M214.83 451.523C347.216 451.523 429.602 360.898 429.602 226.523C429.602 187.816 422.852 152.786 410.112 122.5L106 426.214C136.689 442.604 173.299 451.523 214.83 451.523Z\"\n      fill=\"#FFBEC2\"\n    />\n  </svg>\n)\n`\n\nLowlight.registerLanguage('tsx', typescript)\n\nexport default function App() {\n  return (\n    <>\n      <div className=\"app\">\n        <Figure />\n        <Ticks />\n        <Controls />\n      </div>\n      <div className=\"code\">\n        <Lowlight language=\"ts\" value={code} />\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "examples/counter/src/index.css",
    "content": "@import url('https://rsms.me/inter/inter.css');\nhtml {\n  font-family: 'Inter', sans-serif;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml,\nbody {\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n}\n\n.app {\n  background: white;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  user-select: none;\n  position: relative;\n  width: 100%;\n  height: 100vh;\n}\n\nsvg {\n  pointer-events: none;\n}\n\npath {\n  pointer-events: auto;\n}\n\n.ticks {\n  position: absolute;\n  top: 50px;\n  left: 50px;\n  font-size: 12px;\n  font-weight: 200;\n  font-variant-numeric: tabular-nums;\n}\n\n.figure {\n  font-variant-numeric: tabular-nums;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform: translate3d(-50%, -50%, 0);\n  font-weight: 800;\n  font-size: 30em;\n  letter-spacing: -20px;\n  pointer-events: none;\n  color: black;\n  -webkit-text-fill-color: white; /* Will override color (regardless of order) */\n  -webkit-text-stroke-width: 1px;\n  -webkit-text-stroke-color: black;\n}\n\n.logo {\n  position: absolute;\n  right: 150px;\n  bottom: 150px;\n}\n\n.logo > svg {\n  position: absolute;\n  width: 100px;\n  height: 100px;\n  cursor: pointer;\n}\n\n.code {\n  padding: 10%;\n}\n\npre {\n  font-size: 0.8em;\n  margin-left: -2.5rem !important;\n  margin-right: -2.5rem !important;\n  width: calc(100% + 5rem);\n  padding: 3em !important;\n  border: 1px solid #eee !important;\n  border-radius: 4px;\n}\n\n.src a * {\n  opacity: 0.5;\n  display: inline-block;\n  margin: 10px 5px;\n}\n"
  },
  {
    "path": "examples/counter/src/main.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport App from './App.tsx'\nimport './prism.css'\nimport './index.css'\n\ncreateRoot(document.getElementById('root')!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/counter/src/prism.css",
    "content": "/**\n * VS theme by Andrew Lock (https://andrewlock.net)\n * Inspired by Visual Studio syntax coloring\n */\n\ncode[class*='language-'],\npre[class*='language-'] {\n  color: #393a34;\n  font-family:\n    'Consolas', 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;\n  direction: ltr;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  font-size: 0.9em;\n  line-height: 1.2em;\n\n  -moz-tab-size: 4;\n  -o-tab-size: 4;\n  tab-size: 4;\n\n  -webkit-hyphens: none;\n  -moz-hyphens: none;\n  -ms-hyphens: none;\n  hyphens: none;\n}\n\npre > code[class*='language-'] {\n  font-size: 1em;\n}\n\npre[class*='language-']::-moz-selection,\npre[class*='language-'] ::-moz-selection,\ncode[class*='language-']::-moz-selection,\ncode[class*='language-'] ::-moz-selection {\n  background: #c1def1;\n}\n\npre[class*='language-']::selection,\npre[class*='language-'] ::selection,\ncode[class*='language-']::selection,\ncode[class*='language-'] ::selection {\n  background: #c1def1;\n}\n\n/* Code blocks */\npre[class*='language-'] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n  border: 1px solid #dddddd;\n  background-color: white;\n}\n\n/* Inline code */\n:not(pre) > code[class*='language-'] {\n  padding: 0.2em;\n  padding-top: 1px;\n  padding-bottom: 1px;\n  background: #f8f8f8;\n  border: 1px solid #dddddd;\n}\n\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #b2b8c0;\n  font-style: italic;\n}\n\n.token.namespace {\n  opacity: 0.7;\n}\n\n.token.string {\n  color: #67c2c7;\n}\n\n.token.punctuation,\n.token.operator {\n  color: #393a34 !important; /* no highlight */\n}\n\n.token.url,\n.token.symbol,\n.token.number,\n.token.boolean,\n.token.variable,\n.token.constant,\n.token.inserted {\n  color: #36acaa;\n}\n\n.token.atrule,\n.token.keyword,\n.token.attr-value,\n.language-autohotkey .token.selector,\n.language-json .token.boolean,\n.language-json .token.number,\ncode[class*='language-css'] {\n  color: #9494ab;\n}\n\n.token.function {\n  color: #ff7bab;\n}\n\n.token.deleted,\n.language-autohotkey .token.tag {\n  color: #9a050f;\n}\n\n.token.selector,\n.language-autohotkey .token.keyword {\n  color: #00009f;\n}\n\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n\n.token.italic {\n  font-style: italic;\n}\n\n.token.class-name,\n.language-json .token.property {\n  color: #67c2c7;\n}\n\n.token.tag,\n.token.selector {\n  color: #67c2c7;\n}\n\n.token.attr-name,\n.token.property,\n.token.regex,\n.token.entity {\n  color: #91adbd;\n}\n\n.token.directive.tag .tag {\n  background: #ffff00;\n  color: #393a34;\n}\n\n/* overrides color-values for the Line Numbers plugin\n   * http://prismjs.com/plugins/line-numbers/\n   */\n.line-numbers .line-numbers-rows {\n  border-right-color: #a5a5a5;\n}\n\n.line-numbers-rows > span:before {\n  color: #2b91af;\n}\n\n/* overrides color-values for the Line Highlight plugin\n  * http://prismjs.com/plugins/line-highlight/\n  */\n.line-highlight {\n  background: rgba(193, 222, 241, 0.2);\n  background: -webkit-linear-gradient(\n    left,\n    rgba(193, 222, 241, 0.2) 70%,\n    rgba(221, 222, 241, 0)\n  );\n  background: linear-gradient(\n    to right,\n    rgba(193, 222, 241, 0.2) 70%,\n    rgba(221, 222, 241, 0)\n  );\n}\n"
  },
  {
    "path": "examples/counter/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/counter/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/counter/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "examples/counter/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/counter/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/package.json",
    "content": "{\n  \"name\": \"editor-proxyWithHistory\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"prismjs\": \"^1.23.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"valtio\": \"^2.1.1\",\n    \"valtio-history\": \"^1.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@vitejs/plugin-react\": \"^4.3.1\",\n    \"typescript\": \"^5.5.3\",\n    \"vite\": \"^5.4.1\"\n  }\n}\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/src/App.tsx",
    "content": "import React from 'react'\nimport { useSnapshot } from 'valtio'\nimport { proxyWithHistory } from 'valtio-history'\n\nconst textProxy = proxyWithHistory({\n  text: 'Add some text to this initial value and then undo/redo',\n})\n\nconst update = (event: React.ChangeEvent<HTMLTextAreaElement>) =>\n  (textProxy.value.text = event.target.value)\n\nexport default function App() {\n  const { value, undo, redo, history, canUndo, canRedo, getCurrentChangeDate } =\n    useSnapshot(textProxy)\n\n  return (\n    <div className=\"App\">\n      <h2>Editor with history</h2>\n      <div className=\"info\">\n        <span>\n          change {history.index + 1} / {history.nodes.length}\n        </span>\n        <span>|</span>\n        <span>{getCurrentChangeDate().toISOString()}</span>\n      </div>\n      <div className=\"editor\">\n        <textarea value={value.text} rows={4} onChange={update} />\n      </div>\n      <button onClick={undo} disabled={!canUndo()}>\n        Undo\n      </button>\n      <button onClick={redo} disabled={!canRedo()}>\n        Redo\n      </button>\n    </div>\n  )\n}\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/src/index.css",
    "content": ".App {\n  font-family: 'Helvetica Neue', sans-serif;\n  text-align: center;\n}\n\n.info {\n  display: flex;\n  justify-content: center;\n  padding: 16px;\n\n  & > span {\n    margin-right: 32px;\n  }\n}\n\n.editor {\n  display: flex;\n  margin: 0 auto;\n  width: 80%;\n  flex-direction: column;\n  margin-bottom: 8px;\n}\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/src/main.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport App from './App.tsx'\nimport './index.css'\n\ncreateRoot(document.getElementById('root')!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/editor-proxyWithHistory/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/photo-booth-vanillajs/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Photo Booth</title>\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/water.css@2/out/water.css\"\n    />\n  </head>\n\n  <body>\n    <header><a href=\"https://github.com/pmndrs/valtio\">Valtio 🧙</a></header>\n    <main>\n      <h1>Photo Booth</h1>\n      <section id=\"video-container\">\n        <video playsinline autoplay></video>\n      </section>\n      <controls>\n        <button id=\"take-pic-btn\">Take picture</button>\n      </controls>\n      <canvas hidden></canvas>\n      <controls id=\"candidate-img-controls\"> </controls>\n      <div id=\"images\"></div>\n    </main>\n    <script type=\"module\" src=\"/src/main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/photo-booth-vanillajs/package.json",
    "content": "{\n  \"name\": \"valtio-photo-booth-demo\",\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"start\": \"yarn run dev\"\n  },\n  \"dependencies\": {\n    \"lighterhtml\": \"4.2.0\",\n    \"localforage\": \"1.10.0\",\n    \"valtio\": \"^2.1.1\",\n    \"vite\": \"2.8.1\"\n  },\n  \"keywords\": [],\n  \"description\": \"\"\n}\n"
  },
  {
    "path": "examples/photo-booth-vanillajs/src/index.css",
    "content": "main {\n  -moz-osx-font-smoothing: grayscale;\n  text-align: center;\n  color: #2c3e50;\n  margin-top: 60px;\n}\n\nvideo {\n  width: 100%;\n  max-height: calc(50vh - 16px);\n  background: var(--background-alt);\n  cursor: pointer;\n}\n\ncontrols {\n  width: 100%;\n  display: flex;\n  justify-content: center;\n  padding: 32px 0;\n}\n\n.image-remove-btn {\n  position: absolute;\n  top: 0;\n  right: 0;\n  padding: 8px;\n  margin-top: 4px;\n  cursor: pointer;\n}\n\n.image-remove-btn.selected {\n  display: none;\n}\n\ncanvas {\n  margin: 32px;\n}\n\nbutton {\n  margin-top: 18px;\n}\n\n#images {\n  display: flex;\n  flex-flow: wrap;\n  gap: 8px;\n}\n\na:not(.selected) {\n  position: relative;\n  flex: 0 1 auto;\n  width: calc(33% - 4px);\n  padding-top: 2;\n  padding-right: 2;\n  padding-bottom: 0;\n  padding-left: 2;\n}\n\na.selected {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  width: 100%;\n  background: var(--background);\n  z-index: 10;\n}\n\nimg {\n  border: 1px solid transparent;\n  transition: all 250ms cubic-bezier(0, 0.72, 0, 1.02);\n}\n\nimg.selected {\n  display: block;\n  width: 90%;\n  border-radius: 8;\n  margin: 64px auto;\n}\n\nimg:not(.selected) {\n  width: 100%;\n  border-radius: 4;\n}\n\nimg:hover {\n  border: 1px solid var(--highlight);\n  transform-origin: center center;\n  z-index: 2;\n  cursor: pointer;\n}\n\n.animate-in img:hover {\n  border: 1px solid transparent;\n  transform: scale(1);\n}\n\n.animate-in {\n  animation: up-in;\n  animation-duration: 250ms;\n  animation-fill-mode: forwards;\n  animation-timing-function: cubic-bezier(0, 0.72, 0, 1.02);\n  will-change: transform;\n  transform: translate3d(0, -200px, 0);\n  opacity: 0.5;\n}\n\n.animate-in:after {\n  position: absolute;\n  top: 32px;\n  right: calc(5%);\n  content: 'Close';\n  color: var(--select-arrow);\n  font-size: 1rem;\n}\n\n.hidden {\n  display: none;\n}\n\n@keyframes up-in {\n  from {\n    transform: translate3d(0, -200px, 0);\n    opacity: 0.5;\n  }\n\n  to {\n    transform: translate3d(0, 0, 0);\n    opacity: 1;\n  }\n}\n"
  },
  {
    "path": "examples/photo-booth-vanillajs/src/main.js",
    "content": "import './index.css'\nimport { html, render } from 'lighterhtml'\nimport localforage from 'localforage'\nimport { proxy, snapshot, subscribe } from 'valtio/vanilla'\n\nlocalforage.config({ name: 'valtio_photo_booth' })\n\nconst getId = () => new Date().getTime()\n\nwindow.history.pushState({}, '', '/photo-booth')\n\n// reference https://github.com/webrtc/samples/blob/gh-pages/src/content/getusermedia/canvas/js/main.js\nconst canvas = document.querySelector('canvas')\ncanvas.width = 480\ncanvas.height = 360\nconst ctx = canvas.getContext('2d')\nconst video = document.querySelector('video')\nconst imageContainer = document.getElementById('images')\nconst snapPicButton = document.getElementById('take-pic-btn')\nconst acceptPicButton = document.getElementById('accept-pic-btn')\nconst rejectPicButton = document.getElementById('reject-pic-btn')\nconst candidateImgControlsContainer = document.getElementById(\n  'candidate-img-controls',\n)\n\nconst store = proxy({\n  camera: {\n    images: [],\n    candidateImage: null,\n  },\n  ui: {\n    selectedImageId:\n      decodeURI(window.location.pathname).split('/photo-booth/')[1] || null,\n  },\n})\n\nsubscribe(store, () => {\n  // keep storage synced\n  localforage.setItem('images', snapshot(store).camera.images || [])\n  // hide canvas when not used\n  if (store.camera.candidateImage) {\n    canvas.removeAttribute('hidden')\n  } else {\n    canvas.setAttribute('hidden', 'hidden')\n  }\n  // render html\n  renderCanvasControls(!!store.camera.candidateImage)\n  renderImages(store.camera.images, store.ui.selectedImageId)\n})\n\n/******** PERSISTED IMAGES HANDLING *********/\n\nasync function loadImages() {\n  const images = await localforage.getItem('images')\n  if (images) store.camera.images = images\n}\n\nfunction removeImage(id) {\n  store.camera.images = store.camera.images.filter((img) => img.id !== id)\n}\n\nfunction selectImage(id) {\n  const selectedId = store.ui.selectedImageId\n  if (selectedId === id) {\n    store.ui.selectedImageId = null\n    window.history.pushState({}, '', '/photo-booth/')\n  } else {\n    store.ui.selectedImageId = id\n    window.history.pushState({}, '', '/photo-booth/' + id)\n  }\n}\n\nfunction saveImage(url) {\n  const id = getId()\n  store.camera.images.push({ id, url })\n  window.history.pushState({}, '', id)\n}\n\nfunction renderImages(images, selectedId) {\n  render(\n    imageContainer,\n    html.node`\n    ${images.map(({ url, id }) => {\n      const selected = id === selectedId\n      return html.node`\n      <a href=\"${url}\" class=${\n        selected ? 'selected animate-in' : ''\n      } onclick=${(e) => {\n        e.preventDefault()\n        selectImage(id)\n      }}>\n        <img src=${url} class=${selected ? 'selected animate-in' : ''}>\n        <button class=${\n          selected ? 'image-remove-btn selected' : 'image-remove-btn'\n        } onclick=${() => removeImage(id)}>X</button>\n      </a>\n    `\n    })}\n  `,\n  )\n}\n\n/******** CANDIDATE IMAGE HANDLING *********/\n\nfunction keepCandidateImage(e) {\n  e.preventDefault()\n  saveImage(store.camera.candidateImage)\n  store.camera.candidateImage = null\n}\n\nfunction removeCandidateImage(e) {\n  e.preventDefault()\n  store.camera.candidateImage = null\n}\n\nfunction renderCanvasControls(hasCandidateImage) {\n  const className = !hasCandidateImage ? 'hidden' : ''\n  render(\n    candidateImgControlsContainer,\n    html.node`\n      <button id=\"accept-pic-btn\" class=${className} onclick=${keepCandidateImage}>\n        Keep picture</button>\n      <button id=\"reject-pic-btn\" class=${className} onclick=${removeCandidateImage}>Reject picture</button>\n    `,\n  )\n}\n\n/******** MEDIA STREAM INIT *********/\n\nasync function startMediaStream() {\n  try {\n    const stream = await navigator.mediaDevices.getUserMedia({\n      video: true,\n      audio: false,\n    })\n    video.srcObject = stream\n  } catch (err) {\n    // should show user an error message\n    console.log('failed to create stream', err)\n  }\n}\n\n/******** SETUP *********/\n\nwindow.addEventListener('load', async () => {\n  await loadImages()\n  await startMediaStream()\n\n  snapPicButton.addEventListener('click', () => {\n    const width = video.videoWidth\n    const height = video.videoHeight\n    ctx.fillRect(0, 0, width, height)\n    ctx.drawImage(video, 0, 0, width, height)\n    store.camera.candidateImage = canvas.toDataURL('image/webp')\n  })\n\n  acceptPicButton.addEventListener('click', () => {\n    saveImage(store.camera.candidateImage)\n    store.camera.candidateImage = null\n  })\n\n  rejectPicButton.addEventListener('click', () => {\n    store.camera.candidateImage = null\n  })\n})\n"
  },
  {
    "path": "examples/starter/README.md",
    "content": "# Starter [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/valtio/tree/main/examples/starter)\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/valtio\n\n# install project dependencies & build the library\ncd valtio && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/starter && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/valtio/tree/main/examples/starter\n"
  },
  {
    "path": "examples/starter/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta name=\"description\" content=\"valtio examples\" />\n    <title>Valtio Examples | Starter</title>\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/starter/package.json",
    "content": "{\n  \"name\": \"starter\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"valtio\": \"^2.1.2\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react-swc\": \"^3.5.0\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^5.3.4\"\n  }\n}\n"
  },
  {
    "path": "examples/starter/src/index.css",
    "content": "html,\nbody,\n#root {\n  height: 100%;\n}\n\n#root {\n  display: flex;\n  place-items: center;\n  justify-content: center;\n\n  color: #fff;\n  background-color: rgb(23 23 23);\n}\n"
  },
  {
    "path": "examples/starter/src/index.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { proxy, useSnapshot } from 'valtio'\n\nimport banner from './assets/banner.svg'\n\nimport './index.css'\n\nconst state = proxy({ count: 0 })\n\nconst Counter = () => {\n  const snap = useSnapshot(state)\n\n  return (\n    <>\n      <span className=\"text-3xl\">{snap.count}</span>\n      <button\n        className=\"bg-sky-400 font-bold py-2 px-4 rounded\"\n        onClick={() => ++state.count}\n      >\n        +1\n      </button>\n    </>\n  )\n}\n\nfunction App() {\n  return (\n    <div className=\"grid place-items-center gap-6\">\n      <a href=\"https://valtio.dev/\" target=\"_blank\" rel=\"noreferrer\">\n        <img\n          src={banner}\n          alt=\"Valtio banner\"\n          className=\"w-96\"\n          style={{\n            filter: 'drop-shadow(0 0 2em #b2ebf2)',\n          }}\n        />\n      </a>\n\n      <h1 className=\"text-5xl font-bold\">Valtio Starter</h1>\n\n      <Counter />\n    </div>\n  )\n}\n\ncreateRoot(document.getElementById('root')!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/starter/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/starter/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2019\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"moduleResolution\": \"node\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"jsx\": \"react-jsx\",\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"module\": \"esnext\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"vite.config.ts\", \"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/starter/vite.config.ts",
    "content": "import react from '@vitejs/plugin-react-swc'\nimport { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/subscribe/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <title>Valtio vanilla</title>\n    <meta name=\"description\" content=\"\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/water.css@2/out/water.css\"\n    />\n  </head>\n  <body>\n    <script type=\"module\" async defer>\n      import { render, html } from '//unpkg.com/lighterhtml?module'\n      import pk from '//unpkg.com/valtio@1.3.0?module'\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/todo/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/todo/package.json",
    "content": "{\n  \"name\": \"counter\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"prismjs\": \"^1.23.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"valtio\": \"^2.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@vitejs/plugin-react\": \"^4.3.1\",\n    \"typescript\": \"^5.5.3\",\n    \"vite\": \"^5.4.1\"\n  }\n}\n"
  },
  {
    "path": "examples/todo/src/App.tsx",
    "content": "import * as React from 'react'\nimport { actions, filters, Todo, useFilter, useTodos } from './store'\n\nfunction App() {\n  return (\n    <div className=\"App\">\n      <h1>Todo</h1>\n      <AddTodoInput />\n      <TodoList />\n      <FilterRow />\n    </div>\n  )\n}\n\nfunction AddTodoInput() {\n  const [value, setValue] = React.useState('')\n\n  function handleSubmit() {\n    actions.addTodo({ name: value })\n    setValue('')\n  }\n\n  return (\n    <div>\n      <input\n        type=\"text\"\n        value={value}\n        onChange={({ target }) => setValue(target.value)}\n      />\n      <button onClick={handleSubmit}>Add Todo</button>\n    </div>\n  )\n}\n\nfunction TodoList() {\n  const todos = useTodos()\n  return (\n    <div>\n      {todos.map((todo) => (\n        <TodoRow key={todo.id} todo={todo} />\n      ))}\n    </div>\n  )\n}\n\nfunction TodoRow({ todo }: { todo: Todo }) {\n  function handleCheckBoxChange(event: React.ChangeEvent<HTMLInputElement>) {\n    actions.toggleTodo(todo.id, event.target.checked)\n  }\n\n  function handleDeleteClick() {\n    actions.removeTodo(todo.id)\n  }\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        alignItems: 'center',\n        textDecoration: todo.completed ? 'line-through' : 'initial',\n      }}\n    >\n      <input type=\"checkbox\" onChange={handleCheckBoxChange} />\n      <h2>{todo.name}</h2>\n      <h2 style={{ color: 'red', paddingLeft: 10 }} onClick={handleDeleteClick}>\n        X\n      </h2>\n    </div>\n  )\n}\n\nfunction FilterRow() {\n  const activeFilter = useFilter()\n\n  return (\n    <div style={{ padding: 20 }}>\n      {filters.map((filter) => (\n        <button\n          style={{\n            fontWeight: activeFilter === filter ? 'bold' : 'normal',\n          }}\n          onClick={() => {\n            actions.toggleFilter(filter)\n          }}\n        >\n          {filter}\n        </button>\n      ))}\n    </div>\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "examples/todo/src/index.css",
    "content": "@import url('https://rsms.me/inter/inter.css');\nhtml {\n  font-family: 'Inter', sans-serif;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nhtml,\nbody {\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n}\n\n.app {\n  background: white;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  user-select: none;\n  position: relative;\n  width: 100%;\n  height: 100vh;\n}\n\nsvg {\n  pointer-events: none;\n}\n\npath {\n  pointer-events: auto;\n}\n\n.ticks {\n  position: absolute;\n  top: 50px;\n  left: 50px;\n  font-size: 12px;\n  font-weight: 200;\n  font-variant-numeric: tabular-nums;\n}\n\n.figure {\n  font-variant-numeric: tabular-nums;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform: translate3d(-50%, -50%, 0);\n  font-weight: 800;\n  font-size: 30em;\n  letter-spacing: -20px;\n  pointer-events: none;\n  color: black;\n  -webkit-text-fill-color: white; /* Will override color (regardless of order) */\n  -webkit-text-stroke-width: 1px;\n  -webkit-text-stroke-color: black;\n}\n\n.logo {\n  position: absolute;\n  right: 150px;\n  bottom: 150px;\n}\n\n.logo > svg {\n  position: absolute;\n  width: 100px;\n  height: 100px;\n  cursor: pointer;\n}\n\n.code {\n  padding: 10%;\n}\n\npre {\n  font-size: 0.8em;\n  margin-left: -2.5rem !important;\n  margin-right: -2.5rem !important;\n  width: calc(100% + 5rem);\n  padding: 3em !important;\n  border: 1px solid #eee !important;\n  border-radius: 4px;\n}\n\n.src a * {\n  opacity: 0.5;\n  display: inline-block;\n  margin: 10px 5px;\n}\n"
  },
  {
    "path": "examples/todo/src/main.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport App from './App.tsx'\nimport './prism.css'\nimport './index.css'\n\ncreateRoot(document.getElementById('root')!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/todo/src/prism.css",
    "content": "/**\n * VS theme by Andrew Lock (https://andrewlock.net)\n * Inspired by Visual Studio syntax coloring\n */\n\ncode[class*='language-'],\npre[class*='language-'] {\n  color: #393a34;\n  font-family:\n    'Consolas', 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;\n  direction: ltr;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  font-size: 0.9em;\n  line-height: 1.2em;\n\n  -moz-tab-size: 4;\n  -o-tab-size: 4;\n  tab-size: 4;\n\n  -webkit-hyphens: none;\n  -moz-hyphens: none;\n  -ms-hyphens: none;\n  hyphens: none;\n}\n\npre > code[class*='language-'] {\n  font-size: 1em;\n}\n\npre[class*='language-']::-moz-selection,\npre[class*='language-'] ::-moz-selection,\ncode[class*='language-']::-moz-selection,\ncode[class*='language-'] ::-moz-selection {\n  background: #c1def1;\n}\n\npre[class*='language-']::selection,\npre[class*='language-'] ::selection,\ncode[class*='language-']::selection,\ncode[class*='language-'] ::selection {\n  background: #c1def1;\n}\n\n/* Code blocks */\npre[class*='language-'] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n  border: 1px solid #dddddd;\n  background-color: white;\n}\n\n/* Inline code */\n:not(pre) > code[class*='language-'] {\n  padding: 0.2em;\n  padding-top: 1px;\n  padding-bottom: 1px;\n  background: #f8f8f8;\n  border: 1px solid #dddddd;\n}\n\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #b2b8c0;\n  font-style: italic;\n}\n\n.token.namespace {\n  opacity: 0.7;\n}\n\n.token.string {\n  color: #67c2c7;\n}\n\n.token.punctuation,\n.token.operator {\n  color: #393a34 !important; /* no highlight */\n}\n\n.token.url,\n.token.symbol,\n.token.number,\n.token.boolean,\n.token.variable,\n.token.constant,\n.token.inserted {\n  color: #36acaa;\n}\n\n.token.atrule,\n.token.keyword,\n.token.attr-value,\n.language-autohotkey .token.selector,\n.language-json .token.boolean,\n.language-json .token.number,\ncode[class*='language-css'] {\n  color: #9494ab;\n}\n\n.token.function {\n  color: #ff7bab;\n}\n\n.token.deleted,\n.language-autohotkey .token.tag {\n  color: #9a050f;\n}\n\n.token.selector,\n.language-autohotkey .token.keyword {\n  color: #00009f;\n}\n\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n\n.token.italic {\n  font-style: italic;\n}\n\n.token.class-name,\n.language-json .token.property {\n  color: #67c2c7;\n}\n\n.token.tag,\n.token.selector {\n  color: #67c2c7;\n}\n\n.token.attr-name,\n.token.property,\n.token.regex,\n.token.entity {\n  color: #91adbd;\n}\n\n.token.directive.tag .tag {\n  background: #ffff00;\n  color: #393a34;\n}\n\n/* overrides color-values for the Line Numbers plugin\n   * http://prismjs.com/plugins/line-numbers/\n   */\n.line-numbers .line-numbers-rows {\n  border-right-color: #a5a5a5;\n}\n\n.line-numbers-rows > span:before {\n  color: #2b91af;\n}\n\n/* overrides color-values for the Line Highlight plugin\n  * http://prismjs.com/plugins/line-highlight/\n  */\n.line-highlight {\n  background: rgba(193, 222, 241, 0.2);\n  background: -webkit-linear-gradient(\n    left,\n    rgba(193, 222, 241, 0.2) 70%,\n    rgba(221, 222, 241, 0)\n  );\n  background: linear-gradient(\n    to right,\n    rgba(193, 222, 241, 0.2) 70%,\n    rgba(221, 222, 241, 0)\n  );\n}\n"
  },
  {
    "path": "examples/todo/src/store.ts",
    "content": "import { proxy, useSnapshot } from 'valtio'\n\nexport interface Todo {\n  id: number\n  name: string\n  completed?: boolean\n}\n\nexport const filters: Filter[] = ['all', 'completed']\ntype Filter = 'all' | 'completed'\n\ninterface Store {\n  todos: Todo[]\n  filter: Filter\n}\n\nexport const store = proxy<Store>({\n  todos: [],\n  filter: 'all',\n})\n\nlet id = 0\nexport const actions = {\n  addTodo(todo: Omit<Todo, 'id'>) {\n    store.todos.push({\n      ...todo,\n      id: id++,\n    })\n  },\n  toggleTodo(id: number, value: boolean) {\n    const todo = store.todos.find((todo) => todo.id === id)\n    if (todo && value) todo.completed = value\n    else if (todo) todo.completed = !todo.completed\n  },\n  removeTodo(id: number) {\n    store.todos = store.todos.filter((todo) => todo.id !== id)\n  },\n  toggleFilter(filter: Filter) {\n    store.filter = filter\n  },\n}\n\nexport function useTodos() {\n  const snapShot = useSnapshot(store)\n\n  switch (snapShot.filter) {\n    case 'all':\n      return snapShot.todos\n    case 'completed':\n      return snapShot.todos.filter((todo) => todo.completed)\n    default:\n      throw Error('Error: un supported filter')\n  }\n}\n\nexport function useFilter() {\n  return useSnapshot(store).filter\n}\n"
  },
  {
    "path": "examples/todo/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/todo/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/todo/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "examples/todo/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/todo/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/todo-with-proxyMap/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/todo-with-proxyMap/package.json",
    "content": "{\n  \"name\": \"counter\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"lint\": \"eslint .\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"clsx\": \"^2.1.1\",\n    \"prismjs\": \"^1.23.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"valtio\": \"^2.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@vitejs/plugin-react\": \"^4.3.1\",\n    \"typescript\": \"^5.5.3\",\n    \"vite\": \"^5.4.1\"\n  }\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/AddTodoInput.tsx",
    "content": "import React from 'react'\n\nimport { actions } from './store'\n\nexport function AddTodoInput() {\n  const [value, setValue] = React.useState('')\n\n  function handleSubmit(e: React.FormEvent<HTMLFormElement>) {\n    e.preventDefault()\n    if (value.trim().length > 0) {\n      actions.addTodo({ name: value })\n      setValue('')\n    }\n  }\n\n  return (\n    <header className=\"header\">\n      <h1>Todo</h1>\n      <form onSubmit={handleSubmit}>\n        <input\n          className=\"new-todo\"\n          type=\"text\"\n          value={value}\n          placeholder=\"What needs to be done?\"\n          onChange={({ target }) => setValue(target.value)}\n        />\n      </form>\n    </header>\n  )\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/App.tsx",
    "content": "import { AddTodoInput } from './AddTodoInput'\nimport { TodoList } from './TodoList'\nimport { Filter } from './Filter'\n\nimport './styles.css'\n\nfunction App() {\n  return (\n    <div className=\"todoapp\">\n      <AddTodoInput />\n      <TodoList />\n      <Filter />\n    </div>\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/Filter.tsx",
    "content": "import cx from 'clsx'\nimport { useFilter, actions, useTodosCount } from './store'\n\nexport function Filter() {\n  const count = useTodosCount()\n  const activeFilter = useFilter()\n\n  if (!count.active && !count.completed) return null\n\n  return (\n    <footer className=\"footer\">\n      <span className=\"todo-count\">\n        <strong>{count.active}</strong> {`item${count.active > 1 ? 's' : ''}`}{' '}\n        left\n      </span>\n      <ul className=\"filters\">\n        <li>\n          <button\n            onClick={() => actions.toggleFilter('all')}\n            className={cx({ selected: activeFilter === 'all' })}\n          >\n            All\n          </button>\n        </li>{' '}\n        <li>\n          <button\n            onClick={() => actions.toggleFilter('todo')}\n            className={cx({\n              selected: activeFilter === 'todo',\n            })}\n          >\n            Active\n          </button>\n        </li>{' '}\n        <li>\n          <button\n            onClick={() => actions.toggleFilter('done')}\n            className={cx({\n              selected: activeFilter === 'done',\n            })}\n          >\n            Completed\n          </button>\n        </li>\n      </ul>\n    </footer>\n  )\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/TodoItem.tsx",
    "content": "import { useEffect, useRef, useState } from 'react'\nimport cx from 'clsx'\n\nimport { actions, Todo } from './store'\n\nexport function TodoItem({ todo }: { todo: Todo }) {\n  const [isEditing, setIsEditing] = useState(false)\n  const [editText, setEditText] = useState(todo.name)\n  const inputRef = useRef<HTMLInputElement>(null)\n\n  useEffect(() => {\n    if (isEditing && inputRef.current) {\n      const node = inputRef.current\n      node.focus()\n      node.setSelectionRange(node.value.length, node.value.length)\n    } else if (inputRef.current) {\n      const node = inputRef.current\n      node.blur()\n    }\n  }, [isEditing])\n\n  const handleCheckBoxChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    actions.toggleTodo(todo.id, event.target.checked)\n  }\n\n  const handleDelete = () => {\n    actions.removeTodo(todo.id)\n  }\n\n  const handleDoubleClick = () => {\n    setIsEditing(true)\n  }\n\n  const handleEdit = (e: React.ChangeEvent<HTMLInputElement>) => {\n    setEditText(e.target.value)\n  }\n\n  const handleKeydown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    if (e.key === 'Escape') {\n      setIsEditing(false)\n      setEditText(todo.name)\n    } else if (e.key === 'Enter' && editText.trim().length > 0) {\n      setIsEditing(false)\n      actions.updateTodo(todo.id, editText)\n    }\n  }\n\n  const handleBlur = () => {\n    setIsEditing(false)\n    if (editText.trim().length > 0) {\n      actions.updateTodo(todo.id, editText)\n    }\n  }\n\n  return (\n    <li className={cx({ completed: todo.completed, editing: isEditing })}>\n      <div className=\"view\">\n        <input\n          className=\"toggle\"\n          type=\"checkbox\"\n          checked={todo.completed}\n          onChange={handleCheckBoxChange}\n        />\n        <label onDoubleClick={handleDoubleClick}>{todo.name}</label>\n        <button className=\"destroy\" onClick={handleDelete} />\n      </div>\n      <input\n        ref={inputRef}\n        className=\"edit\"\n        value={editText}\n        onChange={handleEdit}\n        onKeyDown={handleKeydown}\n        onBlur={handleBlur}\n      />\n    </li>\n  )\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/TodoList.tsx",
    "content": "import { actions, useTodos } from './store'\n\nimport { TodoItem } from './TodoItem'\n\nexport function TodoList() {\n  const todos = useTodos()\n\n  const handleToggleAll = (e: React.ChangeEvent<HTMLInputElement>) => {\n    actions.toggleAll(e.target.checked)\n  }\n\n  if (!todos.length) return null\n\n  return (\n    <section className=\"main\">\n      <input\n        id=\"toggle-all\"\n        className=\"toggle-all\"\n        type=\"checkbox\"\n        onChange={handleToggleAll}\n      />\n      <label htmlFor=\"toggle-all\" />\n      <ul className=\"todo-list\">\n        {todos.map((todo) => (\n          <TodoItem key={todo.id} todo={todo} />\n        ))}\n      </ul>\n    </section>\n  )\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/main.tsx",
    "content": "import { render } from 'react-dom'\n\nimport App from './App'\n\nconst rootElement = document.getElementById('root')\nrender(<App />, rootElement)\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/react-app-env.d.ts",
    "content": "/// <reference types=\"react-scripts\" />\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/store.ts",
    "content": "import { proxy, useSnapshot } from 'valtio'\nimport { proxyMap } from 'valtio/utils'\n\nexport interface Todo {\n  id: number\n  name: string\n  completed?: boolean\n}\n\nexport const filters: Filter[] = ['all', 'todo', 'done']\ntype Filter = 'all' | 'todo' | 'done'\n\ninterface Store {\n  todos: Map<number, Todo>\n  filter: Filter\n}\n\nexport const store = proxy<Store>({\n  todos: proxyMap(),\n  filter: 'all',\n})\n\nlet id = 0\nexport const actions = {\n  addTodo(todo: Omit<Todo, 'id'>) {\n    const nextId = id++\n    store.todos.set(nextId, { id: nextId, ...todo, completed: false })\n  },\n  toggleTodo(id: number, value: boolean) {\n    const todo = store.todos.get(id)\n    if (todo && value) todo.completed = value\n    else if (todo) todo.completed = !todo.completed\n  },\n  toggleAll(completed: boolean) {\n    store.todos.forEach((todo) => {\n      todo.completed = completed\n    })\n  },\n  removeTodo(id: number) {\n    store.todos.delete(id)\n  },\n  toggleFilter(filter: Filter) {\n    store.filter = filter\n  },\n  updateTodo(id: number, value: string) {\n    const todo = store.todos.get(id)\n    if (todo) todo.name = value\n  },\n}\n\nexport function useTodos() {\n  const snapshot = useSnapshot(store)\n\n  switch (snapshot.filter) {\n    case 'all':\n      return Array.from(snapshot.todos.values())\n    case 'done':\n      return Array.from(snapshot.todos.values()).filter(\n        (todo) => todo.completed,\n      )\n    case 'todo':\n      return Array.from(snapshot.todos.values()).filter(\n        (todo) => !todo.completed,\n      )\n    default:\n      throw Error('Error: un supported filter')\n  }\n}\n\nexport function useTodosCount() {\n  const snapshot = useSnapshot(store)\n  const count = {\n    active: 0,\n    completed: 0,\n  }\n\n  snapshot.todos.forEach(({ completed }) => {\n    if (completed) {\n      count.completed++\n    } else {\n      count.active++\n    }\n  })\n\n  return count\n}\n\nexport function useFilter() {\n  return useSnapshot(store).filter\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/src/styles.css",
    "content": "/* https://www.npmjs.com/package/todomvc-app-css */\nhtml,\nbody {\n  margin: 0;\n  padding: 0;\n}\n\nbutton {\n  margin: 0;\n  padding: 0;\n  border: 0;\n  background: none;\n  font-size: 100%;\n  vertical-align: baseline;\n  font-family: inherit;\n  font-weight: inherit;\n  color: inherit;\n  -webkit-appearance: none;\n  appearance: none;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\nbody {\n  font:\n    14px 'Helvetica Neue',\n    Helvetica,\n    Arial,\n    sans-serif;\n  line-height: 1.4em;\n  background: #f5f5f5;\n  color: #111111;\n  min-width: 230px;\n  max-width: 550px;\n  margin: 0 auto;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  font-weight: 300;\n}\n\n.hidden {\n  display: none;\n}\n\n.todoapp {\n  background: #fff;\n  margin: 130px 0 40px 0;\n  position: relative;\n  box-shadow:\n    0 2px 4px 0 rgba(0, 0, 0, 0.2),\n    0 25px 50px 0 rgba(0, 0, 0, 0.1);\n}\n\n.todoapp input::-webkit-input-placeholder {\n  font-style: italic;\n  font-weight: 400;\n  color: rgba(0, 0, 0, 0.4);\n}\n\n.todoapp input::-moz-placeholder {\n  font-style: italic;\n  font-weight: 400;\n  color: rgba(0, 0, 0, 0.4);\n}\n\n.todoapp input::input-placeholder {\n  font-style: italic;\n  font-weight: 400;\n  color: rgba(0, 0, 0, 0.4);\n}\n\n.todoapp h1 {\n  position: absolute;\n  top: -140px;\n  width: 100%;\n  font-size: 80px;\n  font-weight: 200;\n  text-align: center;\n  color: rgba(175, 47, 47, 0.45);\n  -webkit-text-rendering: optimizeLegibility;\n  -moz-text-rendering: optimizeLegibility;\n  text-rendering: optimizeLegibility;\n}\n\n.new-todo,\n.edit {\n  position: relative;\n  margin: 0;\n  width: 100%;\n  font-size: 24px;\n  font-family: inherit;\n  font-weight: inherit;\n  line-height: 1.4em;\n  color: inherit;\n  padding: 6px;\n  border: 1px solid #999;\n  box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);\n  box-sizing: border-box;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.new-todo {\n  padding: 16px 16px 16px 60px;\n  height: 65px;\n  border: none;\n  background: rgba(0, 0, 0, 0.003);\n  box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);\n}\n\n.main {\n  position: relative;\n  z-index: 2;\n  border-top: 1px solid #e6e6e6;\n}\n\n.toggle-all {\n  width: 1px;\n  height: 1px;\n  border: none; /* Mobile Safari */\n  opacity: 0;\n  position: absolute;\n  right: 100%;\n  bottom: 100%;\n}\n\n.toggle-all + label {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 45px;\n  height: 65px;\n  font-size: 0;\n  position: absolute;\n  top: -65px;\n  left: -0;\n}\n\n.toggle-all + label:before {\n  content: '❯';\n  display: inline-block;\n  font-size: 22px;\n  color: #949494;\n  padding: 10px 27px 10px 27px;\n  -webkit-transform: rotate(90deg);\n  transform: rotate(90deg);\n}\n\n.toggle-all:checked + label:before {\n  color: #484848;\n}\n\n.todo-list {\n  margin: 0;\n  padding: 0;\n  list-style: none;\n}\n\n.todo-list li {\n  position: relative;\n  font-size: 24px;\n  border-bottom: 1px solid #ededed;\n}\n\n.todo-list li:last-child {\n  border-bottom: none;\n}\n\n.todo-list li.editing {\n  border-bottom: none;\n  padding: 0;\n}\n\n.todo-list li.editing .edit {\n  display: block;\n  width: calc(100% - 43px);\n  padding: 12px 16px;\n  margin: 0 0 0 43px;\n}\n\n.todo-list li.editing .view {\n  display: none;\n}\n\n.todo-list li .toggle {\n  text-align: center;\n  width: 40px;\n  /* auto, since non-WebKit browsers doesn't support input styling */\n  height: auto;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  margin: auto 0;\n  border: none; /* Mobile Safari */\n  -webkit-appearance: none;\n  appearance: none;\n}\n\n.todo-list li .toggle {\n  opacity: 0;\n}\n\n.todo-list li .toggle + label {\n  /*\n\t\tFirefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433\n\t\tIE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/\n\t*/\n  background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23949494%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');\n  background-repeat: no-repeat;\n  background-position: center left;\n}\n\n.todo-list li .toggle:checked + label {\n  background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E');\n}\n\n.todo-list li label {\n  word-break: break-all;\n  padding: 15px 15px 15px 60px;\n  display: block;\n  line-height: 1.2;\n  transition: color 0.4s;\n  font-weight: 400;\n  color: #484848;\n}\n\n.todo-list li.completed label {\n  color: #949494;\n  text-decoration: line-through;\n}\n\n.todo-list li .destroy {\n  display: none;\n  position: absolute;\n  top: 0;\n  right: 10px;\n  bottom: 0;\n  width: 40px;\n  height: 40px;\n  margin: auto 0;\n  font-size: 30px;\n  color: #949494;\n  transition: color 0.2s ease-out;\n}\n\n.todo-list li .destroy:hover,\n.todo-list li .destroy:focus {\n  color: #c18585;\n}\n\n.todo-list li .destroy:after {\n  content: '×';\n  display: block;\n  height: 100%;\n  line-height: 1.1;\n}\n\n.todo-list li:hover .destroy {\n  display: block;\n}\n\n.todo-list li .edit {\n  display: none;\n}\n\n.todo-list li.editing:last-child {\n  margin-bottom: -1px;\n}\n\n.footer {\n  padding: 10px 15px;\n  height: 20px;\n  text-align: center;\n  font-size: 15px;\n  border-top: 1px solid #e6e6e6;\n}\n\n.footer:before {\n  content: '';\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  height: 50px;\n  overflow: hidden;\n  box-shadow:\n    0 1px 1px rgba(0, 0, 0, 0.2),\n    0 8px 0 -3px #f6f6f6,\n    0 9px 1px -3px rgba(0, 0, 0, 0.2),\n    0 16px 0 -6px #f6f6f6,\n    0 17px 2px -6px rgba(0, 0, 0, 0.2);\n}\n\n.todo-count {\n  float: left;\n  text-align: left;\n}\n\n.todo-count strong {\n  font-weight: 300;\n}\n\n.filters {\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  position: absolute;\n  right: 0;\n  left: 0;\n}\n\n.filters li {\n  display: inline;\n}\n\n.filters li button {\n  color: inherit;\n  padding: 3px 7px;\n  text-decoration: none;\n  border: 1px solid transparent;\n  border-radius: 3px;\n}\n\n.filters li button:hover {\n  border-color: #db7676;\n}\n\n.filters li button.selected {\n  border-color: #ce4646;\n}\n\n.clear-completed,\nhtml .clear-completed:active {\n  float: right;\n  position: relative;\n  line-height: 19px;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.clear-completed:hover {\n  text-decoration: underline;\n}\n\n.info {\n  margin: 65px auto 0;\n  color: #4d4d4d;\n  font-size: 11px;\n  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n  text-align: center;\n}\n\n.info p {\n  line-height: 1;\n}\n\n.info a {\n  color: inherit;\n  text-decoration: none;\n  font-weight: 400;\n}\n\n.info a:hover {\n  text-decoration: underline;\n}\n\n/*\n\tHack to remove background from Mobile Safari.\n\tCan't use it globally since it destroys checkboxes in Firefox\n*/\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  .toggle-all,\n  .todo-list li .toggle {\n    background: none;\n  }\n\n  .todo-list li .toggle {\n    height: 40px;\n  }\n}\n\n@media (max-width: 430px) {\n  .footer {\n    height: 50px;\n  }\n\n  .filters {\n    bottom: 10px;\n  }\n}\n\n:focus,\n.toggle:focus + label,\n.toggle-all:focus + label {\n  box-shadow: 0 0 2px 2px #cf7d7d;\n  outline: 0;\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./tsconfig.app.json\" },\n    { \"path\": \"./tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n\n    /* Linting */\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/todo-with-proxyMap/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"valtio\",\n  \"description\": \"🧙 Valtio makes proxy-state simple for React and Vanilla\",\n  \"private\": true,\n  \"type\": \"commonjs\",\n  \"version\": \"2.3.1\",\n  \"main\": \"./index.js\",\n  \"types\": \"./index.d.ts\",\n  \"typesVersions\": {\n    \">=4.5\": {\n      \"esm/*\": [\n        \"esm/*\"\n      ],\n      \"*\": [\n        \"*\"\n      ]\n    },\n    \"*\": {\n      \"esm/*\": [\n        \"ts_version_4.5_and_above_is_required.d.ts\"\n      ],\n      \"*\": [\n        \"ts_version_4.5_and_above_is_required.d.ts\"\n      ]\n    }\n  },\n  \"exports\": {\n    \"./package.json\": \"./package.json\",\n    \".\": {\n      \"react-native\": {\n        \"types\": \"./index.d.ts\",\n        \"default\": \"./index.js\"\n      },\n      \"import\": {\n        \"types\": \"./esm/index.d.mts\",\n        \"default\": \"./esm/index.mjs\"\n      },\n      \"default\": {\n        \"types\": \"./index.d.ts\",\n        \"default\": \"./index.js\"\n      }\n    },\n    \"./*\": {\n      \"react-native\": {\n        \"types\": \"./*.d.ts\",\n        \"default\": \"./*.js\"\n      },\n      \"import\": {\n        \"types\": \"./esm/*.d.mts\",\n        \"default\": \"./esm/*.mjs\"\n      },\n      \"default\": {\n        \"types\": \"./*.d.ts\",\n        \"default\": \"./*.js\"\n      }\n    }\n  },\n  \"files\": [\n    \"**\"\n  ],\n  \"sideEffects\": false,\n  \"scripts\": {\n    \"prebuild\": \"shx rm -rf dist\",\n    \"build\": \"pnpm run prebuild && pnpm run \\\"/^build:.*/\\\" && pnpm run postbuild\",\n    \"build-watch\": \"pnpm run \\\"/^build:.*/\\\" --watch\",\n    \"build:base\": \"rollup -c\",\n    \"build:utils\": \"rollup -c --config-utils\",\n    \"build:vanilla\": \"rollup -c --config-vanilla\",\n    \"build:vanilla_utils\": \"rollup -c --config-vanilla_utils\",\n    \"build:react\": \"rollup -c --config-react\",\n    \"build:react_utils\": \"rollup -c --config-react_utils\",\n    \"postbuild\": \"pnpm run patch-d-ts && pnpm run copy && pnpm run patch-old-ts && pnpm run patch-esm-ts\",\n    \"fix\": \"pnpm run fix:lint && pnpm run fix:format\",\n    \"fix:format\": \"prettier . --write\",\n    \"fix:lint\": \"eslint . --fix\",\n    \"test\": \"pnpm run \\\"/^test:.*/\\\"\",\n    \"test:format\": \"prettier . --list-different\",\n    \"test:types\": \"tsc --noEmit\",\n    \"test:lint\": \"eslint .\",\n    \"test:spec\": \"vitest run\",\n    \"patch-d-ts\": \"node --input-type=module -e \\\"import { entries } from './rollup.config.mjs'; import shelljs from 'shelljs'; const { find, sed } = shelljs; find('dist/**/*.d.ts').forEach(f => { entries.forEach(({ find, replacement }) => { sed('-i', new RegExp(' from \\\\'' + find.source.slice(0, -1) + '\\\\';$'), ' from \\\\'' + replacement + '\\\\';', f); }); sed('-i', / from '(\\\\.[^']+)\\\\.ts';$/, ' from \\\\'\\\\$1\\\\';', f); });\\\"\",\n    \"copy\": \"shx cp -r dist/src/* dist/esm && shx cp -r dist/src/* dist && shx rm -rf dist/{src,tests} && shx cp package.json README.md LICENSE dist && json -I -f dist/package.json -e \\\"this.private=false; this.devDependencies=undefined; this.optionalDependencies=undefined; this.scripts=undefined; this.prettier=undefined;\\\"\",\n    \"patch-old-ts\": \"shx touch dist/ts_version_4.5_and_above_is_required.d.ts\",\n    \"patch-esm-ts\": \"node -e \\\"require('shelljs').find('dist/esm/**/*.d.ts').forEach(f=>{var f2=f.replace(/\\\\.ts$/,'.mts');require('fs').renameSync(f,f2);require('shelljs').sed('-i',/ from '(\\\\.[^']+)';$/,' from \\\\'\\\\$1.mjs\\\\';',f2);require('shelljs').sed('-i',/^declare module '(\\\\.[^']+)'/,'declare module \\\\'\\\\$1.mjs\\\\'',f2)})\\\"\"\n  },\n  \"engines\": {\n    \"node\": \">=12.20.0\"\n  },\n  \"prettier\": {\n    \"semi\": false,\n    \"singleQuote\": true\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/pmndrs/valtio.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"state\",\n    \"manager\",\n    \"management\",\n    \"mobx\",\n    \"proxy\",\n    \"store\"\n  ],\n  \"author\": \"Daishi Kato\",\n  \"contributors\": [],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/pmndrs/valtio/issues\"\n  },\n  \"homepage\": \"https://github.com/pmndrs/valtio\",\n  \"packageManager\": \"pnpm@10.18.3\",\n  \"dependencies\": {\n    \"proxy-compare\": \"^3.0.1\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.39.3\",\n    \"@redux-devtools/extension\": \"^3.3.0\",\n    \"@rollup/plugin-alias\": \"^6.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.3\",\n    \"@rollup/plugin-replace\": \"^6.0.3\",\n    \"@rollup/plugin-typescript\": \"^12.3.0\",\n    \"@testing-library/jest-dom\": \"^6.9.1\",\n    \"@testing-library/react\": \"^16.3.2\",\n    \"@types/jsdom\": \"^28.0.0\",\n    \"@types/react\": \"^19.2.14\",\n    \"@types/react-dom\": \"^19.2.3\",\n    \"@vitest/coverage-v8\": \"^4.0.18\",\n    \"@vitest/eslint-plugin\": \"^1.6.9\",\n    \"@vitest/ui\": \"^4.0.18\",\n    \"esbuild\": \"^0.27.3\",\n    \"eslint\": \"^9.39.3\",\n    \"eslint-import-resolver-typescript\": \"^4.4.4\",\n    \"eslint-plugin-import\": \"^2.32.0\",\n    \"eslint-plugin-jest-dom\": \"^5.5.0\",\n    \"eslint-plugin-react\": \"^7.37.5\",\n    \"eslint-plugin-react-hooks\": \"7.0.1\",\n    \"eslint-plugin-testing-library\": \"^7.16.0\",\n    \"jest-leak-detector\": \"^30.2.0\",\n    \"jsdom\": \"^28.1.0\",\n    \"json\": \"^11.0.0\",\n    \"postinstall-postinstall\": \"^2.1.0\",\n    \"prettier\": \"^3.8.1\",\n    \"proxy-memoize\": \"^3.0.1\",\n    \"react\": \"19.2.4\",\n    \"react-dom\": \"19.2.4\",\n    \"redux\": \"^5.0.1\",\n    \"rollup\": \"^4.59.0\",\n    \"rollup-plugin-esbuild\": \"^6.2.1\",\n    \"shelljs\": \"^0.10.0\",\n    \"shx\": \"^0.4.0\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.56.1\",\n    \"vite\": \"^7.3.1\",\n    \"vitest\": \"^4.0.18\"\n  },\n  \"peerDependencies\": {\n    \"@types/react\": \">=18.0.0\",\n    \"react\": \">=18.0.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/react\": {\n      \"optional\": true\n    },\n    \"react\": {\n      \"optional\": true\n    }\n  }\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - .\nminimumReleaseAge: 1440\n"
  },
  {
    "path": "rollup.config.mjs",
    "content": "/*global process*/\nimport path from 'path'\nimport alias from '@rollup/plugin-alias'\nimport resolve from '@rollup/plugin-node-resolve'\nimport replace from '@rollup/plugin-replace'\nimport typescript from '@rollup/plugin-typescript'\nimport esbuild from 'rollup-plugin-esbuild'\n\nconst extensions = ['.js', '.ts', '.tsx']\nconst { root } = path.parse(process.cwd())\nexport const entries = [\n  { find: /.*\\/vanilla\\/utils\\.ts$/, replacement: 'valtio/vanilla/utils' },\n  { find: /.*\\/react\\/utils\\.ts$/, replacement: 'valtio/react/utils' },\n  { find: /.*\\/vanilla\\.ts$/, replacement: 'valtio/vanilla' },\n  { find: /.*\\/react\\.ts$/, replacement: 'valtio/react' },\n]\n\nfunction external(id) {\n  return !id.startsWith('.') && !id.startsWith(root)\n}\n\nfunction getEsbuild() {\n  return esbuild({\n    target: 'es2018',\n    supported: { 'import-meta': true },\n    tsconfig: path.resolve('./tsconfig.json'),\n  })\n}\n\nfunction createDeclarationConfig(input, output) {\n  return {\n    input,\n    output: {\n      dir: output,\n    },\n    external,\n    plugins: [\n      typescript({\n        declaration: true,\n        emitDeclarationOnly: true,\n        outDir: output,\n      }),\n    ],\n  }\n}\n\nfunction createESMConfig(input, output) {\n  return {\n    input,\n    output: { file: output, format: 'esm' },\n    external,\n    plugins: [\n      alias({ entries: entries.filter((entry) => !entry.find.test(input)) }),\n      resolve({ extensions }),\n      replace({\n        ...(output.endsWith('.js')\n          ? {\n              'import.meta.env?.MODE': 'process.env.NODE_ENV',\n            }\n          : {\n              'import.meta.env?.MODE':\n                '(import.meta.env ? import.meta.env.MODE : undefined)',\n            }),\n        delimiters: ['\\\\b', '\\\\b(?!(\\\\.|/))'],\n        preventAssignment: true,\n      }),\n      getEsbuild(),\n    ],\n  }\n}\n\nfunction createCommonJSConfig(input, output) {\n  return {\n    input,\n    output: { file: output, format: 'cjs' },\n    external,\n    plugins: [\n      alias({ entries: entries.filter((entry) => !entry.find.test(input)) }),\n      resolve({ extensions }),\n      replace({\n        'import.meta.env?.MODE': 'process.env.NODE_ENV',\n        delimiters: ['\\\\b', '\\\\b(?!(\\\\.|/))'],\n        preventAssignment: true,\n      }),\n      getEsbuild(),\n    ],\n  }\n}\n\nexport default function (args) {\n  let c = Object.keys(args).find((key) => key.startsWith('config-'))\n  if (c) {\n    c = c.slice('config-'.length).replace(/_/g, '/')\n  } else {\n    c = 'index'\n  }\n  return [\n    ...(c === 'index' ? [createDeclarationConfig(`src/${c}.ts`, 'dist')] : []),\n    createCommonJSConfig(`src/${c}.ts`, `dist/${c}.js`),\n    createESMConfig(`src/${c}.ts`, `dist/esm/${c}.mjs`),\n  ]\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './vanilla.ts'\nexport * from './react.ts'\n"
  },
  {
    "path": "src/react/utils/useProxy.ts",
    "content": "import { useLayoutEffect } from 'react'\nimport { useSnapshot } from '../../react.ts'\n\nconst DUMMY_SYMBOL = Symbol()\n\n/**\n * useProxy\n *\n * Takes a proxy and returns a new proxy which you can use in both react render\n * and in callbacks. The root reference is replaced on every render, but the\n * keys (and subkeys) below it are stable until they're intentionally mutated.\n * For the best ergonomics, you can export a custom hook from your store, so you\n * don't have to figure out a separate name for the hook reference. E.g.:\n *\n * @example\n * export const store = proxy(initialState)\n * export const useStore = () => useProxy(store)\n * // in the component file:\n * function Cmp() {\n *   const store = useStore()\n *   return <button onClick={() => {store.count++}}>{store.count}</button>\n * }\n *\n */\nexport function useProxy<T extends object>(\n  proxy: T,\n  options?: NonNullable<Parameters<typeof useSnapshot>[1]>,\n): T {\n  const snapshot = useSnapshot(proxy, options) as T\n\n  // touch dummy prop so that it doesn't trigger re-renders when no props are touched.\n  // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n  ;(snapshot as any)[DUMMY_SYMBOL]\n\n  let isRendering = true\n  useLayoutEffect(() => {\n    // This is an intentional hack\n    // It might not work with React Compiler\n    // eslint-disable-next-line react-hooks/immutability, react-hooks/exhaustive-deps\n    isRendering = false\n  })\n\n  return new Proxy(proxy, {\n    get(target, prop) {\n      return isRendering ? snapshot[prop as keyof T] : target[prop as keyof T]\n    },\n  })\n}\n"
  },
  {
    "path": "src/react/utils.ts",
    "content": "export { useProxy } from './utils/useProxy.ts'\n"
  },
  {
    "path": "src/react.ts",
    "content": "import {\n  useCallback,\n  useDebugValue,\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useSyncExternalStore,\n} from 'react'\nimport {\n  affectedToPathList,\n  createProxy as createProxyToCompare,\n  isChanged,\n} from 'proxy-compare'\nimport { snapshot, subscribe } from './vanilla.ts'\nimport type { Snapshot } from './vanilla.ts'\n\n/**\n * React hook to display affected paths in React DevTools for debugging\n *\n * This internal hook collects the paths that were accessed during render\n * and displays them in React DevTools to help with debugging render optimizations.\n */\nconst useAffectedDebugValue = (\n  state: object,\n  affected: WeakMap<object, unknown>,\n) => {\n  const pathList = useRef<(string | number | symbol)[][]>(undefined)\n  useEffect(() => {\n    pathList.current = affectedToPathList(state, affected, true)\n  })\n  // TODO should we use useState instead?\n  // eslint-disable-next-line react-hooks/refs\n  useDebugValue(pathList.current)\n}\nconst condUseAffectedDebugValue = useAffectedDebugValue\n\n// This is required only for performance.\n// Ref: https://github.com/pmndrs/valtio/issues/519\nconst targetCache = new WeakMap()\n\ntype Options = {\n  sync?: boolean\n}\n\n/**\n * useSnapshot\n *\n * Create a local snapshot that catches changes. This hook actually returns a wrapped snapshot in a proxy for\n * render optimization instead of a plain object compared to `snapshot()` method.\n * Rule of thumb: read from snapshots, mutate the source.\n * The component will only re-render when the parts of the state you access have changed, it is render-optimized.\n *\n * @example A\n * function Counter() {\n *   const snap = useSnapshot(state)\n *   return (\n *     <div>\n *       {snap.count}\n *       <button onClick={() => ++state.count}>+1</button>\n *     </div>\n *   )\n * }\n *\n * [Notes]\n * Every object inside your proxy also becomes a proxy (if you don't use \"ref\"), so you can also use them to create\n * the local snapshot as seen on example B.\n *\n * @example B\n * function ProfileName() {\n *   const snap = useSnapshot(state.profile)\n *   return (\n *     <div>\n *       {snap.name}\n *     </div>\n *   )\n * }\n *\n * Beware that you still can replace the child proxy with something else so it will break your snapshot. You can see\n * above what happens with the original proxy when you replace the child proxy.\n *\n * > console.log(state)\n * { profile: { name: \"valtio\" } }\n * > childState = state.profile\n * > console.log(childState)\n * { name: \"valtio\" }\n * > state.profile.name = \"react\"\n * > console.log(childState)\n * { name: \"react\" }\n * > state.profile = { name: \"new name\" }\n * > console.log(childState)\n * { name: \"react\" }\n * > console.log(state)\n * { profile: { name: \"new name\" } }\n *\n * `useSnapshot()` depends on the original reference of the child proxy so if you replace it with a new one, the\n * component that is subscribed to the old proxy won't receive new updates because it is still subscribed to\n * the old one.\n *\n * In this case we recommend the example C or D. On both examples you don't need to worry with re-render,\n * because it is render-optimized.\n *\n * @example C\n * const snap = useSnapshot(state)\n * return (\n *   <div>\n *     {snap.profile.name}\n *   </div>\n * )\n *\n * @example D\n * const { profile } = useSnapshot(state)\n * return (\n *   <div>\n *     {profile.name}\n *   </div>\n * )\n */\nexport function useSnapshot<T extends object>(\n  proxyObject: T,\n  options?: Options,\n): Snapshot<T> {\n  const notifyInSync = options?.sync\n  // per-proxy & per-hook affected, it's not ideal but memo compatible\n  const affected = useMemo(\n    () => proxyObject && new WeakMap<object, unknown>(),\n    [proxyObject],\n  )\n  const lastSnapshot = useRef<Snapshot<T>>(undefined)\n  let inRender = true\n  const currSnapshot = useSyncExternalStore(\n    useCallback(\n      (callback) => {\n        const unsub = subscribe(proxyObject, callback, notifyInSync)\n        callback() // Note: do we really need this?\n        return unsub\n      },\n      [proxyObject, notifyInSync],\n    ),\n    () => {\n      const nextSnapshot = snapshot(proxyObject)\n      try {\n        if (\n          !inRender &&\n          lastSnapshot.current &&\n          !isChanged(\n            lastSnapshot.current,\n            nextSnapshot,\n            affected,\n            new WeakMap(),\n          )\n        ) {\n          // not changed\n          return lastSnapshot.current\n        }\n      } catch {\n        // ignore if a promise or something is thrown\n      }\n      return nextSnapshot\n    },\n    () => snapshot(proxyObject),\n  )\n  // TODO how could we bypass this?\n  // eslint-disable-next-line react-hooks/immutability\n  inRender = false\n  useLayoutEffect(() => {\n    lastSnapshot.current = currSnapshot\n  })\n  if (import.meta.env?.MODE !== 'production') {\n    condUseAffectedDebugValue(currSnapshot as object, affected)\n  }\n  const proxyCache = useMemo(() => new WeakMap(), []) // per-hook proxyCache\n  return createProxyToCompare(currSnapshot, affected, proxyCache, targetCache)\n}\n"
  },
  {
    "path": "src/types.d.ts",
    "content": "declare interface ImportMeta {\n  env?: {\n    MODE: string\n  }\n}\n"
  },
  {
    "path": "src/utils.ts",
    "content": "export * from './vanilla/utils.ts'\nexport * from './react/utils.ts'\n"
  },
  {
    "path": "src/vanilla/utils/deepClone.ts",
    "content": "import { unstable_getInternalStates } from '../../vanilla.ts'\n\nconst isObject = (x: unknown): x is object =>\n  typeof x === 'object' && x !== null\n\nlet defaultRefSet: WeakSet<object> | undefined\nconst getDefaultRefSet = (): WeakSet<object> => {\n  if (!defaultRefSet) {\n    defaultRefSet = unstable_getInternalStates().refSet\n  }\n  return defaultRefSet\n}\n\n/**\n * Creates a deep clone of an object, maintaining proxy behavior for Maps and Sets\n */\nexport function deepClone<T>(\n  obj: T,\n  getRefSet: () => WeakSet<object> = getDefaultRefSet,\n): T {\n  if (!isObject(obj) || getRefSet().has(obj)) {\n    return obj\n  }\n\n  const baseObject: T = Array.isArray(obj)\n    ? []\n    : Object.create(Object.getPrototypeOf(obj))\n  Reflect.ownKeys(obj).forEach((key) => {\n    baseObject[key as keyof T] = deepClone(obj[key as keyof T], getRefSet)\n  })\n  return baseObject\n}\n"
  },
  {
    "path": "src/vanilla/utils/deepProxy.ts",
    "content": "import { proxy, unstable_getInternalStates } from '../../vanilla.ts'\nimport { isProxyMap, proxyMap } from './proxyMap.ts'\nimport { isProxySet, proxySet } from './proxySet.ts'\n\nconst isObject = (x: unknown): x is object =>\n  typeof x === 'object' && x !== null\n\nlet defaultRefSet: WeakSet<object> | undefined\nconst getDefaultRefSet = (): WeakSet<object> => {\n  if (!defaultRefSet) {\n    defaultRefSet = unstable_getInternalStates().refSet\n  }\n  return defaultRefSet\n}\n\nconst cloneContainer = <T extends object>(src: T): T => {\n  return (\n    Array.isArray(src) ? [] : Object.create(Object.getPrototypeOf(src))\n  ) as T\n}\n\n/**\n * Deeply proxies an input while normalizing Maps/Sets into proxyMap/proxySet.\n * - Values in refSet or primitives are returned as-is.\n * - Map/proxyMap and Set/proxySet are re-instantiated by passing their existing iterables directly.\n * - Arrays/objects are rebuilt recursively and wrapped with proxy().\n */\nexport function unstable_deepProxy<T>(\n  obj: T,\n  getRefSet: () => WeakSet<object> = getDefaultRefSet,\n): T {\n  const memo = new WeakMap<object, unknown>()\n\n  const visit = (value: unknown) => {\n    if (!isObject(value) || getRefSet().has(value)) return value\n\n    // Normalize Sets: deep-visit each element before wrapping\n    if (value instanceof Set || isProxySet(value as object)) {\n      const input = value as Iterable<unknown>\n      const items: unknown[] = []\n      for (const el of input) {\n        items.push(visit(el))\n      }\n      return proxySet(items)\n    }\n\n    // Normalize Maps: deep-visit keys and values before wrapping\n    if (value instanceof Map || isProxyMap(value as object)) {\n      const input = value as Iterable<[unknown, unknown]>\n      const entries: [unknown, unknown][] = []\n      for (const [k, v] of input) {\n        entries.push([visit(k), visit(v)])\n      }\n      return proxyMap(entries)\n    }\n\n    const hit = memo.get(value)\n\n    if (hit) return hit\n\n    const target = cloneContainer(value)\n    memo.set(value, target)\n\n    for (const key of Reflect.ownKeys(value)) {\n      const desc = Reflect.getOwnPropertyDescriptor(value, key)\n      if (!desc) continue // type guard to make sure we can access metadata\n      if ('value' in desc) {\n        const next = visit((value as Record<PropertyKey, unknown>)[key])\n        Object.defineProperty(target, key, { ...desc, value: next })\n      } else {\n        Object.defineProperty(target, key, desc)\n      }\n    }\n\n    return proxy(target)\n  }\n\n  return visit(obj) as T\n}\n"
  },
  {
    "path": "src/vanilla/utils/devtools.ts",
    "content": "import { snapshot, subscribe, unstable_enableOp } from '../../vanilla.ts'\nimport type {} from '@redux-devtools/extension'\n\n// FIXME https://github.com/reduxjs/redux-devtools/issues/1097\ntype Message = {\n  type: string\n  payload?: any\n  state?: any\n}\n\nconst DEVTOOLS = Symbol()\n\ntype Config = Parameters<\n  (Window extends { __REDUX_DEVTOOLS_EXTENSION__?: infer T }\n    ? T\n    : { connect: (param: any) => any })['connect']\n>[0]\n\ntype Options = {\n  enabled?: boolean\n  name?: string\n} & Config\n\n/**\n * Connects a proxy object to Redux DevTools Extension for state debugging\n *\n * This allows real-time monitoring and time-travel debugging of state changes\n * using the Redux DevTools browser extension.\n *\n * Limitation: Only plain objects/values are supported.\n *\n * @example\n * import { devtools } from 'valtio/utils'\n * const state = proxy({ count: 0, text: 'hello' })\n * const unsub = devtools(state, { name: 'state name', enabled: true })\n */\nexport function devtools<T extends object>(\n  proxyObject: T,\n  options?: Options,\n): (() => void) | undefined {\n  const { enabled, name = '', ...rest } = options || {}\n\n  let extension: (typeof window)['__REDUX_DEVTOOLS_EXTENSION__'] | false\n  try {\n    extension =\n      (enabled ?? import.meta.env?.MODE !== 'production') &&\n      window.__REDUX_DEVTOOLS_EXTENSION__\n  } catch {\n    // ignored\n  }\n  if (!extension) {\n    if (import.meta.env?.MODE !== 'production' && enabled) {\n      console.warn('[Warning] Please install/enable Redux devtools extension')\n    }\n    return\n  }\n\n  unstable_enableOp()\n  let isTimeTraveling = false\n  const devtools = extension.connect({ name, ...rest })\n  const unsub1 = subscribe(proxyObject, (unstable_ops) => {\n    const action = unstable_ops\n      .filter(([_, path]) => path[0] !== DEVTOOLS)\n      .map(([op, path]) => `${op}:${path.map(String).join('.')}`)\n      .join(', ')\n\n    if (!action) {\n      return\n    }\n\n    if (isTimeTraveling) {\n      isTimeTraveling = false\n    } else {\n      const snapWithoutDevtools = Object.assign({}, snapshot(proxyObject))\n      delete (snapWithoutDevtools as any)[DEVTOOLS]\n      devtools.send(\n        {\n          type: action,\n          updatedAt: new Date().toLocaleString(),\n        } as any,\n        snapWithoutDevtools,\n      )\n    }\n  })\n  const unsub2 = (\n    devtools as unknown as {\n      // FIXME https://github.com/reduxjs/redux-devtools/issues/1097\n      subscribe: (\n        listener: (message: Message) => void,\n      ) => (() => void) | undefined\n    }\n  ).subscribe((message) => {\n    if (message.type === 'ACTION' && message.payload) {\n      try {\n        Object.assign(proxyObject, JSON.parse(message.payload))\n      } catch (e) {\n        console.error(\n          'please dispatch a serializable value that JSON.parse() and proxy() support\\n',\n          e,\n        )\n      }\n    }\n    if (message.type === 'DISPATCH' && message.state) {\n      if (\n        message.payload?.type === 'JUMP_TO_ACTION' ||\n        message.payload?.type === 'JUMP_TO_STATE'\n      ) {\n        isTimeTraveling = true\n\n        const state = JSON.parse(message.state)\n        Object.assign(proxyObject, state)\n      }\n      ;(proxyObject as any)[DEVTOOLS] = message\n    } else if (\n      message.type === 'DISPATCH' &&\n      message.payload?.type === 'COMMIT'\n    ) {\n      devtools.init(snapshot(proxyObject))\n    } else if (\n      message.type === 'DISPATCH' &&\n      message.payload?.type === 'IMPORT_STATE'\n    ) {\n      const actions = message.payload.nextLiftedState?.actionsById\n      const computedStates =\n        message.payload.nextLiftedState?.computedStates || []\n\n      isTimeTraveling = true\n\n      computedStates.forEach(({ state }: { state: any }, index: number) => {\n        const action = actions[index] || 'No action found'\n\n        Object.assign(proxyObject, state)\n\n        if (index === 0) {\n          devtools.init(snapshot(proxyObject))\n        } else {\n          devtools.send(action, snapshot(proxyObject))\n        }\n      })\n    }\n  })\n  devtools.init(snapshot(proxyObject))\n  return () => {\n    unsub1()\n    unsub2?.()\n  }\n}\n"
  },
  {
    "path": "src/vanilla/utils/proxyMap.ts",
    "content": "import { proxy, unstable_getInternalStates } from '../../vanilla.ts'\n\nconst { proxyStateMap, snapCache } = unstable_getInternalStates()\nconst isProxy = (x: any) => proxyStateMap.has(x)\n\ntype InternalProxyObject<K, V> = Map<K, V> & {\n  data: Array<V>\n  index: number\n  epoch: number\n  toJSON: () => Map<K, V>\n}\n\n/**\n * Determines if an object is a proxy Map created with proxyMap\n */\nexport const isProxyMap = (obj: object): boolean => {\n  return (\n    Symbol.toStringTag in obj &&\n    obj[Symbol.toStringTag] === 'Map' &&\n    proxyStateMap.has(obj)\n  )\n}\n\n/**\n * Creates a reactive Map that integrates with Valtio's proxy system\n *\n * This utility creates a Map-like object that works with Valtio's reactivity system,\n * allowing you to track changes to the Map in the same way as regular proxy objects.\n * The API is the same as the standard JavaScript Map.\n *\n * @example\n * import { proxyMap } from 'valtio/utils'\n * const state = proxyMap([[\"key\", \"value\"]])\n *\n * This can be used inside a proxy as well\n *\n * const state = proxy({\n *   count: 1,\n *   map: proxyMap()\n * })\n *\n * When using an object as a key, you can wrap it with `ref` so it's not proxied\n * this is useful if you want to preserve the key equality\n *\n * import { ref } from 'valtio'\n *\n * const key = ref({})\n * state.set(key, \"value\")\n * state.get(key) //value\n *\n * const key = {}\n * state.set(key, \"value\")\n * state.get(key) //undefined\n */\nexport function proxyMap<K, V>(entries?: Iterable<[K, V]> | undefined | null) {\n  const initialData: Array<V> = []\n  let initialIndex = 0\n  const indexMap = new Map<K, number>()\n\n  const snapMapCache = new WeakMap<object, Map<K, number>>()\n  const registerSnapMap = () => {\n    const cache = snapCache.get(vObject)\n    const latestSnap = cache?.[1]\n    if (latestSnap && !snapMapCache.has(latestSnap)) {\n      const clonedMap = new Map(indexMap)\n      snapMapCache.set(latestSnap, clonedMap)\n    }\n  }\n  const getMapForThis = (x: any) => snapMapCache.get(x) || indexMap\n\n  if (entries) {\n    if (typeof entries[Symbol.iterator] !== 'function') {\n      throw new TypeError(\n        'proxyMap:\\n\\tinitial state must be iterable\\n\\t\\ttip: structure should be [[key, value]]',\n      )\n    }\n    for (const [key, value] of entries) {\n      indexMap.set(key, initialIndex)\n      initialData[initialIndex++] = value\n    }\n  }\n\n  const vObject: InternalProxyObject<K, V> = {\n    data: initialData,\n    index: initialIndex,\n    epoch: 0,\n    get size() {\n      if (!isProxy(this)) {\n        registerSnapMap()\n      }\n      const map = getMapForThis(this)\n      return map.size\n    },\n    get(key: K) {\n      const map = getMapForThis(this)\n      const index = map.get(key)\n      if (index === undefined) {\n        this.epoch // touch property for tracking\n        return undefined\n      }\n      return this.data[index]\n    },\n    has(key: K) {\n      const map = getMapForThis(this)\n      this.epoch // touch property for tracking\n      return map.has(key)\n    },\n    set(key: K, value: V) {\n      if (!isProxy(this)) {\n        throw new Error('Cannot perform mutations on a snapshot')\n      }\n      const index = indexMap.get(key)\n      if (index === undefined) {\n        indexMap.set(key, this.index)\n        this.data[this.index++] = value\n      } else {\n        this.data[index] = value\n      }\n      this.epoch++\n      return this\n    },\n    delete(key: K) {\n      if (!isProxy(this)) {\n        throw new Error('Cannot perform mutations on a snapshot')\n      }\n      const index = indexMap.get(key)\n      if (index === undefined) {\n        return false\n      }\n      delete this.data[index]\n      indexMap.delete(key)\n      this.epoch++\n      return true\n    },\n    clear() {\n      if (!isProxy(this)) {\n        throw new Error('Cannot perform mutations on a snapshot')\n      }\n      this.data.length = 0 // empty array\n      this.index = 0\n      this.epoch++\n      indexMap.clear()\n    },\n    forEach(cb: (value: V, key: K, map: Map<K, V>) => void) {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      map.forEach((index, key) => {\n        cb(this.data[index]!, key, this)\n      })\n    },\n    *entries(): MapIterator<[K, V]> {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      for (const [key, index] of map) {\n        yield [key, this.data[index]!]\n      }\n    },\n    *keys(): MapIterator<K> {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      for (const key of map.keys()) {\n        yield key\n      }\n    },\n    *values(): MapIterator<V> {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      for (const index of map.values()) {\n        yield this.data[index]!\n      }\n    },\n    [Symbol.iterator]() {\n      return this.entries()\n    },\n    get [Symbol.toStringTag]() {\n      return 'Map'\n    },\n    toJSON(): Map<K, V> {\n      return new Map(this.entries())\n    },\n  }\n\n  const proxiedObject = proxy(vObject)\n  Object.defineProperties(proxiedObject, {\n    size: { enumerable: false },\n    index: { enumerable: false },\n    epoch: { enumerable: false },\n    data: { enumerable: false },\n    toJSON: { enumerable: false },\n  })\n  Object.seal(proxiedObject)\n\n  return proxiedObject as unknown as Map<K, V> & {\n    $$valtioSnapshot: Omit<Map<K, V>, 'set' | 'delete' | 'clear'>\n  }\n}\n"
  },
  {
    "path": "src/vanilla/utils/proxySet.ts",
    "content": "import { proxy, unstable_getInternalStates } from '../../vanilla.ts'\n\nconst { proxyStateMap, snapCache } = unstable_getInternalStates()\nconst maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x)\nconst isProxy = (x: any) => proxyStateMap.has(x)\n\ntype RSetLike<T> = { has(value: T): boolean }\n\ntype InternalProxySet<T> = Set<T> & {\n  data: T[]\n  toJSON: () => object\n  index: number\n  epoch: number\n  intersection<U>(other: RSetLike<U>): Set<T & U>\n  intersection(other: Set<T>): Set<T>\n  union<U>(other: RSetLike<U>): Set<T | U>\n  union(other: Set<T>): Set<T>\n  difference<U>(other: RSetLike<U>): Set<T>\n  difference(other: Set<T>): Set<T>\n  symmetricDifference<U>(other: RSetLike<U>): Set<T | U>\n  symmetricDifference(other: Set<T>): Set<T>\n  isSubsetOf(other: RSetLike<T>): boolean\n  isSupersetOf(other: RSetLike<T>): boolean\n  isDisjointFrom(other: RSetLike<T>): boolean\n}\n\n/**\n * Determines if an object is a proxy Set created with proxySet\n */\nexport const isProxySet = (obj: object): boolean => {\n  return (\n    Symbol.toStringTag in obj &&\n    obj[Symbol.toStringTag] === 'Set' &&\n    proxyStateMap.has(obj)\n  )\n}\n\n/**\n * Creates a reactive Set that integrates with Valtio's proxy system\n *\n * This utility creates a Set-like object that works with Valtio's reactivity system,\n * allowing you to track changes to the Set in the same way as regular proxy objects.\n * The API extends the standard JavaScript Set with additional set operations like\n * union, intersection, difference, etc.\n *\n * @example\n * import { proxySet } from 'valtio/utils'\n * const state = proxySet([1,2,3])\n *\n * // can be used inside a proxy as well\n * const state = proxy({\n *   count: 1,\n *   set: proxySet()\n * })\n */\n\nexport function proxySet<T>(initialValues?: Iterable<T> | null) {\n  const initialData: T[] = []\n  const indexMap = new Map<T, number>()\n  let initialIndex = 0\n\n  const snapMapCache = new WeakMap<object, Map<T, number>>()\n  const registerSnapMap = () => {\n    const cache = snapCache.get(vObject)\n    const latestSnap = cache?.[1]\n    if (latestSnap && !snapMapCache.has(latestSnap)) {\n      const clonedMap = new Map(indexMap)\n      snapMapCache.set(latestSnap, clonedMap)\n    }\n  }\n  const getMapForThis = (x: any) => snapMapCache.get(x) || indexMap\n\n  if (initialValues) {\n    if (typeof initialValues[Symbol.iterator] !== 'function') {\n      throw new TypeError('not iterable')\n    }\n    for (const value of initialValues) {\n      if (!indexMap.has(value)) {\n        const v = maybeProxify(value)\n        indexMap.set(v, initialIndex)\n        initialData[initialIndex++] = v\n      }\n    }\n  }\n\n  const isIterable = (o: unknown): o is Iterable<unknown> =>\n    typeof o === 'object' && o !== null && Symbol.iterator in (o as object)\n\n  const hasForEach = <U>(\n    o: RSetLike<U>,\n  ): o is RSetLike<U> & { forEach: (cb: (v: U) => void) => void } =>\n    typeof (o as { forEach?: unknown }).forEach === 'function'\n\n  const asIterable = <U>(other: RSetLike<U> | Set<U>): Iterable<U> => {\n    if (isIterable(other)) return other as Iterable<U>\n    if (hasForEach(other)) {\n      const acc: U[] = []\n      other.forEach((v) => acc.push(v))\n      return acc\n    }\n    throw new TypeError('Expected an iterable')\n  }\n\n  function intersectionImpl<T, U>(\n    this: InternalProxySet<T>,\n    other: RSetLike<U>,\n  ): Set<T & U>\n  function intersectionImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>\n  function intersectionImpl<T>(\n    this: InternalProxySet<T>,\n    other: RSetLike<unknown> | Set<T>,\n  ): Set<unknown> {\n    this.epoch // touch property for tracking\n    const otherSet = proxySet(asIterable(other))\n    const result = proxySet<T>()\n    for (const value of this.values()) {\n      if (otherSet.has(value)) {\n        result.add(value)\n      }\n    }\n    return proxySet(result)\n  }\n\n  function unionImpl<T, U>(\n    this: InternalProxySet<T>,\n    other: RSetLike<U>,\n  ): Set<T | U>\n  function unionImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>\n  function unionImpl<T>(\n    this: InternalProxySet<T>,\n    other: RSetLike<unknown> | Set<T>,\n  ): Set<unknown> {\n    this.epoch // touch property for tracking\n    const otherSet = proxySet(asIterable(other))\n    const result = proxySet<unknown>()\n    for (const v of this.values()) result.add(v)\n    for (const v of otherSet.values()) result.add(v)\n    return proxySet(result)\n  }\n\n  function differenceImpl<T, U>(\n    this: InternalProxySet<T>,\n    other: RSetLike<U>,\n  ): Set<T>\n  function differenceImpl<T>(this: InternalProxySet<T>, other: Set<T>): Set<T>\n  function differenceImpl<T>(\n    this: InternalProxySet<T>,\n    other: RSetLike<unknown> | Set<T>,\n  ): Set<T> {\n    this.epoch // touch property for tracking\n    const otherSet = proxySet(asIterable(other))\n    const result = proxySet<T>()\n    for (const v of this.values()) if (!otherSet.has(v)) result.add(v)\n    return proxySet(result)\n  }\n\n  function symmetricDifferenceImpl<T, U>(\n    this: InternalProxySet<T>,\n    other: RSetLike<U>,\n  ): Set<T | U>\n  function symmetricDifferenceImpl<T>(\n    this: InternalProxySet<T>,\n    other: Set<T>,\n  ): Set<T>\n  function symmetricDifferenceImpl<T>(\n    this: InternalProxySet<T>,\n    other: RSetLike<unknown> | Set<T>,\n  ): Set<unknown> {\n    this.epoch // touch property for tracking\n    const otherSet = proxySet(asIterable(other))\n    const result = proxySet<unknown>()\n    for (const v of this.values()) if (!otherSet.has(v)) result.add(v)\n    // (v as T) -> v is unknown from RSetLike<unknown>, but has() accepts T and safely handles type mismatches\n    for (const v of otherSet.values()) if (!this.has(v as T)) result.add(v)\n    return proxySet(result)\n  }\n\n  const vObject: InternalProxySet<T> = {\n    data: initialData,\n    index: initialIndex,\n    epoch: 0,\n    get size() {\n      if (!isProxy(this)) {\n        registerSnapMap()\n      }\n      return indexMap.size\n    },\n    has(value: T) {\n      const map = getMapForThis(this)\n      const v = maybeProxify(value)\n      this.epoch // touch property for tracking\n      return map.has(v)\n    },\n    add(value: T) {\n      if (!isProxy(this)) {\n        throw new Error('Cannot perform mutations on a snapshot')\n      }\n      const v = maybeProxify(value)\n      if (!indexMap.has(v)) {\n        indexMap.set(v, this.index)\n        this.data[this.index++] = v\n        this.epoch++\n      }\n      return this\n    },\n    delete(value: T) {\n      if (!isProxy(this)) {\n        throw new Error('Cannot perform mutations on a snapshot')\n      }\n      const v = maybeProxify(value)\n      const index = indexMap.get(v)\n      if (index === undefined) {\n        return false\n      }\n      delete this.data[index]\n      indexMap.delete(v)\n      this.epoch++\n      return true\n    },\n    clear() {\n      if (!isProxy(this)) {\n        throw new Error('Cannot perform mutations on a snapshot')\n      }\n      this.data.length = 0 // empty array\n      this.index = 0\n      this.epoch++\n      indexMap.clear()\n    },\n    forEach(cb: (value: T, valueAgain: T, set: Set<T>) => void) {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      map.forEach((index) => {\n        cb(this.data[index]!, this.data[index]!, this)\n      })\n    },\n    *values(): SetIterator<T> {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      for (const index of map.values()) {\n        yield this.data[index]!\n      }\n    },\n    keys(): SetIterator<T> {\n      this.epoch // touch property for tracking\n      return this.values()\n    },\n    *entries(): SetIterator<[T, T]> {\n      this.epoch // touch property for tracking\n      const map = getMapForThis(this)\n      for (const index of map.values()) {\n        const value = this.data[index]!\n        yield [value, value]\n      }\n    },\n    toJSON(): Set<T> {\n      return new Set(this.values())\n    },\n    [Symbol.iterator]() {\n      return this.values()\n    },\n    get [Symbol.toStringTag]() {\n      return 'Set'\n    },\n    intersection: intersectionImpl,\n    union: unionImpl,\n    difference: differenceImpl,\n    symmetricDifference: symmetricDifferenceImpl,\n    isSubsetOf(other: RSetLike<T>) {\n      this.epoch // touch property for tracking\n      for (const v of this.values()) if (!other.has(v)) return false\n      return true\n    },\n    isSupersetOf(other: RSetLike<T>) {\n      this.epoch // touch property for tracking\n      const it = asIterable(other)\n      for (const v of it) if (!this.has(v)) return false\n      return true\n    },\n    isDisjointFrom(other: RSetLike<T>) {\n      this.epoch // touch property for tracking\n      for (const v of this.values()) if (other.has(v)) return false\n      return true\n    },\n  }\n\n  const proxiedObject = proxy(vObject)\n  Object.defineProperties(proxiedObject, {\n    size: { enumerable: false },\n    data: { enumerable: false },\n    index: { enumerable: false },\n    epoch: { enumerable: false },\n    toJSON: { enumerable: false },\n  })\n  Object.seal(proxiedObject)\n\n  return proxiedObject as InternalProxySet<T> & {\n    $$valtioSnapshot: Omit<InternalProxySet<T>, 'set' | 'delete' | 'clear'>\n  }\n}\n"
  },
  {
    "path": "src/vanilla/utils/subscribeKey.ts",
    "content": "import { subscribe } from '../../vanilla.ts'\n\n/**\n * subscribeKey\n *\n * The subscribeKey utility enables subscription to a primitive subproperty of a given state proxy.\n * Subscriptions created with subscribeKey will only fire when the specified property changes.\n * notifyInSync: same as the parameter to subscribe(); true disables batching of subscriptions.\n *\n * @example\n * import { subscribeKey } from 'valtio/utils'\n * subscribeKey(state, 'count', (v) => console.log('state.count has changed to', v))\n */\nexport function subscribeKey<T extends object, K extends keyof T>(\n  proxyObject: T,\n  key: K,\n  callback: (value: T[K]) => void,\n  notifyInSync?: boolean,\n): () => void {\n  let prevValue = proxyObject[key]\n  return subscribe(\n    proxyObject,\n    () => {\n      const nextValue = proxyObject[key]\n      if (!Object.is(prevValue, nextValue)) {\n        callback((prevValue = nextValue))\n      }\n    },\n    notifyInSync,\n  )\n}\n"
  },
  {
    "path": "src/vanilla/utils/watch.ts",
    "content": "import { subscribe } from '../../vanilla.ts'\n\ntype Cleanup = () => void\ntype WatchGet = <T extends object>(proxyObject: T) => T\ntype WatchCallback = (\n  get: WatchGet,\n) => Cleanup | void | Promise<Cleanup | void> | undefined\ntype WatchOptions = {\n  sync?: boolean\n}\n\nlet currentCleanups: Set<Cleanup> | undefined\n\nlet didWarnDeprecation = false\n\n/**\n * watch\n *\n * @deprecated This util is no longer maintained. Please migrate to [valtio-reactive](https://github.com/valtiojs/valtio-reactive).\n *\n * Creates a reactive effect that automatically tracks proxy objects and\n * reevaluates everytime one of the tracked proxy objects updates. It returns\n * a cleanup function to stop the reactive effect from reevaluating.\n *\n * Callback is invoked immediately to detect the tracked objects.\n *\n * Callback passed to `watch` receives a `get` function that \"tracks\" the\n * passed proxy object.\n *\n * Watch callbacks may return an optional cleanup function, which is evaluated\n * whenever the callback reevaluates or when the cleanup function returned by\n * `watch` is evaluated.\n *\n * `watch` calls inside `watch` are also automatically tracked and cleaned up\n * whenever the parent `watch` reevaluates.\n */\nexport function watch(\n  callback: WatchCallback,\n  options?: WatchOptions,\n): Cleanup {\n  if (import.meta.env?.MODE !== 'production' && !didWarnDeprecation) {\n    console.warn(\n      '[DEPRECATED] The `watch` util is no longer maintained. Please migrate to [valtio-reactive](https://github.com/valtiojs/valtio-reactive).',\n    )\n    didWarnDeprecation = true\n  }\n\n  let alive = true\n  const cleanups = new Set<Cleanup>()\n\n  type ProxyObject = object\n  type Unsubscribe = () => void\n  const subscriptions = new Map<ProxyObject, Unsubscribe>()\n\n  const cleanup = () => {\n    if (alive) {\n      alive = false\n      cleanups.forEach((clean) => clean())\n      cleanups.clear()\n      subscriptions.forEach((unsubscribe) => unsubscribe())\n      subscriptions.clear()\n    }\n  }\n\n  const revalidate = async () => {\n    if (!alive) {\n      return\n    }\n\n    // run own cleanups before re-subscribing\n    cleanups.forEach((clean) => clean())\n    cleanups.clear()\n\n    const proxiesToSubscribe = new Set<ProxyObject>()\n\n    // Setup watch context, this allows us to automatically capture\n    // watch cleanups if the watch callback itself has watch calls.\n    const parent = currentCleanups\n    currentCleanups = cleanups\n\n    // Ensures that the parent is reset if the callback throws an error.\n    try {\n      const promiseOrPossibleCleanup = callback((proxyObject) => {\n        proxiesToSubscribe.add(proxyObject)\n        // in case the callback is a promise and the watch has ended\n        if (alive && !subscriptions.has(proxyObject)) {\n          // subscribe to new proxy immediately -> this fixes problems when Promises are used due to the callstack\n          const unsubscribe = subscribe(proxyObject, revalidate, options?.sync)\n          subscriptions.set(proxyObject, unsubscribe)\n        }\n        return proxyObject\n      })\n      const couldBeCleanup =\n        promiseOrPossibleCleanup && promiseOrPossibleCleanup instanceof Promise\n          ? await promiseOrPossibleCleanup\n          : promiseOrPossibleCleanup\n\n      // If there's a cleanup, we add this to the cleanups set\n      if (couldBeCleanup) {\n        if (alive) {\n          cleanups.add(couldBeCleanup)\n        } else {\n          cleanup()\n        }\n      }\n    } finally {\n      currentCleanups = parent\n    }\n\n    // Unsubscribe old subscriptions\n    subscriptions.forEach((unsubscribe, proxyObject) => {\n      if (!proxiesToSubscribe.has(proxyObject)) {\n        subscriptions.delete(proxyObject)\n        unsubscribe()\n      }\n    })\n  }\n\n  // If there's a parent watch call, we attach this watch's\n  // cleanup to the parent.\n  if (currentCleanups) {\n    currentCleanups.add(cleanup)\n  }\n\n  // Invoke effect to create subscription list\n  revalidate()\n\n  return cleanup\n}\n"
  },
  {
    "path": "src/vanilla/utils.ts",
    "content": "export { subscribeKey } from './utils/subscribeKey.ts'\nexport { watch } from './utils/watch.ts'\nexport { devtools } from './utils/devtools.ts'\nexport { deepClone } from './utils/deepClone.ts'\nexport { unstable_deepProxy } from './utils/deepProxy.ts'\nexport { proxySet, isProxySet } from './utils/proxySet.ts'\nexport { proxyMap, isProxyMap } from './utils/proxyMap.ts'\n"
  },
  {
    "path": "src/vanilla.ts",
    "content": "import { getUntracked, markToTrack } from 'proxy-compare'\n\nconst isObject = (x: unknown): x is object =>\n  typeof x === 'object' && x !== null\n\n/** Function type for any kind of function */\ntype AnyFunction = (...args: any[]) => any\n\n/** Object that can be proxied */\ntype ProxyObject = object\n\n/** Property access path as an array of property names/symbols */\ntype Path = (string | symbol)[]\n\n/**\n * Operation performed on a proxy object\n * - 'set': A property was set to a new value\n * - 'delete': A property was deleted\n */\ntype Op =\n  | [op: 'set', path: Path, value: unknown, prevValue: unknown]\n  | [op: 'delete', path: Path, prevValue: unknown]\n\n/** Function called when a proxy object changes */\ntype Listener = (op: Op | undefined, nextVersion: number) => void\n\nexport type INTERNAL_Op = Op\n\n/** JavaScript primitive types */\ntype Primitive = string | number | boolean | null | undefined | symbol | bigint\n\n/** Types that should not be proxied in snapshots */\ntype SnapshotIgnore =\n  | Date\n  | Map<any, any>\n  | Set<any>\n  | WeakMap<any, any>\n  | WeakSet<any>\n  | Error\n  | RegExp\n  | AnyFunction\n  | Primitive\n\n/**\n * Snapshot type that converts objects to readonly versions recursively\n */\nexport type Snapshot<T> = T extends { $$valtioSnapshot: infer S }\n  ? S\n  : T extends SnapshotIgnore\n    ? T\n    : T extends object\n      ? { readonly [K in keyof T]: Snapshot<T[K]> }\n      : T\n\ntype RemoveListener = () => void\ntype AddListener = (listener: Listener) => RemoveListener\n\ntype ProxyState = readonly [\n  target: object,\n  ensureVersion: (nextCheckVersion?: number) => number,\n  addListener: AddListener,\n]\n\nconst canProxyDefault = (x: unknown): boolean =>\n  isObject(x) &&\n  !refSet.has(x) &&\n  (Array.isArray(x) || !(Symbol.iterator in x)) &&\n  !(x instanceof WeakMap) &&\n  !(x instanceof WeakSet) &&\n  !(x instanceof Error) &&\n  !(x instanceof Number) &&\n  !(x instanceof Date) &&\n  !(x instanceof String) &&\n  !(x instanceof RegExp) &&\n  !(x instanceof ArrayBuffer) &&\n  !(x instanceof Promise)\n\nconst createSnapshotDefault = <T extends object>(\n  target: T,\n  version: number,\n): T => {\n  const cache = snapCache.get(target)\n  if (cache?.[0] === version) {\n    return cache[1] as T\n  }\n  const snap: any = Array.isArray(target)\n    ? []\n    : Object.create(Object.getPrototypeOf(target))\n  markToTrack(snap, true) // mark to track\n  snapCache.set(target, [version, snap])\n  Reflect.ownKeys(target).forEach((key) => {\n    if (Object.getOwnPropertyDescriptor(snap, key)) {\n      // Only the known case is Array.length so far.\n      return\n    }\n    const value = Reflect.get(target, key)\n    const { enumerable } = Reflect.getOwnPropertyDescriptor(\n      target,\n      key,\n    ) as PropertyDescriptor\n    const desc: PropertyDescriptor = {\n      value,\n      enumerable: enumerable as boolean,\n      // This is intentional to avoid copying with proxy-compare.\n      // It's still non-writable, so it avoids assigning a value.\n      configurable: true,\n    }\n    if (refSet.has(value as object)) {\n      markToTrack(value as object, false) // mark not to track\n    } else if (proxyStateMap.has(value as object)) {\n      const [target, ensureVersion] = proxyStateMap.get(\n        value as object,\n      ) as ProxyState\n      desc.value = createSnapshotDefault(target, ensureVersion()) as Snapshot<T>\n    }\n    Object.defineProperty(snap, key, desc)\n  })\n  return Object.preventExtensions(snap)\n}\n\nconst createHandlerDefault = <T extends object>(\n  isInitializing: () => boolean,\n  addPropListener: (prop: string | symbol, propValue: unknown) => void,\n  removePropListener: (prop: string | symbol) => void,\n  notifyUpdate: (op: Op | undefined) => void,\n): ProxyHandler<T> => ({\n  deleteProperty(target: T, prop: string | symbol) {\n    const prevValue = Reflect.get(target, prop)\n    removePropListener(prop)\n    const deleted = Reflect.deleteProperty(target, prop)\n    if (deleted) {\n      notifyUpdate(createOp?.('delete', prop, prevValue))\n    }\n    return deleted\n  },\n  set(target: T, prop: string | symbol, value: any, receiver: object) {\n    const hasPrevValue = !isInitializing() && Reflect.has(target, prop)\n    const prevValue = Reflect.get(target, prop, receiver)\n    if (\n      hasPrevValue &&\n      (objectIs(prevValue, value) ||\n        (proxyCache.has(value) && objectIs(prevValue, proxyCache.get(value))))\n    ) {\n      return true\n    }\n    removePropListener(prop)\n    if (isObject(value)) {\n      value = getUntracked(value) || value\n    }\n    const nextValue =\n      !proxyStateMap.has(value) && canProxy(value) ? proxy(value) : value\n    addPropListener(prop, nextValue)\n    Reflect.set(target, prop, nextValue, receiver)\n    notifyUpdate(createOp?.('set', prop, value, prevValue))\n    return true\n  },\n})\n\nconst createOpDefault = (\n  type: 'set' | 'delete',\n  prop: symbol | string,\n  ...args: unknown[]\n) => [type, [prop], ...args] as Op\n\n// internal states\nconst proxyStateMap: WeakMap<ProxyObject, ProxyState> = new WeakMap()\nconst refSet: WeakSet<object> = new WeakSet()\nconst snapCache: WeakMap<object, [version: number, snap: unknown]> =\n  new WeakMap()\nconst versionHolder = [1] as [number]\nconst proxyCache: WeakMap<object, ProxyObject> = new WeakMap()\n\n// internal functions\nlet objectIs: (a: unknown, b: unknown) => boolean = Object.is\nlet newProxy = <T extends object>(target: T, handler: ProxyHandler<T>): T =>\n  new Proxy(target, handler)\nlet canProxy: typeof canProxyDefault = canProxyDefault\nlet createSnapshot: typeof createSnapshotDefault = createSnapshotDefault\nlet createHandler: typeof createHandlerDefault = createHandlerDefault\nlet createOp: typeof createOpDefault | undefined\n\n/**\n * Creates a reactive proxy object that can be tracked for changes\n */\nexport function proxy<T extends object>(baseObject: T = {} as T): T {\n  if (!isObject(baseObject)) {\n    throw new Error('object required')\n  }\n  const found = proxyCache.get(baseObject) as T | undefined\n  if (found) {\n    return found\n  }\n  let version = versionHolder[0]\n  const listeners = new Set<Listener>()\n  const notifyUpdate = (\n    op: Op | undefined,\n    nextVersion = ++versionHolder[0],\n  ) => {\n    if (version !== nextVersion) {\n      checkVersion = version = nextVersion\n      listeners.forEach((listener) => listener(op, nextVersion))\n    }\n  }\n  let checkVersion = version\n  const ensureVersion = (nextCheckVersion = versionHolder[0]) => {\n    if (checkVersion !== nextCheckVersion) {\n      checkVersion = nextCheckVersion\n      propProxyStates.forEach(([propProxyState]) => {\n        const propVersion = propProxyState[1](nextCheckVersion)\n        if (propVersion > version) {\n          version = propVersion\n        }\n      })\n    }\n    return version\n  }\n  const createPropListener =\n    (prop: string | symbol): Listener =>\n    (op, nextVersion) => {\n      let newOp: Op | undefined\n      if (op) {\n        newOp = [...op]\n        newOp[1] = [prop, ...(newOp[1] as Path)]\n      }\n      notifyUpdate(newOp, nextVersion)\n    }\n  const propProxyStates = new Map<\n    string | symbol,\n    readonly [ProxyState, RemoveListener?]\n  >()\n  const addPropListener = (prop: string | symbol, propValue: unknown) => {\n    const propProxyState =\n      !refSet.has(propValue as object) && proxyStateMap.get(propValue as object)\n    if (propProxyState) {\n      if (import.meta.env?.MODE !== 'production' && propProxyStates.has(prop)) {\n        throw new Error('prop listener already exists')\n      }\n      if (listeners.size) {\n        const remove = propProxyState[2](createPropListener(prop))\n        propProxyStates.set(prop, [propProxyState, remove])\n      } else {\n        propProxyStates.set(prop, [propProxyState])\n      }\n    }\n  }\n  const removePropListener = (prop: string | symbol) => {\n    const entry = propProxyStates.get(prop)\n    if (entry) {\n      propProxyStates.delete(prop)\n      entry[1]?.()\n    }\n  }\n  const addListener = (listener: Listener) => {\n    listeners.add(listener)\n    if (listeners.size === 1) {\n      propProxyStates.forEach(([propProxyState, prevRemove], prop) => {\n        if (import.meta.env?.MODE !== 'production' && prevRemove) {\n          throw new Error('remove already exists')\n        }\n        const remove = propProxyState[2](createPropListener(prop))\n        propProxyStates.set(prop, [propProxyState, remove])\n      })\n    }\n    const removeListener = () => {\n      listeners.delete(listener)\n      if (listeners.size === 0) {\n        propProxyStates.forEach(([propProxyState, remove], prop) => {\n          if (remove) {\n            remove()\n            propProxyStates.set(prop, [propProxyState])\n          }\n        })\n      }\n    }\n    return removeListener\n  }\n  let initializing = true\n  const handler = createHandler<T>(\n    () => initializing,\n    addPropListener,\n    removePropListener,\n    notifyUpdate,\n  )\n  const proxyObject = newProxy(baseObject, handler)\n  proxyCache.set(baseObject, proxyObject)\n  const proxyState: ProxyState = [baseObject, ensureVersion, addListener]\n  proxyStateMap.set(proxyObject, proxyState)\n  Reflect.ownKeys(baseObject).forEach((key) => {\n    const desc = Object.getOwnPropertyDescriptor(\n      baseObject,\n      key,\n    ) as PropertyDescriptor\n    if ('value' in desc && desc.writable) {\n      proxyObject[key as keyof T] = baseObject[key as keyof T]\n    }\n  })\n  initializing = false\n  return proxyObject\n}\n\n/**\n * Gets the current version number of a proxy object\n */\nexport function getVersion(proxyObject: unknown): number | undefined {\n  const proxyState = proxyStateMap.get(proxyObject as object)\n  return proxyState?.[1]()\n}\n\n/**\n * Subscribes to changes in a proxy object\n */\nexport function subscribe<T extends object>(\n  proxyObject: T,\n  callback: (unstable_ops: Op[]) => void,\n  notifyInSync?: boolean,\n): () => void {\n  const proxyState = proxyStateMap.get(proxyObject as object)\n  if (import.meta.env?.MODE !== 'production' && !proxyState) {\n    console.warn('Please use proxy object')\n  }\n  let promise: Promise<void> | undefined\n  const ops: Op[] = []\n  const addListener = (proxyState as ProxyState)[2]\n  let isListenerActive = false\n  const listener: Listener = (op) => {\n    if (op) {\n      ops.push(op)\n    }\n    if (notifyInSync) {\n      callback(ops.splice(0))\n      return\n    }\n    if (!promise) {\n      promise = Promise.resolve().then(() => {\n        promise = undefined\n        if (isListenerActive) {\n          callback(ops.splice(0))\n        }\n      })\n    }\n  }\n  const removeListener = addListener(listener)\n  isListenerActive = true\n  return () => {\n    isListenerActive = false\n    removeListener()\n  }\n}\n\n/**\n * Creates an immutable snapshot of the current state of a proxy object\n */\nexport function snapshot<T extends object>(proxyObject: T): Snapshot<T> {\n  const proxyState = proxyStateMap.get(proxyObject as object)\n  if (import.meta.env?.MODE !== 'production' && !proxyState) {\n    console.warn('Please use proxy object')\n  }\n  const [target, ensureVersion] = proxyState as ProxyState\n  return createSnapshot(target, ensureVersion()) as Snapshot<T>\n}\n\n/**\n * Marks an object to be excluded from proxying\n *\n * Objects marked with ref will be kept as references in snapshots\n * instead of being deeply copied.\n */\nexport function ref<T extends object>(obj: T) {\n  refSet.add(obj)\n  return obj as T & { $$valtioSnapshot: T }\n}\n\n// ------------------------------------------------\n// unstable APIs (subject to change without notice)\n// ------------------------------------------------\n\nexport function unstable_getInternalStates(): {\n  proxyStateMap: typeof proxyStateMap\n  refSet: typeof refSet\n  snapCache: typeof snapCache\n  versionHolder: typeof versionHolder\n  proxyCache: typeof proxyCache\n} {\n  return {\n    proxyStateMap,\n    refSet,\n    snapCache,\n    versionHolder,\n    proxyCache,\n  }\n}\n\nexport function unstable_replaceInternalFunction(\n  name: 'objectIs',\n  fn: (prev: typeof objectIs) => typeof objectIs,\n): void\n\nexport function unstable_replaceInternalFunction(\n  name: 'newProxy',\n  fn: (prev: typeof newProxy) => typeof newProxy,\n): void\n\nexport function unstable_replaceInternalFunction(\n  name: 'canProxy',\n  fn: (prev: typeof canProxy) => typeof canProxy,\n): void\n\nexport function unstable_replaceInternalFunction(\n  name: 'createSnapshot',\n  fn: (prev: typeof createSnapshot) => typeof createSnapshot,\n): void\n\nexport function unstable_replaceInternalFunction(\n  name: 'createHandler',\n  fn: (prev: typeof createHandler) => typeof createHandler,\n): void\n\nexport function unstable_replaceInternalFunction(\n  name:\n    | 'objectIs'\n    | 'newProxy'\n    | 'canProxy'\n    | 'createSnapshot'\n    | 'createHandler',\n  fn: (prev: any) => any,\n) {\n  switch (name) {\n    case 'objectIs':\n      objectIs = fn(objectIs)\n      break\n    case 'newProxy':\n      newProxy = fn(newProxy)\n      break\n    case 'canProxy':\n      canProxy = fn(canProxy)\n      break\n    case 'createSnapshot':\n      createSnapshot = fn(createSnapshot)\n      break\n    case 'createHandler':\n      createHandler = fn(createHandler)\n      break\n    default:\n      throw new Error('unknown function')\n  }\n}\n\nexport function unstable_enableOp(\n  enabled: boolean | typeof createOpDefault = true,\n): void {\n  if (enabled === true) {\n    createOp = createOpDefault\n  } else if (enabled === false) {\n    createOp = undefined\n  } else {\n    createOp = enabled\n  }\n}\n"
  },
  {
    "path": "tests/async.test.tsx",
    "content": "/// <reference types=\"react/canary\" />\n\nimport ReactExports, { StrictMode, Suspense } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, useSnapshot } from 'valtio'\nimport { sleep } from './utils'\n\nconst { use } = ReactExports\nconst use2 = <T,>(x: T): Awaited<T> =>\n  x instanceof Promise ? use(x) : (x as Awaited<T>)\n\ndescribe('async', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it.skipIf(typeof use === 'undefined')('delayed increment', async () => {\n    const state = proxy<any>({ count: 0 })\n    const delayedIncrement = () => {\n      const nextCount = state.count + 1\n      state.count = sleep(300).then(() => nextCount)\n    }\n\n    const Counter = () => {\n      const snap = useSnapshot(state)\n      return (\n        <>\n          <div>count: {use2(snap.count)}</div>\n          <button onClick={delayedIncrement}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(300))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it.skipIf(typeof use === 'undefined')('delayed object', async () => {\n    const state = proxy<any>({ object: { text: 'none' } })\n    const delayedObject = () => {\n      state.object = sleep(300).then(() => ({ text: 'hello' }))\n    }\n\n    const Counter = () => {\n      const snap = useSnapshot(state)\n      return (\n        <>\n          <div>text: {use2(snap.object).text}</div>\n          <button onClick={delayedObject}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('text: none')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(300))\n    expect(screen.getByText('text: hello')).toBeInTheDocument()\n  })\n\n  it.skipIf(typeof use === 'undefined')(\n    'delayed object update fulfilled',\n    async () => {\n      const state = proxy<any>({\n        object: sleep(300).then(() => ({ text: 'counter', count: 0 })),\n      })\n      const updateObject = () => {\n        state.object = state.object.then((v: any) =>\n          sleep(300).then(() => ({ ...v, count: v.count + 1 })),\n        )\n      }\n\n      const Counter = () => {\n        const snap = useSnapshot(state)\n        return (\n          <>\n            <div>text: {use2(snap.object).text}</div>\n            <div>count: {use2(snap.object).count}</div>\n            <button onClick={updateObject}>button</button>\n          </>\n        )\n      }\n\n      // eslint-disable-next-line testing-library/no-unnecessary-act\n      await act(() =>\n        render(\n          <StrictMode>\n            <Suspense fallback=\"loading\">\n              <Counter />\n            </Suspense>\n          </StrictMode>,\n        ),\n      )\n\n      expect(screen.getByText('loading')).toBeInTheDocument()\n      await act(() => vi.advanceTimersByTimeAsync(300))\n      expect(screen.getByText('text: counter')).toBeInTheDocument()\n      expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('button'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('loading')).toBeInTheDocument()\n      await act(() => vi.advanceTimersByTimeAsync(300))\n      expect(screen.getByText('text: counter')).toBeInTheDocument()\n      expect(screen.getByText('count: 1')).toBeInTheDocument()\n    },\n  )\n\n  it.skipIf(typeof use === 'undefined')('delayed falsy value', async () => {\n    const state = proxy<any>({ value: true })\n    const delayedValue = () => {\n      state.value = sleep(300).then(() => null)\n    }\n\n    const Counter = () => {\n      const snap = useSnapshot(state)\n      return (\n        <>\n          <div>value: {String(use2(snap.value))}</div>\n          <button onClick={delayedValue}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('value: true')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(300))\n    expect(screen.getByText('value: null')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/basic.test.tsx",
    "content": "import { StrictMode, useEffect, useLayoutEffect, useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, snapshot, useSnapshot } from 'valtio'\nimport { useCommitCount } from './utils'\n\ndescribe('basic', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('simple counter', async () => {\n    const obj = proxy({ count: 0 })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const { unmount } = render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    unmount()\n  })\n\n  it('no extra re-renders (commits)', async () => {\n    const obj = proxy({ count: 0, count2: 0 })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count: {snap.count} ({useCommitCount(1)})\n          </div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const Counter2 = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count2: {snap.count2} ({useCommitCount(1)})\n          </div>\n          <button onClick={() => ++obj.count2}>button2</button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n        <Counter2 />\n      </>,\n    )\n\n    expect(screen.getByText('count: 0 (1)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0 (1)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (2)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0 (1)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (2)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 1 (2)')).toBeInTheDocument()\n  })\n\n  it('no extra re-renders (render func calls in non strict mode)', async () => {\n    const obj = proxy({ count: 0, count2: 0 })\n\n    const renderFn = vi.fn()\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      renderFn(snap.count)\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const renderFn2 = vi.fn()\n    const Counter2 = () => {\n      const snap = useSnapshot(obj)\n      renderFn2(snap.count2)\n      return (\n        <>\n          <div>count2: {snap.count2}</div>\n          <button onClick={() => ++obj.count2}>button2</button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n        <Counter2 />\n      </>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(1)\n    expect(renderFn).lastCalledWith(0)\n    expect(renderFn2).toBeCalledTimes(1)\n    expect(renderFn2).lastCalledWith(0)\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(2)\n    expect(renderFn).lastCalledWith(1)\n    expect(renderFn2).toBeCalledTimes(1)\n    expect(renderFn2).lastCalledWith(0)\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(screen.getByText('count2: 1')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(2)\n    expect(renderFn).lastCalledWith(1)\n    expect(renderFn2).toBeCalledTimes(2)\n    expect(renderFn2).lastCalledWith(1)\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(screen.getByText('count2: 2')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(2)\n    expect(renderFn).lastCalledWith(1)\n    expect(renderFn2).toBeCalledTimes(3)\n    expect(renderFn2).lastCalledWith(2)\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n    expect(screen.getByText('count2: 2')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(3)\n    expect(renderFn).lastCalledWith(2)\n    expect(renderFn2).toBeCalledTimes(3)\n    expect(renderFn2).lastCalledWith(2)\n  })\n\n  it('object in object', async () => {\n    const obj = proxy({ object: { count: 0 } })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.object.count}</div>\n          <button onClick={() => ++obj.object.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('array in object', async () => {\n    const obj = proxy({ counts: [0, 1, 2] })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>counts: {snap.counts.join(',')}</div>\n          <button onClick={() => obj.counts.push(obj.counts.length)}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('counts: 0,1,2')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('counts: 0,1,2,3')).toBeInTheDocument()\n  })\n\n  it('array pop and splice', async () => {\n    const arr = proxy([0, 1, 2])\n\n    const Counter = () => {\n      const snap = useSnapshot(arr)\n      return (\n        <>\n          <div>counts: {snap.join(',')}</div>\n          <button onClick={() => arr.pop()}>button</button>\n          <button onClick={() => arr.splice(1, 0, 10, 11)}>button2</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('counts: 0,1,2')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('counts: 0,1')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('counts: 0,10,11,1')).toBeInTheDocument()\n  })\n\n  it('array length after direct assignment', async () => {\n    const obj = proxy({ counts: [0, 1, 2] })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>counts: {snap.counts.join(',')}</div>\n          <div>length: {snap.counts.length}</div>\n          <button\n            onClick={() => (obj.counts[obj.counts.length] = obj.counts.length)}\n          >\n            increment\n          </button>\n          <button\n            onClick={() =>\n              (obj.counts[obj.counts.length + 5] = obj.counts.length + 5)\n            }\n          >\n            jump\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('counts: 0,1,2')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('increment'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('counts: 0,1,2,3')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('jump'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('counts: 0,1,2,3,,,,,,9')).toBeInTheDocument()\n  })\n\n  it('deleting property', async () => {\n    const obj = proxy<{ count?: number }>({ count: 1 })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.count ?? 'none'}</div>\n          <button onClick={() => delete obj.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: none')).toBeInTheDocument()\n  })\n\n  it('circular object', async () => {\n    const obj = proxy<any>({ object: {} })\n    obj.object = obj\n    obj.object.count = 0\n\n    const Counter = () => {\n      const snap = useSnapshot(obj) as any\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('circular object with non-proxy object (#375)', async () => {\n    const initialObject = { count: 0 }\n    const state: any = proxy(initialObject)\n    state.obj = initialObject\n\n    const Counter = () => {\n      const snap = useSnapshot(state)\n      return <div>count: {snap.obj ? 1 : snap.count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('render from outside', async () => {\n    const obj = proxy({ count: 0, anotherCount: 0 })\n\n    const Counter = () => {\n      const [show, setShow] = useState(false)\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          {show ? (\n            <div>count: {snap.count}</div>\n          ) : (\n            <div>anotherCount: {snap.anotherCount}</div>\n          )}\n          <button onClick={() => ++obj.count}>button</button>\n          <button onClick={() => setShow((x) => !x)}>toggle</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('anotherCount: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    fireEvent.click(screen.getByText('toggle'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('should not commit stale value for newly accessed keys on local rerender (regression in #1176)', async () => {\n    const obj = proxy({ count: 0, anotherValue: 0 })\n\n    const commitFn = vi.fn()\n    const Component = () => {\n      const [showAnotherValue, setShowAnotherValue] = useState(false)\n      const snap = useSnapshot(obj)\n      const value = showAnotherValue ? snap.anotherValue : 'hidden'\n      useLayoutEffect(() => {\n        commitFn(value)\n      }, [value])\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          {showAnotherValue && <div>anotherValue: {snap.anotherValue}</div>}\n          <button onClick={() => setShowAnotherValue(true)}>\n            showAnotherValue\n          </button>\n        </>\n      )\n    }\n\n    render(<Component />)\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    expect(screen.queryByText('anotherValue: 0')).not.toBeInTheDocument()\n    expect(screen.queryByText('anotherValue: 1')).not.toBeInTheDocument()\n    expect(commitFn).toBeCalledTimes(1)\n    expect(commitFn).toHaveBeenNthCalledWith(1, 'hidden')\n\n    obj.anotherValue += 1\n\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(commitFn).toBeCalledTimes(1)\n\n    fireEvent.click(screen.getByText('showAnotherValue'))\n\n    expect(screen.getByText('anotherValue: 1')).toBeInTheDocument()\n    expect(commitFn.mock.calls.map(([value]) => value)).toEqual(['hidden', 1])\n  })\n\n  it('counter with sync option', async () => {\n    const obj = proxy({ count: 0 })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj, { sync: true })\n      return (\n        <>\n          <div>\n            count: {snap.count} ({useCommitCount(1)})\n          </div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n      </>,\n    )\n\n    expect(screen.getByText('count: 0 (1)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (2)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 2 (3)')).toBeInTheDocument()\n  })\n\n  it('support undefined property (#439)', async () => {\n    const obj = proxy({ prop: undefined })\n\n    expect('prop' in obj).toBe(true)\n\n    const Component = () => {\n      const snap = useSnapshot(obj)\n      return <div>has prop: {JSON.stringify('prop' in snap)}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Component />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('has prop: true')).toBeInTheDocument()\n  })\n\n  it('sync snapshot between nested components (#460)', async () => {\n    const obj = proxy<{\n      id: 'prop1' | 'prop2'\n      prop1: string\n      prop2?: string\n    }>({ id: 'prop1', prop1: 'value1' })\n\n    const Child = ({ id }: { id: 'prop1' | 'prop2' }) => {\n      const snap = useSnapshot(obj)\n      return <div>Child: {snap[id]}</div>\n    }\n\n    const handleClick = () => {\n      obj.prop2 = 'value2'\n      obj.id = 'prop2'\n    }\n\n    const Parent = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>Parent: {snap[snap.id]}</div>\n          <Child id={snap.id} />\n          <button onClick={handleClick}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Parent />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('Parent: value1')).toBeInTheDocument()\n    expect(screen.getByText('Child: value1')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('Parent: value2')).toBeInTheDocument()\n    expect(screen.getByText('Child: value2')).toBeInTheDocument()\n  })\n\n  it('respects property enumerability (#726)', async () => {\n    const x = proxy(Object.defineProperty({ a: 1 }, 'b', { value: 2 }))\n\n    expect(Object.keys(snapshot(x))).toEqual(Object.keys(x))\n  })\n\n  it('stable snapshot object (#985)', async () => {\n    const state = proxy({ count: 0, obj: {} })\n\n    let effectCount = 0\n\n    const TestComponent = () => {\n      const { count, obj } = useSnapshot(state)\n      useEffect(() => {\n        ++effectCount\n      }, [obj])\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => ++state.count}>button</button>\n        </>\n      )\n    }\n\n    render(<TestComponent />)\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    expect(effectCount).toBe(1)\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(effectCount).toBe(1)\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n    expect(effectCount).toBe(1)\n  })\n})\n"
  },
  {
    "path": "tests/class.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, useSnapshot } from 'valtio'\nimport { useCommitCount } from './utils'\n\ndescribe('class', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('simple class without methods', async () => {\n    class CountClass {\n      public count: number\n      constructor() {\n        this.count = 0\n      }\n    }\n\n    const obj = proxy(new CountClass())\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('no extra re-renders with class', async () => {\n    class CountClass {\n      public count: number\n      public count2: number\n      constructor() {\n        this.count = 0\n        this.count2 = 0\n      }\n    }\n\n    const obj = proxy(new CountClass())\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count: {snap.count} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const Counter2 = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count2: {snap.count2} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count2}>button2</button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n        <Counter2 />\n      </>,\n    )\n\n    expect(screen.getByText('count: 0 (0)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0 (0)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (1)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0 (0)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (1)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 1 (1)')).toBeInTheDocument()\n  })\n\n  it('inherited class without methods', async () => {\n    class BaseClass {\n      public count: number\n      constructor() {\n        this.count = 0\n      }\n    }\n    class CountClass extends BaseClass {\n      public count2: number\n      constructor() {\n        super()\n        this.count2 = 0\n      }\n    }\n\n    const obj = proxy(new CountClass())\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('class with a method', async () => {\n    class CountClass {\n      public count: number\n      constructor() {\n        this.count = 0\n      }\n      public doubled() {\n        return this.count * 2\n      }\n    }\n\n    const obj = proxy(new CountClass())\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            doubled: {snap.doubled()} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const Counter2 = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <div>\n          count: {snap.count} ({useCommitCount()})\n        </div>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n        <Counter2 />\n      </>,\n    )\n\n    expect(screen.getByText('doubled: 0 (0)')).toBeInTheDocument()\n    expect(screen.getByText('count: 0 (0)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('doubled: 2 (1)')).toBeInTheDocument()\n    expect(screen.getByText('count: 1 (1)')).toBeInTheDocument()\n  })\n\n  it('inherited class with a method', async () => {\n    class BaseClass {\n      public count: number\n      constructor() {\n        this.count = 0\n      }\n      public doubled() {\n        return this.count * 2\n      }\n    }\n    class CountClass extends BaseClass {\n      public count2: number\n      constructor() {\n        super()\n        this.count2 = 0\n      }\n    }\n\n    const obj = proxy(new CountClass())\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            doubled: {snap.doubled()} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const Counter2 = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count2: {snap.count2} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count2}>button2</button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n        <Counter2 />\n      </>,\n    )\n\n    expect(screen.getByText('doubled: 0 (0)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0 (0)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('doubled: 2 (1)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 0 (0)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('doubled: 2 (1)')).toBeInTheDocument()\n    expect(screen.getByText('count2: 1 (1)')).toBeInTheDocument()\n  })\n\n  it('no extra re-renders with getters', async () => {\n    class CountClass {\n      public count: number\n      public count2: number\n      constructor() {\n        this.count = 0\n        this.count2 = 0\n      }\n      get count1() {\n        return this.count\n      }\n      get sum() {\n        return this.count + this.count2\n      }\n    }\n\n    const obj = proxy(new CountClass())\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count: {snap.count1} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    const Counter2 = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            sum: {snap.sum} ({useCommitCount()})\n          </div>\n          <button onClick={() => ++obj.count2}>button2</button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n        <Counter2 />\n      </>,\n    )\n\n    expect(screen.getByText('count: 0 (0)')).toBeInTheDocument()\n    expect(screen.getByText('sum: 0 (0)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (1)')).toBeInTheDocument()\n    expect(screen.getByText('sum: 1 (1)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button2'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1 (1)')).toBeInTheDocument()\n    expect(screen.getByText('sum: 2 (2)')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/deepClone.test.tsx",
    "content": "import { describe, expect, it } from 'vitest'\nimport { proxy } from 'valtio'\nimport { deepClone } from 'valtio/utils'\n\ndescribe('deepClone', () => {\n  // Basic data types\n  it('should handle primitive values', () => {\n    expect(deepClone(42)).toBe(42)\n    expect(deepClone('hello')).toBe('hello')\n    expect(deepClone(true)).toBe(true)\n    expect(deepClone(null)).toBe(null)\n    expect(deepClone(undefined)).toBe(undefined)\n  })\n\n  it('should clone plain objects', () => {\n    const original = { a: 1, b: 'string', c: true }\n    const cloned = deepClone(original)\n\n    expect(cloned).toEqual(original)\n    expect(cloned).not.toBe(original) // Different reference\n  })\n\n  it('should clone nested objects', () => {\n    const original = {\n      a: 1,\n      b: {\n        c: 'string',\n        d: {\n          e: true,\n        },\n      },\n    }\n    const cloned = deepClone(original)\n\n    expect(cloned).toEqual(original)\n    expect(cloned.b).not.toBe(original.b) // Different reference for nested objects\n    expect(cloned.b.d).not.toBe(original.b.d)\n  })\n\n  it('should clone arrays', () => {\n    const original = [1, 2, [3, 4, [5, 6]]]\n    const cloned = deepClone(original)\n\n    expect(cloned).toEqual(original)\n    expect(cloned).not.toBe(original)\n    expect(cloned[2]).not.toBe(original[2])\n  })\n\n  // Valtio specific tests\n  it('should clone proxy objects', () => {\n    const original = proxy({ a: 1, b: 2 })\n    const cloned = deepClone(original)\n\n    expect(cloned).toEqual(original)\n    expect(cloned).not.toBe(original)\n  })\n})\n"
  },
  {
    "path": "tests/deepProxy.test.tsx",
    "content": "import { describe, expect, it, vi } from 'vitest'\nimport { proxy } from 'valtio'\nimport {\n  unstable_deepProxy as deepProxy,\n  isProxyMap,\n  isProxySet,\n  proxyMap,\n  proxySet,\n} from 'valtio/utils'\n\ndescribe('deepProxy – core behavior', () => {\n  it('should properly clone a proxySet', () => {\n    const original = proxySet<number>([1, 2, 3])\n    const cloned = deepProxy(original)\n\n    expect([...cloned]).toEqual([...original])\n    expect(cloned).not.toBe(original)\n    expect(isProxySet(cloned)).toBe(true)\n  })\n\n  it('should maintain proxySet reactivity', () => {\n    const state = proxy({\n      count: 0,\n      set: proxySet<number>([1, 2, 3]),\n    })\n\n    const cloned = deepProxy(state)\n\n    cloned.set.add(4)\n    expect([...cloned.set]).toContain(4)\n    expect(() => cloned.set.add(5)).not.toThrow()\n  })\n\n  it('should properly clone a proxyMap', () => {\n    const original = proxyMap<string, number>([\n      ['a', 1],\n      ['b', 2],\n      ['c', 3],\n    ])\n    const cloned = deepProxy(original)\n\n    expect([...cloned.entries()]).toEqual([...original.entries()])\n    expect(cloned).not.toBe(original)\n    expect(isProxyMap(cloned)).toBe(true)\n  })\n\n  it('should maintain proxyMap reactivity', () => {\n    const state = proxy({\n      count: 0,\n      map: proxyMap<string, number>([\n        ['a', 1],\n        ['b', 2],\n      ]),\n    })\n\n    const cloned = deepProxy(state)\n\n    cloned.map.set('c', 3)\n    expect(cloned.map.get('c')).toBe(3)\n    expect(() => cloned.map.set('d', 4)).not.toThrow()\n  })\n\n  // Complex object with both proxySet and proxyMap\n  it('should handle complex objects with both proxySet and proxyMap', () => {\n    const original = proxy({\n      name: 'test',\n      count: 42,\n      set: proxySet<number>([1, 2, 3]),\n      map: proxyMap<string, any>([\n        ['a', 1],\n        ['b', { nested: true }],\n        ['c', proxySet<string>(['x', 'y', 'z'])],\n      ]),\n      nested: {\n        anotherSet: proxySet<string>(['a', 'b', 'c']),\n      },\n    })\n\n    const cloned = deepProxy(original)\n\n    expect(cloned.name).toBe('test')\n    expect(cloned.count).toBe(42)\n\n    // proxySet\n    expect([...cloned.set]).toEqual([1, 2, 3])\n\n    // proxyMap\n    expect(cloned.map.get('a')).toBe(1)\n    expect(cloned.map.get('b')).toEqual({ nested: true })\n\n    // nested proxySet inside proxyMap\n    const nestedSet = cloned.map.get('c')\n    expect([...nestedSet]).toEqual(['x', 'y', 'z'])\n    expect(typeof nestedSet.add).toBe('function')\n\n    // nested object with proxySet\n    expect([...cloned.nested.anotherSet]).toEqual(['a', 'b', 'c'])\n\n    // reactivity preserved\n    expect(() => cloned.set.add(4)).not.toThrow()\n    expect(() => cloned.map.set('d', 4)).not.toThrow()\n    expect(() => cloned.map.get('c').add('w')).not.toThrow()\n    expect(() => cloned.nested.anotherSet.add('d')).not.toThrow()\n  })\n\n  // Edge cases\n  it('should handle empty proxySet and proxyMap', () => {\n    const original = proxy({\n      emptySet: proxySet<number>(),\n      emptyMap: proxyMap<string, number>(),\n    })\n\n    const cloned = deepProxy(original)\n\n    expect(cloned.emptySet.size).toBe(0)\n    expect(cloned.emptyMap.size).toBe(0)\n\n    expect(() => cloned.emptySet.add(1)).not.toThrow()\n    expect(() => cloned.emptyMap.set('a', 1)).not.toThrow()\n  })\n})\n\n// -----------------\n// Extra edge cases\n// -----------------\ndescribe('deepProxy – additional edge cases', () => {\n  it('handles a simple circurlar ref', () => {\n    const a: any = { name: 'a' }\n    a.self = a\n\n    const cloned = deepProxy(a)\n    expect(cloned).not.toBe(a)\n    expect(cloned.name).toBe('a')\n    expect(cloned.self).toBe(cloned)\n  })\n\n  it('handles mutual circular references across two objects', () => {\n    const a: any = { name: 'a' }\n    const b: any = { name: 'b' }\n    a.b = b\n    b.a = a\n\n    const cloned = deepProxy(a)\n    expect(cloned).not.toBe(a)\n    expect(cloned.name).toBe('a')\n    expect(cloned.b.name).toBe('b')\n    expect(cloned.b.a).toBe(cloned)\n  })\n\n  it('preserves shared references (aliasing)', () => {\n    const shared = { id: 123 }\n    const obj = {\n      left: { child: shared },\n      right: { child: shared },\n    }\n\n    const cloned = deepProxy(obj)\n    expect(cloned).not.toBe(obj)\n    expect(cloned.left.child).not.toBe(shared)\n    expect(cloned.right.child).not.toBe(shared)\n    expect(cloned.left.child).toBe(cloned.right.child)\n  })\n\n  it('converts native Set/Map to proxySet/proxyMap recursively', () => {\n    const obj = {\n      s: new Set([1, 2, 3]),\n      m: new Map<string, any>([\n        ['x', 1],\n        ['y', { z: 9 }],\n      ]),\n    }\n\n    const cloned = deepProxy(obj)\n\n    expect(isProxySet(cloned.s)).toBe(true)\n    expect(isProxyMap(cloned.m)).toBe(true)\n\n    expect([...cloned.s]).toEqual([1, 2, 3])\n    expect(cloned.m.get('x')).toBe(1)\n    expect(cloned.m.get('y')).toEqual({ z: 9 })\n\n    expect(() => cloned.s.add(99)).not.toThrow()\n    expect(() => cloned.m.set('k', 42)).not.toThrow()\n  })\n\n  it('normalizes nested native Set/Map into proxySet/proxyMap', () => {\n    const obj = {\n      list: [new Set(['a', 'b']), { inner: new Map([['k', new Set([1, 2])]]) }],\n    }\n\n    const cloned = deepProxy(obj)\n    const s0 = cloned.list[0] as unknown as Set<string>\n    expect(isProxySet(s0)).toBe(true)\n    expect([...s0]).toEqual(['a', 'b'])\n\n    const m = (cloned.list[1] as any).inner as Map<string, Set<number>>\n    expect(isProxyMap(m)).toBe(true)\n    const innerS = m.get('k')!\n    expect(isProxySet(innerS)).toBe(true)\n    expect([...innerS]).toEqual([1, 2])\n  })\n\n  it('leaves functions as-is (callable identity preserved)', () => {\n    const fn = vi.fn((x: number) => x * 2)\n    const obj = { fn }\n    const cloned = deepProxy(obj)\n\n    expect(cloned.fn).toBe(fn)\n    expect(cloned.fn(3)).toBe(6)\n    expect(fn).toHaveBeenCalledWith(3)\n  })\n\n  it('preserves symbol-keyed properties and descriptors', () => {\n    const sym = Symbol('secret')\n    const obj: any = { visible: 1 }\n    Object.defineProperty(obj, sym, {\n      value: 42,\n      enumerable: false,\n      configurable: true,\n      writable: true,\n    })\n\n    const cloned = deepProxy(obj)\n    expect(cloned.visible).toBe(1)\n\n    const desc = Object.getOwnPropertyDescriptor(cloned, sym)!\n    expect(desc.enumerable).toBe(false)\n    expect(desc.value).toBe(42)\n  })\n\n  it('preserves non-enumerable and non-writable string-keyed descriptors', () => {\n    const obj: any = {}\n    Object.defineProperty(obj, 'hidden', {\n      value: 7,\n      enumerable: false,\n      configurable: true,\n      writable: false,\n    })\n\n    const cloned = deepProxy(obj)\n    const d = Object.getOwnPropertyDescriptor(cloned, 'hidden')!\n    expect(d.enumerable).toBe(false)\n    expect(d.writable).toBe(false)\n    expect(d.value).toBe(7)\n  })\n\n  it('preserves getter/setter behavior and metadata', () => {\n    const store: { _x: number } = { _x: 1 }\n    const obj: any = {}\n    Object.defineProperty(obj, 'x', {\n      get() {\n        return store._x\n      },\n      set(v: number) {\n        store._x = v\n      },\n      enumerable: true,\n      configurable: true,\n    })\n\n    const cloned = deepProxy(obj)\n    expect(cloned.x).toBe(1)\n    cloned.x = 10\n    expect(store._x).toBe(10)\n\n    const d = Object.getOwnPropertyDescriptor(cloned, 'x')!\n    expect(typeof d.get).toBe('function')\n    expect(typeof d.set).toBe('function')\n  })\n\n  it('preserves prototype chain of custom classes (methods continue to work)', () => {\n    class Counter {\n      n: number\n      constructor(n: number) {\n        this.n = n\n      }\n      inc() {\n        this.n += 1\n      }\n    }\n    const c = new Counter(5)\n    const obj = { c }\n\n    const cloned = deepProxy(obj)\n    const cc = cloned.c as any\n    expect(Object.getPrototypeOf(cc)).toBe(Counter.prototype)\n    expect(cc.n).toBe(5)\n    cc.inc()\n    expect(cc.n).toBe(6)\n  })\n\n  it('handles arrays with holes and custom non-enum props', () => {\n    const arr: any[] = []\n    arr[2] = 'two' // holes at 0 and 1\n    Object.defineProperty(arr, 'meta', {\n      value: 'info',\n      enumerable: false,\n    })\n    const obj = { arr }\n\n    const cloned = deepProxy(obj)\n    expect(0 in cloned.arr).toBe(false)\n    expect(1 in cloned.arr).toBe(false)\n    expect(cloned.arr[2]).toBe('two')\n\n    const d = Object.getOwnPropertyDescriptor(cloned.arr, 'meta')!\n    expect(d.enumerable).toBe(false)\n    expect(d.value).toBe('info')\n  })\n\n  it('respects a provided refSet: passes through exotic instances by identity', () => {\n    const date = new Date('2020-01-02T03:04:05.000Z')\n    const re = /abc/gi\n    const buf = new ArrayBuffer(8)\n    const u8 = new Uint8Array(buf)\n    u8[0] = 123\n\n    const refSet = new WeakSet<object>([date, re, buf, u8])\n\n    const obj = {\n      date,\n      re,\n      buf,\n      u8,\n      nested: { date, u8 },\n    }\n\n    const cloned = deepProxy(obj, () => refSet)\n\n    expect(cloned.date).toBe(date)\n    expect(cloned.re).toBe(re)\n    expect(cloned.buf).toBe(buf)\n    expect(cloned.u8).toBe(u8)\n    expect(cloned.nested.date).toBe(date)\n    expect(cloned.nested.u8).toBe(u8)\n\n    // functionality intact\n    expect(cloned.date.getUTCFullYear()).toBe(2020)\n    expect(cloned.re.test('XYZabc')).toBe(true)\n    expect(cloned.u8[0]).toBe(123)\n  })\n\n  it('deeply proxies inside a proxied state object while keeping primitives by value', () => {\n    const state = proxy({\n      title: 'hello',\n      count: 1,\n      nested: {\n        arr: [1, 2, 3],\n        s: proxySet([1, 2]),\n        m: proxyMap([\n          ['x', 10],\n          ['y', 20],\n        ]),\n      },\n    })\n\n    const cloned = deepProxy(state)\n    expect(cloned.title).toBe('hello')\n    expect(cloned.count).toBe(1)\n    expect([...cloned.nested.arr]).toEqual([1, 2, 3])\n    expect([...cloned.nested.s]).toEqual([1, 2])\n    expect(cloned.nested.m.get('x')).toBe(10)\n\n    expect(() => cloned.nested.s.add(3)).not.toThrow()\n    expect(() => cloned.nested.m.set('z', 30)).not.toThrow()\n  })\n})\n"
  },
  {
    "path": "tests/devtools.test.tsx",
    "content": "import { StrictMode, Suspense } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, useSnapshot } from 'valtio'\nimport { devtools } from 'valtio/utils'\n\ndescribe('devtools', () => {\n  let extensionSubscriber: ((message: any) => void) | undefined\n\n  const extension = {\n    subscribe: vi.fn((fn: typeof extensionSubscriber) => {\n      extensionSubscriber = fn\n      return () => {}\n    }),\n    unsubscribe: vi.fn(),\n    send: vi.fn(),\n    init: vi.fn(),\n    error: vi.fn(),\n  }\n  const extensionConnector = { connect: vi.fn(() => extension) }\n  ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = extensionConnector\n\n  beforeEach(() => {\n    extensionConnector.connect.mockClear()\n    extension.subscribe.mockClear()\n    extension.unsubscribe.mockClear()\n    extension.send.mockClear()\n    extension.init.mockClear()\n    extension.error.mockClear()\n    extensionSubscriber = undefined\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('connects to the extension by initialiing', () => {\n    const obj = proxy({ count: 0 })\n    devtools(obj, { enabled: true })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(extension.init).toHaveBeenLastCalledWith({ count: 0 })\n  })\n\n  describe('If there is no extension installed...', () => {\n    let savedConsoleWarn: any\n    beforeEach(() => {\n      savedConsoleWarn = console.warn\n      console.warn = vi.fn()\n      ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = undefined\n    })\n    afterEach(() => {\n      console.warn = savedConsoleWarn\n      ;(window as any).__REDUX_DEVTOOLS_EXTENSION__ = extensionConnector\n    })\n\n    it('does not throw', () => {\n      const obj = proxy({ count: 0 })\n      devtools(obj)\n      const Counter = () => {\n        const snap = useSnapshot(obj)\n        return (\n          <>\n            <div>count: {snap.count}</div>\n            <button onClick={() => ++obj.count}>button</button>\n          </>\n        )\n      }\n      expect(() => {\n        render(\n          <StrictMode>\n            <Counter />\n          </StrictMode>,\n        )\n      }).not.toThrow()\n    })\n\n    it('does not warn if enabled is undefined', () => {\n      const obj = proxy({ count: 0 })\n      devtools(obj)\n      const Counter = () => {\n        const snap = useSnapshot(obj)\n        return (\n          <>\n            <div>count: {snap.count}</div>\n            <button onClick={() => ++obj.count}>button</button>\n          </>\n        )\n      }\n      render(\n        <StrictMode>\n          <Counter />\n        </StrictMode>,\n      )\n      expect(console.warn).not.toBeCalled()\n    })\n\n    it('[DEV-ONLY] warns if enabled is true', () => {\n      const obj = proxy({ count: 0 })\n      devtools(obj, { enabled: true })\n      const Counter = () => {\n        const snap = useSnapshot(obj)\n        return (\n          <>\n            <div>count: {snap.count}</div>\n            <button onClick={() => ++obj.count}>button</button>\n          </>\n        )\n      }\n      render(\n        <StrictMode>\n          <Counter />\n        </StrictMode>,\n      )\n      expect(console.warn).toHaveBeenLastCalledWith(\n        '[Warning] Please install/enable Redux devtools extension',\n      )\n    })\n\n    it.skip('[PRD-ONLY] does not warn even if enabled is true', () => {\n      const obj = proxy({ count: 0 })\n      devtools(obj, { enabled: true })\n      const Counter = () => {\n        const snap = useSnapshot(obj)\n        return (\n          <>\n            <div>count: {snap.count}</div>\n            <button onClick={() => ++obj.count}>button</button>\n          </>\n        )\n      }\n      render(\n        <StrictMode>\n          <Counter />\n        </StrictMode>,\n      )\n      expect(console.warn).not.toBeCalled()\n    })\n  })\n\n  it('updating state should call devtools.send', async () => {\n    const obj = proxy({ count: 0 })\n    devtools(obj, { enabled: true })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.count}</div>\n          <button onClick={() => ++obj.count}>button</button>\n        </>\n      )\n    }\n\n    extension.send.mockClear()\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(extension.send).toBeCalledTimes(0)\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(extension.send).toBeCalledTimes(1)\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n    expect(extension.send).toBeCalledTimes(2)\n  })\n\n  describe('when it receives an message of type...', () => {\n    it('updating state with ACTION', async () => {\n      const obj = proxy({ count: 0 })\n      devtools(obj, { enabled: true })\n\n      const Counter = () => {\n        const snap = useSnapshot(obj)\n        return (\n          <>\n            <div>count: {snap.count}</div>\n            <button onClick={() => ++obj.count}>button</button>\n          </>\n        )\n      }\n\n      extension.send.mockClear()\n\n      render(\n        <StrictMode>\n          <Suspense fallback={'loading'}>\n            <Counter />\n          </Suspense>\n        </StrictMode>,\n      )\n\n      expect(extension.send).toBeCalledTimes(0)\n\n      fireEvent.click(screen.getByText('button'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('count: 1')).toBeInTheDocument()\n      expect(extension.send).toBeCalledTimes(1)\n\n      act(() =>\n        (extensionSubscriber as (message: any) => void)({\n          type: 'ACTION',\n          payload: JSON.stringify({ count: 0 }),\n        }),\n      )\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('count: 0')).toBeInTheDocument()\n      expect(extension.send).toBeCalledTimes(2)\n    })\n\n    describe('DISPATCH and payload of type...', () => {\n      it('dispatch & COMMIT', async () => {\n        const obj = proxy({ count: 0 })\n        devtools(obj, { enabled: true })\n\n        const Counter = () => {\n          const snap = useSnapshot(obj)\n          return (\n            <>\n              <div>count: {snap.count}</div>\n              <button onClick={() => ++obj.count}>button</button>\n            </>\n          )\n        }\n\n        extension.send.mockClear()\n\n        render(\n          <StrictMode>\n            <Counter />\n          </StrictMode>,\n        )\n\n        expect(extension.send).toBeCalledTimes(0)\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('count: 1')).toBeInTheDocument()\n        expect(extension.send).toBeCalledTimes(1)\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n        act(() =>\n          (extensionSubscriber as (message: any) => void)({\n            type: 'DISPATCH',\n            payload: { type: 'COMMIT' },\n          }),\n        )\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('count: 2')).toBeInTheDocument()\n        expect(extension.init).toBeCalledWith({ count: 2 })\n      })\n\n      it('dispatch & IMPORT_STATE', async () => {\n        const obj = proxy({ count: 0 })\n        devtools(obj, { enabled: true })\n\n        const Counter = () => {\n          const snap = useSnapshot(obj)\n          return (\n            <>\n              <div>count: {snap.count}</div>\n              <button onClick={() => ++obj.count}>button</button>\n            </>\n          )\n        }\n\n        extension.send.mockClear()\n\n        render(\n          <StrictMode>\n            <Counter />\n          </StrictMode>,\n        )\n\n        const nextLiftedState = {\n          actionsById: ['5', '6'],\n          computedStates: [{ state: { count: 5 } }, { state: { count: 6 } }],\n        }\n\n        expect(extension.send).toBeCalledTimes(0)\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('count: 1')).toBeInTheDocument()\n        expect(extension.send).toBeCalledTimes(1)\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n        act(() =>\n          (extensionSubscriber as (message: any) => void)({\n            type: 'DISPATCH',\n            payload: { type: 'IMPORT_STATE', nextLiftedState },\n          }),\n        )\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(extension.init).toBeCalledWith({ count: 5 })\n        expect(screen.getByText('count: 6')).toBeInTheDocument()\n      })\n\n      describe('JUMP_TO_STATE | JUMP_TO_ACTION...', () => {\n        it('time travelling', async () => {\n          const obj = proxy({ count: 0 })\n          devtools(obj, { enabled: true })\n\n          const Counter = () => {\n            const snap = useSnapshot(obj)\n            return (\n              <>\n                <div>count: {snap.count}</div>\n                <button onClick={() => ++obj.count}>button</button>\n              </>\n            )\n          }\n\n          extension.send.mockClear()\n\n          render(\n            <StrictMode>\n              <Counter />\n            </StrictMode>,\n          )\n\n          expect(extension.send).toBeCalledTimes(0)\n\n          fireEvent.click(screen.getByText('button'))\n          await act(() => vi.advanceTimersByTimeAsync(0))\n          expect(screen.getByText('count: 1')).toBeInTheDocument()\n          expect(extension.send).toBeCalledTimes(1)\n\n          act(() =>\n            (extensionSubscriber as (message: any) => void)({\n              type: 'DISPATCH',\n              payload: { type: 'JUMP_TO_ACTION' },\n              state: JSON.stringify({ count: 0 }),\n            }),\n          )\n          await act(() => vi.advanceTimersByTimeAsync(0))\n          expect(screen.getByText('count: 0')).toBeInTheDocument()\n          expect(extension.send).toBeCalledTimes(1)\n\n          fireEvent.click(screen.getByText('button'))\n          await act(() => vi.advanceTimersByTimeAsync(0))\n          expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n          fireEvent.click(screen.getByText('button'))\n          await act(() => vi.advanceTimersByTimeAsync(0))\n          expect(screen.getByText('count: 2')).toBeInTheDocument()\n          expect(extension.send).toBeCalledTimes(3)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "tests/getter.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, useSnapshot } from 'valtio'\n\ndescribe('getter', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('simple object getters', async () => {\n    const computeDouble = vi.fn((x: number) => x * 2)\n    const state = proxy({\n      count: 0,\n      get doubled() {\n        return computeDouble(this.count)\n      },\n    })\n\n    const Counter = ({ name }: { name: string }) => {\n      const snap = useSnapshot(state)\n      return (\n        <>\n          <div>\n            {name} count: {snap.doubled}\n          </div>\n          <button onClick={() => ++state.count}>{name} button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter name=\"A\" />\n        <Counter name=\"B\" />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('A count: 0')).toBeInTheDocument()\n    expect(screen.getByText('B count: 0')).toBeInTheDocument()\n\n    computeDouble.mockClear()\n\n    fireEvent.click(screen.getByText('A button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('A count: 2')).toBeInTheDocument()\n    expect(screen.getByText('B count: 2')).toBeInTheDocument()\n    expect(computeDouble).toBeCalledTimes(1)\n  })\n\n  it('object getters returning object', async () => {\n    const computeDouble = vi.fn((x: number) => x * 2)\n    const state = proxy({\n      count: 0,\n      get doubled() {\n        return { value: computeDouble(this.count) }\n      },\n    })\n\n    const Counter = ({ name }: { name: string }) => {\n      const snap = useSnapshot(state)\n      return (\n        <>\n          <div>\n            {name} count: {snap.doubled.value}\n          </div>\n          <button onClick={() => ++state.count}>{name} button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter name=\"A\" />\n        <Counter name=\"B\" />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('A count: 0')).toBeInTheDocument()\n    expect(screen.getByText('B count: 0')).toBeInTheDocument()\n\n    computeDouble.mockClear()\n\n    fireEvent.click(screen.getByText('A button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('A count: 2')).toBeInTheDocument()\n    expect(screen.getByText('B count: 2')).toBeInTheDocument()\n    expect(computeDouble).toBeCalledTimes(1)\n  })\n})\n"
  },
  {
    "path": "tests/mapset.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { describe, expect, it } from 'vitest'\nimport { proxy, useSnapshot } from 'valtio'\n\ndescribe('mapset', () => {\n  it('unsupported map', async () => {\n    const obj = proxy({ map: new Map([['count', 0]]) })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj) as any\n      return (\n        <>\n          <div>count: {snap.map.get('count')}</div>\n          <button onClick={() => obj.map.set('count', 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(() => screen.getByText('count: 1')).toThrow()\n  })\n\n  it('unsupported set', async () => {\n    const obj = proxy({ set: new Set([1, 2, 3]) })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj) as any\n      return (\n        <>\n          <div>count: {[...snap.set].join(',')}</div>\n          <button onClick={() => obj.set.add(4)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 1,2,3')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(() => screen.getByText('count: 1,2,3,4')).toThrow()\n  })\n})\n"
  },
  {
    "path": "tests/memoryleaks.test.ts",
    "content": "import LeakDetector from 'jest-leak-detector'\nimport { describe, expect, it } from 'vitest'\nimport { proxy, subscribe } from 'valtio'\n\ndescribe('no memory leaks with proxy', () => {\n  it('empty object', async () => {\n    let state = proxy({})\n    const detector = new LeakDetector(state)\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('child object', async () => {\n    let state = proxy({ child: {} })\n    const detector = new LeakDetector(state)\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('global child object', async () => {\n    const child = {}\n    let state = proxy({ child })\n    const detector = new LeakDetector(state)\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('global child proxy', async () => {\n    const child = proxy({})\n    let state = proxy({ child })\n    const detector = new LeakDetector(state)\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('object cycle (level 1)', async () => {\n    let state = proxy({} as { child?: unknown })\n    state.child = state\n    const detector = new LeakDetector(state)\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('object cycle (level 2)', async () => {\n    let state = proxy({ child: {} as { child?: unknown } })\n    state.child.child = state\n    const detector = new LeakDetector(state)\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n})\n\ndescribe('no memory leaks with proxy with subscription', () => {\n  it('empty object', async () => {\n    let state = proxy({})\n    const detector = new LeakDetector(state)\n    let unsub = subscribe(state, () => {})\n    await new Promise((resolve) => setTimeout(resolve, 1))\n    unsub()\n    unsub = undefined as never\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('child object', async () => {\n    let state = proxy({ child: {} })\n    const detector = new LeakDetector(state)\n    let unsub = subscribe(state, () => {})\n    await new Promise((resolve) => setTimeout(resolve, 1))\n    unsub()\n    unsub = undefined as never\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('global child object', async () => {\n    const child = {}\n    let state = proxy({ child })\n    const detector = new LeakDetector(state)\n    let unsub = subscribe(state, () => {})\n    await new Promise((resolve) => setTimeout(resolve, 1))\n    unsub()\n    unsub = undefined as never\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('global child proxy', async () => {\n    const child = proxy({})\n    let state = proxy({ child })\n    const detector = new LeakDetector(state)\n    let unsub = subscribe(state, () => {})\n    await new Promise((resolve) => setTimeout(resolve, 1))\n    unsub()\n    unsub = undefined as never\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('object cycle (level 1)', async () => {\n    let state = proxy({} as { child?: unknown })\n    state.child = state\n    const detector = new LeakDetector(state)\n    let unsub = subscribe(state, () => {})\n    await new Promise((resolve) => setTimeout(resolve, 1))\n    unsub()\n    unsub = undefined as never\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('object cycle (level 2)', async () => {\n    let state = proxy({ child: {} as { child?: unknown } })\n    state.child.child = state\n    const detector = new LeakDetector(state)\n    let unsub = subscribe(state, () => {})\n    await new Promise((resolve) => setTimeout(resolve, 1))\n    unsub()\n    unsub = undefined as never\n    state = undefined as never\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n})\n"
  },
  {
    "path": "tests/optimization.test.tsx",
    "content": "import { useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, useSnapshot } from 'valtio'\n\ndescribe('optimization', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('should not rerender if the leaf value does not change', async () => {\n    const state = proxy({ nested: { count: 0 } })\n\n    const renderFn = vi.fn()\n    const Component = () => {\n      const snap = useSnapshot(state)\n      renderFn()\n      return (\n        <>\n          <div>Count: {snap.nested.count}</div>\n          <button\n            onClick={() => {\n              state.nested = { count: 0 }\n            }}\n          >\n            button-zero\n          </button>\n          <button\n            onClick={() => {\n              state.nested = { count: 1 }\n            }}\n          >\n            button-one\n          </button>\n        </>\n      )\n    }\n\n    render(<Component />)\n\n    expect(screen.getByText('Count: 0')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(1)\n\n    fireEvent.click(screen.getByText('button-zero'))\n\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(renderFn).toBeCalledTimes(1)\n\n    fireEvent.click(screen.getByText('button-one'))\n\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('Count: 1')).toBeInTheDocument()\n    expect(renderFn).toBeCalledTimes(2)\n  })\n\n  it('regression: useSnapshot renders should not fail consistency check with extra render (nested useSnapshot)', async () => {\n    const obj = proxy({ childCount: 0, parentCount: 0 })\n\n    const childRenderFn = vi.fn()\n    const Child = () => {\n      const snap = useSnapshot(obj)\n      childRenderFn(snap.childCount)\n      return (\n        <>\n          <div>childCount: {snap.childCount}</div>\n          <button onClick={() => ++obj.childCount}>childButton</button>\n        </>\n      )\n    }\n\n    const parentRenderFn = vi.fn()\n    const Parent = () => {\n      const snap = useSnapshot(obj)\n      parentRenderFn(snap.parentCount)\n      return (\n        <>\n          <div>parentCount: {snap.parentCount}</div>\n          <button onClick={() => ++obj.parentCount}>parentButton</button>\n          <Child />\n        </>\n      )\n    }\n\n    render(<Parent />)\n\n    expect(screen.getByText('childCount: 0')).toBeInTheDocument()\n    expect(screen.getByText('parentCount: 0')).toBeInTheDocument()\n\n    expect(childRenderFn).toBeCalledTimes(1)\n    expect(childRenderFn).lastCalledWith(0)\n    expect(parentRenderFn).toBeCalledTimes(1)\n    expect(parentRenderFn).lastCalledWith(0)\n\n    obj.parentCount += 1\n\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('childCount: 0')).toBeInTheDocument()\n    expect(screen.getByText('parentCount: 1')).toBeInTheDocument()\n\n    expect(childRenderFn).toBeCalledTimes(2)\n    expect(childRenderFn).lastCalledWith(0)\n    expect(parentRenderFn).toBeCalledTimes(2)\n    expect(parentRenderFn).lastCalledWith(1)\n  })\n\n  it('regression: useSnapshot renders should not fail consistency check with extra render', async () => {\n    const obj = proxy({ childCount: 0, anotherValue: 0 })\n\n    const childRenderFn = vi.fn()\n    const Child = () => {\n      const snap = useSnapshot(obj)\n      childRenderFn(snap.childCount)\n      return (\n        <>\n          <div>childCount: {snap.childCount}</div>\n          <button onClick={() => ++obj.childCount}>childButton</button>\n        </>\n      )\n    }\n\n    const parentRenderFn = vi.fn()\n    const Parent = () => {\n      const [parentCount, setParentCount] = useState(0)\n\n      parentRenderFn(parentCount)\n\n      return (\n        <>\n          <div>parentCount: {parentCount}</div>\n          <button onClick={() => setParentCount((v) => v + 1)}>\n            parentButton\n          </button>\n          <Child />\n        </>\n      )\n    }\n\n    render(<Parent />)\n\n    expect(screen.getByText('childCount: 0')).toBeInTheDocument()\n    expect(screen.getByText('parentCount: 0')).toBeInTheDocument()\n\n    expect(childRenderFn).toBeCalledTimes(1)\n    expect(childRenderFn).lastCalledWith(0)\n    expect(parentRenderFn).toBeCalledTimes(1)\n    expect(parentRenderFn).lastCalledWith(0)\n\n    obj.anotherValue += 1\n\n    fireEvent.click(screen.getByText('parentButton'))\n    expect(screen.getByText('childCount: 0')).toBeInTheDocument()\n    expect(screen.getByText('parentCount: 1')).toBeInTheDocument()\n\n    expect(childRenderFn).toBeCalledTimes(2)\n    expect(childRenderFn).lastCalledWith(0)\n    expect(parentRenderFn).toBeCalledTimes(2)\n    expect(parentRenderFn).lastCalledWith(1)\n  })\n})\n"
  },
  {
    "path": "tests/performance.test.tsx",
    "content": "import { describe, expect, it } from 'vitest'\nimport { proxy, snapshot, subscribe } from 'valtio'\n\nconst DEPTHS = [4, 8, 16, 32, 64, 128, 256]\nconst REPEATS = 5000\n\nconst measurePerformance = (\n  setUp: () => void,\n  action: () => void,\n  tearDown: () => void,\n) => {\n  const times: number[] = []\n  setUp()\n  for (let i = 0; i < REPEATS; i++) {\n    const start = performance.now()\n    action()\n    times.push(performance.now() - start)\n  }\n  tearDown()\n  times.sort((a, b) => a - b)\n  const mid = Math.floor(times.length / 2)\n  const median =\n    times.length % 2 ? times[mid]! : (times[mid - 1]! + times[mid]!) / 2\n  return median\n}\n\nconst mean = (a: number[]) => a.reduce((s, v) => s + v, 0) / a.length\n\nconst logSlope = (xs: number[], ys: number[]) => {\n  const lx = xs.map(Math.log)\n  const ly = ys.map(Math.log)\n  const mx = mean(lx)\n  const my = mean(ly)\n  let num = 0\n  let den = 0\n  for (let i = 0; i < lx.length; i++) {\n    num += (lx[i]! - mx) * (ly[i]! - my)\n    den += (lx[i]! - mx) ** 2\n  }\n  return num / den\n}\n\nconst buildNestedObj = (depth: number) => {\n  const leaf = { x: 1 }\n  let obj: { child: unknown } = { child: leaf }\n  for (let i = 1; i < depth; i++) {\n    obj = { child: obj }\n  }\n  return { obj, leaf }\n}\n\ndescribe('performance with nested objects', () => {\n  it('snapshot with subscription', async () => {\n    const medians: number[] = []\n    for (const depth of DEPTHS) {\n      let unsub: (() => void) | undefined\n      let proxyObj: object | undefined\n      const median = measurePerformance(\n        () => {\n          const { obj, leaf } = buildNestedObj(depth)\n          const proxyLeaf = proxy(leaf)\n          proxyObj = proxy(obj)\n          unsub = subscribe(proxyObj, () => {})\n          snapshot(proxyObj)\n          proxyLeaf.x++\n        },\n        () => {\n          snapshot(proxyObj!)\n        },\n        () => {\n          unsub?.()\n        },\n      )\n      medians.push(median)\n    }\n    const slope = logSlope(DEPTHS, medians)\n    expect(slope).toBeLessThan(0.1)\n  })\n\n  // TODO add more performance tests\n})\n"
  },
  {
    "path": "tests/proxyMap.bench.ts",
    "content": "import { bench, describe } from 'vitest'\nimport { snapshot } from 'valtio'\nimport { proxyMap } from 'valtio/utils'\n\n// Helper function to generate test data\nfunction generateTestData(size: number): [number, number][] {\n  const data: [any, any][] = []\n  for (let i = 0; i < size; i++) {\n    data.push([{ id: i }, { i }])\n  }\n  return data\n}\n\nconst TEST_SIZES = [1000, 10_000, 100_000]\n\nTEST_SIZES.forEach((size) => {\n  describe.skip(`Insertion -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>()\n      testData.forEach(([key, value]) => {\n        map.set(key, value)\n      })\n    })\n  })\n\n  describe.skip(`Insertion and Update -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>()\n      testData.forEach(([key, value]) => {\n        map.set(key, value)\n        map.set(key, -1)\n      })\n    })\n  })\n\n  describe.skip(`Retrieval -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      testData.forEach(([key]) => {\n        map.get(key)\n      })\n    })\n  })\n\n  describe.skip(`Deletion -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      testData.forEach(([key]) => {\n        map.delete(key)\n      })\n    })\n  })\n\n  describe.skip(`Iteration -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const _map = proxyMap<number, number>(testData)\n      testData.forEach(([_key, _value]) => {})\n    })\n  })\n\n  describe.skip(`Insertion, Retrieval, and Deletion -${size} items`, () => {\n    const testData = generateTestData(size)\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      testData.forEach(([key, value]) => {\n        map.set(key, value)\n        map.get(key)\n        map.delete(key)\n      })\n    })\n  })\n\n  describe.skip(`entries -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      for (const [k, v] of map.entries()) {\n        const _k = k\n        const _v = v\n      }\n    })\n  })\n\n  describe.skip(`keys -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      for (const k of map.keys()) {\n        const _k = k\n      }\n    })\n  })\n\n  describe.skip(`values -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      for (const v of map.values()) {\n        const _v = v\n      }\n    })\n  })\n\n  describe.skip(`snapshot -${size} items`, () => {\n    const testData = generateTestData(size)\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      const snap = snapshot(map)\n      testData.forEach(([key, _value]) => {\n        snap.get(key)\n      })\n    })\n  })\n\n  describe(`snapshot & modify -${size} items`, () => {\n    const testData = generateTestData(size)\n    const oneData = generateTestData(1)[0]!\n\n    bench('proxyMap', () => {\n      const map = proxyMap<number, number>(testData)\n      const _snap1 = snapshot(map)\n      map.set(oneData[0], oneData[1])\n      const _snap2 = snapshot(map)\n    })\n  })\n\n  describe.skip('Clear -${size} items', () => {\n    const testData = generateTestData(size)\n    const map = proxyMap<number, number>(testData)\n\n    bench('proxyMap', () => {\n      map.clear()\n    })\n  })\n})\n"
  },
  {
    "path": "tests/proxyMap.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, snapshot, useSnapshot } from 'valtio'\nimport { proxyMap, proxySet } from 'valtio/utils'\n\nconst initialValues = [\n  {\n    name: 'number',\n    value: [[-10, 1]],\n  },\n  {\n    name: 'string',\n    value: [['hello', 'world']],\n  },\n  {\n    name: 'Symbol',\n    value: [[Symbol(), Symbol()]],\n  },\n  {\n    name: 'boolean',\n    value: [[false, true]],\n  },\n  {\n    name: 'array',\n    value: [\n      [\n        [10, 'hello'],\n        [1, 2, 3, 'x', 'w'],\n      ],\n    ],\n  },\n  {\n    name: 'object',\n    value: [[{}, { id: 'something', field: null }]],\n  },\n  {\n    name: 'null',\n    value: [[null, [null, 1]]],\n  },\n  {\n    name: 'function',\n    value: [[() => {}, () => {}]],\n  },\n  {\n    name: 'array buffer',\n    value: [[new ArrayBuffer(8), new ArrayBuffer(8)]],\n  },\n  {\n    name: 'Set',\n    value: [[new Set(), new Set(['x', 'y', 'z'])]],\n  },\n  {\n    name: 'Map',\n    value: [[new Map(), new Map([['key', 'value']])]],\n  },\n  {\n    name: 'proxySet',\n    value: [[proxySet([{}]), proxySet([{}, Symbol()])]],\n  },\n]\n\nconst inputValues = [\n  {\n    name: 'array',\n    value: [1, 'hello'],\n  },\n  {\n    name: 'nested array',\n    value: [[1, 'hello']],\n  },\n  {\n    name: 'Map',\n    value: new Map<any, any>([\n      ['key1', 'value1'],\n      [{}, 'value2'],\n    ]),\n  },\n  {\n    name: 'boolean',\n    value: false,\n  },\n  {\n    name: 'number',\n    value: 123,\n  },\n  {\n    name: 'string',\n    value: 'hello',\n  },\n  {\n    name: 'Set',\n    value: new Set([1, 2, 3]),\n  },\n  {\n    name: 'proxySet',\n    value: proxySet([1, {}, null, 'xyz', Symbol()]),\n  },\n  {\n    name: 'object',\n    value: { id: Symbol(), field: 'field', bool: true, null: null },\n  },\n]\n\ndescribe('proxyMap', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  describe('features parity with native Map', () => {\n    initialValues.forEach(({ name, value }) => {\n      it(`Support Map operations on ${name}`, () => {\n        const map = proxyMap(value as any)\n        const nativeMap = new Map(value as any)\n\n        // check for Symbol.toStringTag / toString\n        expect(`${map}`).toBe(`${nativeMap}`)\n\n        const expectOutputToMatch = () => {\n          expect(map.size).toStrictEqual(nativeMap.size)\n          expect(Array.from(map.values())).toStrictEqual(\n            Array.from(nativeMap.values()),\n          )\n          expect(Array.from(map.keys())).toStrictEqual(\n            Array.from(nativeMap.keys()),\n          )\n          expect(Array.from(map.entries())).toStrictEqual(\n            Array.from(nativeMap.entries()),\n          )\n          expect(JSON.stringify(map)).toStrictEqual(JSON.stringify(nativeMap))\n\n          JSON.stringify(map, (_, mapV) => {\n            JSON.stringify(nativeMap, (_, nativeMapV) => {\n              expect(mapV).toStrictEqual(nativeMapV)\n            })\n          })\n\n          // cover loops\n          const handleForEach = vi.fn()\n          const handleForOf = vi.fn()\n\n          map.forEach(handleForEach)\n          expect(handleForEach).toHaveBeenCalledTimes(map.size)\n\n          for (const _ of map) {\n            handleForOf()\n          }\n\n          expect(handleForOf).toHaveBeenCalledTimes(map.size)\n        }\n\n        expectOutputToMatch()\n\n        const [firstElementFromMap] = map\n        const [firstElementFromNativeMap] = nativeMap\n\n        // Bypass Forbidden non-null assertion\n        const keyFromMap = firstElementFromMap && firstElementFromMap[0]\n        const keyFromNativeMap =\n          firstElementFromNativeMap && firstElementFromNativeMap[0]\n\n        expect(map.has(keyFromMap)).toBe(nativeMap.has(keyFromNativeMap))\n\n        map.delete(keyFromMap)\n        nativeMap.delete(keyFromNativeMap)\n        expectOutputToMatch()\n\n        map.set('newKey', {})\n        nativeMap.set('newKey', {})\n        expectOutputToMatch()\n\n        // test value replacement\n        map.set('newKey', 'newValue')\n        nativeMap.set('newKey', 'newValue')\n        expectOutputToMatch()\n\n        // test getter\n        expect(map.get('newKey')).toBe(nativeMap.get('newKey'))\n      })\n    })\n\n    it('support initialization with null', () => {\n      const map = proxyMap(null)\n      const nativeMap = new Map(null)\n\n      expect(map.size).toStrictEqual(nativeMap.size)\n      expect(Array.from(map.values())).toStrictEqual(\n        Array.from(nativeMap.values()),\n      )\n      expect(Array.from(map.keys())).toStrictEqual(Array.from(nativeMap.keys()))\n      expect(Array.from(map.entries())).toStrictEqual(\n        Array.from(nativeMap.entries()),\n      )\n    })\n  })\n\n  describe('clear map', () => {\n    initialValues.forEach(({ name, value }) => {\n      it(`clear proxyMap of ${name}`, async () => {\n        const state = proxyMap(value as any)\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          return (\n            <>\n              <div>size: {snap.size}</div>\n              <button onClick={() => state.clear()}>button</button>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        expect(state.size).toBeGreaterThan(0)\n        expect(screen.getByText(`size: ${state.size}`)).toBeInTheDocument()\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('size: 0')).toBeInTheDocument()\n      })\n    })\n  })\n\n  describe('add value', () => {\n    inputValues.forEach(({ name, value }) => {\n      it(`update size when adding ${name}`, async () => {\n        const state = proxy({\n          map: proxyMap(),\n        })\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          return (\n            <>\n              <div>size: {snap.map.size}</div>\n              <button onClick={() => state.map.set(value, value)}>\n                button\n              </button>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        expect(screen.getByText('size: 0')).toBeInTheDocument()\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('size: 1')).toBeInTheDocument()\n      })\n    })\n  })\n\n  describe('delete', () => {\n    inputValues.forEach(({ name, value }) => {\n      it(`return false when trying to delete non-existing value of type ${name}`, () => {\n        const set = proxyMap()\n\n        expect(set.delete(value)).toBe(false)\n      })\n    })\n\n    initialValues.forEach(({ name, value }) => {\n      it(`support delete on key of type ${name}`, async () => {\n        const state = proxy({\n          map: proxyMap(value as any),\n        })\n\n        // pick a random value from the set\n        const [firstValue] = state.map\n\n        // Bypass Forbidden non-null assertion\n        const firstKey = firstValue && firstValue[0]\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          return (\n            <>\n              <div>size: {snap.map.size}</div>\n              <button onClick={() => state.map.delete(firstKey)}>button</button>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        expect(screen.getByText(`size: ${state.map.size}`)).toBeInTheDocument()\n\n        const expectedSizeAfterDelete =\n          state.map.size > 1 ? state.map.size - 1 : 0\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`size: ${expectedSizeAfterDelete}`),\n        ).toBeInTheDocument()\n      })\n    })\n  })\n\n  describe('proxyMap internal', () => {\n    it('should be sealed', () => {\n      expect(Object.isSealed(proxySet())).toBe(true)\n    })\n\n    it('should list only enumerable properties', () => {\n      const notEnumerableProps = ['data', 'size', 'toJSON']\n      expect(\n        Object.keys(proxyMap()).some((k) => notEnumerableProps.includes(k)),\n      ).toBe(false)\n    })\n  })\n\n  it('should throw TypeError when initial entries is not iterable', () => {\n    expect(() => proxyMap(123 as any)).toThrow(\n      'proxyMap:\\n\\tinitial state must be iterable\\n\\t\\ttip: structure should be [[key, value]]',\n    )\n  })\n\n  describe('snapshot', () => {\n    it('should error when trying to mutate a snapshot', () => {\n      const state = proxyMap()\n      const snap = snapshot(state)\n\n      // @ts-expect-error - snapshot should not be able to mutate\n      expect(() => snap.set('foo', 'bar')).toThrow(\n        'Cannot perform mutations on a snapshot',\n      )\n      // @ts-expect-error - snapshot should not be able to mutate\n      expect(() => snap.delete('foo')).toThrow(\n        'Cannot perform mutations on a snapshot',\n      )\n      // @ts-expect-error - snapshot should not be able to mutate\n      expect(() => snap.clear()).toThrow(\n        'Cannot perform mutations on a snapshot',\n      )\n    })\n\n    it('should not change snapshot with modifying the original proxy', async () => {\n      const state = proxyMap([\n        ['key1', {}],\n        ['key2', { nested: { count: 1 } }],\n      ])\n      const snap1 = snapshot(state)\n      expect(snap1.get('key1')).toBeDefined()\n      state.get('key2')!.nested!.count++\n      const snap2 = snapshot(state)\n      expect(snap1.get('key2')!.nested!.count).toBe(1)\n      expect(snap2.get('key2')!.nested!.count).toBe(2)\n    })\n\n    it('should work with deleting a key', async () => {\n      const state = proxyMap([['key1', 'val1']])\n      const snap1 = snapshot(state)\n      expect(snap1.has('key1')).toBe(true)\n      expect(snap1.get('key1')).toBe('val1')\n      state.delete('key1')\n      const snap2 = snapshot(state)\n      expect(snap1.has('key1')).toBe(true)\n      expect(snap1.get('key1')).toBe('val1')\n      expect(snap2.has('key1')).toBe(false)\n      expect(snap2.get('key1')).toBe(undefined)\n    })\n\n    it('should work with deleting a key and adding it again', async () => {\n      const state = proxyMap()\n      state.set('key1', 'val1')\n      const snap1 = snapshot(state)\n      expect(snap1.get('key1')).toBe('val1')\n      state.delete('key1')\n      state.set('key2', 'val2')\n      state.set('key1', 'val1modified')\n      const snap2 = snapshot(state)\n      expect(snap1.get('key1')).toBe('val1')\n      expect(snap2.get('key1')).toBe('val1modified')\n    })\n  })\n\n  describe('ui updates - useSnapshot', async () => {\n    it('should update ui when calling has before and after setting and deleting a key', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has key: {`${snap.has('key')}`}</p>\n            <button onClick={() => state.set('key', 'value')}>set key</button>\n            <button onClick={() => state.delete('key')}>delete key</button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set key'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete key'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling has before and after settiing and deleting multiple keys', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has key: {`${snap.has('key')}`}</p>\n            <p>has key2: {`${snap.has('key2')}`}</p>\n            <button\n              onClick={() => {\n                state.set('key', 'value')\n                state.set('key2', 'value')\n              }}\n            >\n              set keys\n            </button>\n            <button\n              onClick={() => {\n                state.delete('key')\n                state.delete('key2')\n              }}\n            >\n              delete keys\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set keys'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: true')).toBeInTheDocument()\n      expect(screen.getByText('has key2: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete keys'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling get with absent key that has been added later', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>value: {`${snap.get('key')}`}</p>\n            <button\n              onClick={() => {\n                state.set('key', 'value')\n              }}\n            >\n              set key\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('value: undefined')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set key'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('value: value')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling has before and after settiing multile keys and deleting a single one (first item)', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has key: {`${snap.has('key')}`}</p>\n            <p>has key2: {`${snap.has('key2')}`}</p>\n            <button\n              onClick={() => {\n                state.set('key', 'value')\n                state.set('key2', 'value')\n              }}\n            >\n              set keys\n            </button>\n            <button\n              onClick={() => {\n                state.delete('key')\n              }}\n            >\n              delete keys\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set keys'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: true')).toBeInTheDocument()\n      expect(screen.getByText('has key2: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete keys'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n      expect(screen.getByText('has key2: true')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling has/get before and after settiing multile keys and deleting a single one multiple times', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has key1: {`${snap.has('key')}`}</p>\n            <p>value1: {`${snap.get('key')}`}</p>\n            <p>has key2: {`${snap.has('key2')}`}</p>\n            <p>value2: {`${snap.get('key2')}`}</p>\n\n            <button\n              onClick={() => {\n                state.set('key', 'value')\n                state.set('key2', 'value')\n              }}\n            >\n              set keys\n            </button>\n            <button\n              onClick={() => {\n                state.delete('key')\n              }}\n            >\n              delete key 1\n            </button>\n            <button\n              onClick={() => {\n                state.delete('key2')\n              }}\n            >\n              delete key 2\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has key1: false')).toBeInTheDocument()\n      expect(screen.getByText('value1: undefined')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n      expect(screen.getByText('value2: undefined')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set keys'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key1: true')).toBeInTheDocument()\n      expect(screen.getByText('has key2: true')).toBeInTheDocument()\n      expect(screen.getByText('value1: value')).toBeInTheDocument()\n      expect(screen.getByText('value2: value')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete key 1'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key1: false')).toBeInTheDocument()\n      expect(screen.getByText('value1: undefined')).toBeInTheDocument()\n      expect(screen.getByText('has key2: true')).toBeInTheDocument()\n      expect(screen.getByText('value2: value')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete key 2'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key1: false')).toBeInTheDocument()\n      expect(screen.getByText('value1: undefined')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n      expect(screen.getByText('value2: undefined')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling only one get with absent key added later', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n        expect(snap).toBeDefined()\n\n        return (\n          <>\n            <button\n              onClick={() => {\n                state.set('key', 'value')\n              }}\n            >\n              set key\n            </button>\n            <button\n              onClick={() => {\n                state.delete('key')\n              }}\n            >\n              delete key\n            </button>\n          </>\n        )\n      }\n\n      const SeparateComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>value: {`${snap.get('key')}`}</p>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n          <SeparateComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('value: undefined')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set key'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('value: value')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete key'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('value: undefined')).toBeInTheDocument()\n    })\n\n    it('should update ui when clearing the map', async () => {\n      const state = proxyMap()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has key: {`${snap.has('key')}`}</p>\n            <p>has key2: {`${snap.has('key2')}`}</p>\n            <p>value1: {`${snap.get('key')}`}</p>\n            <p>value2: {`${snap.get('key2')}`}</p>\n            <button\n              onClick={() => {\n                state.set('key', 'value')\n                state.set('key2', 'value')\n              }}\n            >\n              set keys\n            </button>\n            <button\n              onClick={() => {\n                state.clear()\n              }}\n            >\n              clear map\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n      expect(screen.getByText('value1: undefined')).toBeInTheDocument()\n      expect(screen.getByText('value2: undefined')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('set keys'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: true')).toBeInTheDocument()\n      expect(screen.getByText('has key2: true')).toBeInTheDocument()\n      expect(screen.getByText('value1: value')).toBeInTheDocument()\n      expect(screen.getByText('value2: value')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('clear map'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has key: false')).toBeInTheDocument()\n      expect(screen.getByText('has key2: false')).toBeInTheDocument()\n      expect(screen.getByText('value1: undefined')).toBeInTheDocument()\n      expect(screen.getByText('value2: undefined')).toBeInTheDocument()\n    })\n  })\n\n  describe('ui updates - useSnapshot - iterator methods', () => {\n    const iteratorMethods = ['keys', 'values', 'entries'] as const\n\n    iteratorMethods.forEach((iteratorMethod) => {\n      it(`should be reactive to changes when using ${iteratorMethod} method`, async () => {\n        interface MapItem {\n          id: number\n          name: string\n        }\n        const state = proxyMap<number, MapItem>()\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          const addItem = (id: number) => {\n            const item: MapItem = {\n              id,\n              name: `item ${id}`,\n            }\n            state.set(item.id, item)\n          }\n\n          const methods = {\n            entries: Array.from(snap.entries()).map(([id, item]) => (\n              <li key={id}>{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n            )),\n            values: Array.from(snap.values()).map((item) => (\n              <li\n                key={item.id}\n              >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n            )),\n            keys: Array.from(snap.keys()).map((id) => {\n              const item = snap.get(id)!\n              return (\n                <li\n                  key={id}\n                >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n              )\n            }),\n          }\n\n          return (\n            <>\n              <button\n                onClick={() => {\n                  state.set(1, { name: 'item 1 updated', id: 1 })\n                }}\n              >\n                Update\n              </button>\n              <button onClick={() => addItem(1)}>Add</button>\n              <ul>{methods[iteratorMethod]}</ul>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        fireEvent.click(screen.getByText('Add'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`item.name: item 1; item.id: 1`),\n        ).toBeInTheDocument()\n\n        fireEvent.click(screen.getByText('Update'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`item.name: item 1 updated; item.id: 1`),\n        ).toBeInTheDocument()\n      })\n\n      it(`should be reactive to changes when using ${iteratorMethod} method when setting multiple values`, async () => {\n        interface MapItem {\n          id: number\n          name: string\n        }\n        const state = proxyMap<number, MapItem>()\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          const addItem = (id: number) => {\n            const item: MapItem = {\n              id,\n              name: `item ${id}`,\n            }\n            state.set(item.id, item)\n          }\n\n          return (\n            <>\n              <button\n                onClick={() => {\n                  state.forEach((value, key) => {\n                    state.set(key, { ...value, name: `${value.name} updated` })\n                  })\n                }}\n              >\n                Update\n              </button>\n              <button\n                onClick={() => {\n                  state.delete(1)\n                }}\n              >\n                Delete\n              </button>\n              <button\n                onClick={() => {\n                  addItem(1)\n                  addItem(2)\n                }}\n              >\n                Add\n              </button>\n              <ul>\n                {iteratorMethod === 'entries'\n                  ? Array.from(snap[iteratorMethod]()).map(([id, item]) => (\n                      <li\n                        key={id}\n                      >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n                    ))\n                  : iteratorMethod === 'values'\n                    ? Array.from(snap[iteratorMethod]()).map((item) => (\n                        <li\n                          key={item.id}\n                        >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n                      ))\n                    : Array.from(snap[iteratorMethod]()).map((id) => {\n                        const item = snap.get(id)!\n                        return (\n                          <li\n                            key={id}\n                          >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n                        )\n                      })}\n              </ul>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        fireEvent.click(screen.getByText('Add'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`item.name: item 1; item.id: 1`),\n        ).toBeInTheDocument()\n        expect(\n          screen.getByText(`item.name: item 2; item.id: 2`),\n        ).toBeInTheDocument()\n\n        fireEvent.click(screen.getByText('Update'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`item.name: item 1 updated; item.id: 1`),\n        ).toBeInTheDocument()\n        expect(\n          screen.getByText(`item.name: item 2 updated; item.id: 2`),\n        ).toBeInTheDocument()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "tests/proxySet.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, snapshot, useSnapshot } from 'valtio'\nimport { proxySet } from 'valtio/utils'\n\n// used to initialize proxySet during tests\nconst initialValues = [\n  {\n    name: 'array',\n    value: ['lorem', false, 1],\n  },\n  {\n    name: 'nested array',\n    value: [\n      [1, 2, 3],\n      [1, 2],\n      [51, 2, 3],\n    ],\n  },\n  {\n    name: 'Map',\n    value: new Map([\n      ['key1', 'value1'],\n      ['key2', 'value2'],\n    ]),\n  },\n  {\n    name: 'Set',\n    value: new Set([\n      ['key1', 'value1'],\n      ['key2', 'value2'],\n    ]),\n  },\n  {\n    name: 'string value',\n    value: 'hello',\n  },\n  {\n    name: 'nested Set',\n    value: new Set([\n      new Set([1, 2, 3]),\n      new Set([1, 2]),\n      new Set(['x', 'y', 'z']),\n    ]),\n  },\n  {\n    name: 'proxySet',\n    value: proxySet([1, {}, true]),\n  },\n  {\n    name: 'array of proxySet',\n    value: [proxySet([1, 2, 3]), proxySet([1, 2]), proxySet(['x', 'y', 'z'])],\n  },\n  {\n    name: 'list of objects',\n    value: [\n      { id: Symbol(), field: 'field', bool: true, null: null },\n      { id: Symbol(), field: 'field1', bool: false, null: null },\n      { id: Symbol(), field: 'field3', bool: true, null: null },\n    ],\n  },\n]\n\n// used to test various input types\nconst inputValues = [\n  {\n    name: 'array',\n    value: [1, 'hello'],\n  },\n  {\n    name: 'nested array',\n    value: [[1, 'hello']],\n  },\n  {\n    name: 'Map',\n    value: new Map<any, any>([\n      ['key1', 'value1'],\n      [{}, 'value2'],\n    ]),\n  },\n  {\n    name: 'boolean',\n    value: false,\n  },\n  {\n    name: 'number',\n    value: 123,\n  },\n  {\n    name: 'string',\n    value: 'hello',\n  },\n  {\n    name: 'Set',\n    value: new Set([1, 2, 3]),\n  },\n  {\n    name: 'proxySet',\n    value: proxySet([1, {}, null, 'xyz', Symbol()]),\n  },\n  {\n    name: 'object',\n    value: { id: Symbol(), field: 'field', bool: true, null: null },\n  },\n]\n\ndescribe('proxySet', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  describe('features parity with native Set', () => {\n    initialValues.forEach(({ name, value }) => {\n      it(`support Set operations on ${name}`, () => {\n        const set = proxySet<unknown>(value)\n        const nativeSet = new Set<unknown>(value)\n\n        // check for Symbol.toStringTag / toString\n        expect(`${set}`).toBe(`${nativeSet}`)\n\n        const expectOutputToMatch = () => {\n          expect(set.size).toStrictEqual(nativeSet.size)\n          expect(Array.from(set.values())).toStrictEqual(\n            Array.from(nativeSet.values()),\n          )\n          expect(Array.from(set.keys())).toStrictEqual(\n            Array.from(nativeSet.keys()),\n          )\n          expect(Array.from(set.entries())).toStrictEqual(\n            Array.from(nativeSet.entries()),\n          )\n          expect(JSON.stringify(set)).toStrictEqual(JSON.stringify(nativeSet))\n\n          JSON.stringify(set, (_, setV) => {\n            JSON.stringify(nativeSet, (_, nativeSetV) => {\n              expect(setV).toStrictEqual(nativeSetV)\n            })\n          })\n\n          // cover loops\n          const handleForEach = vi.fn()\n          const handleForOf = vi.fn()\n\n          set.forEach(handleForEach)\n          expect(handleForEach).toHaveBeenCalledTimes(set.size)\n\n          for (const _ of set) {\n            handleForOf()\n          }\n\n          expect(handleForOf).toHaveBeenCalledTimes(set.size)\n        }\n\n        expectOutputToMatch()\n\n        const [valueToDeleteFromSet] = set\n        const [valueToDeleteFromNativeSet] = nativeSet\n\n        expect(set.delete(valueToDeleteFromSet)).toBe(\n          nativeSet.delete(valueToDeleteFromNativeSet),\n        )\n\n        expectOutputToMatch()\n\n        set.add('newValue')\n        nativeSet.add('newValue')\n        expectOutputToMatch()\n\n        set.clear()\n        nativeSet.clear()\n        expectOutputToMatch()\n      })\n    })\n\n    inputValues.forEach(({ value, name }) => {\n      it(`prevent adding duplicate for type ${name}`, () => {\n        const set = proxySet<unknown>([value])\n\n        expect(set.size).toBe(1)\n\n        set.add(value)\n        expect(set.size).toBe(1)\n      })\n    })\n  })\n\n  describe('unsupported initial values', () => {\n    const unsupportedInputTestCases = [\n      {\n        name: 'boolean',\n        value: true,\n      },\n      {\n        name: 'number',\n        value: 123,\n      },\n      {\n        name: 'symbol',\n        value: Symbol(),\n      },\n    ]\n\n    unsupportedInputTestCases.forEach(({ name, value }) => {\n      it(`throw type error when using ${name} as initial value`, () => {\n        expect(() => proxySet(value as any)).toThrow(/not iterable/)\n      })\n    })\n  })\n\n  describe('clear set', () => {\n    initialValues.forEach(({ name, value }) => {\n      it(`clear proxySet of ${name}`, async () => {\n        const state = proxy({\n          set: proxySet<unknown>(value),\n        })\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          return (\n            <>\n              <div>size: {snap.set.size}</div>\n              <button onClick={() => state.set.clear()}>button</button>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        expect(screen.getByText(`size: ${state.set.size}`)).toBeInTheDocument()\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('size: 0')).toBeInTheDocument()\n      })\n    })\n  })\n\n  describe('add value', () => {\n    inputValues.forEach(({ name, value }) => {\n      it(`update size when adding ${name}`, async () => {\n        const state = proxy({\n          set: proxySet(),\n        })\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          return (\n            <>\n              <div>size: {snap.set.size}</div>\n              <button onClick={() => state.set.add(value)}>button</button>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        expect(screen.getByText('size: 0')).toBeInTheDocument()\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(screen.getByText('size: 1')).toBeInTheDocument()\n      })\n    })\n  })\n\n  describe('delete', () => {\n    initialValues.forEach(({ name, value }) => {\n      it(`support delete on ${name}`, async () => {\n        const state = proxy({\n          set: proxySet<unknown>(value),\n        })\n\n        // pick a random value from the set\n        const valueToDelete = Array.from(state.set)[\n          Math.floor(Math.random() * state.set.size)\n        ]\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          return (\n            <>\n              <div>size: {snap.set.size}</div>\n              <button onClick={() => state.set.delete(valueToDelete)}>\n                button\n              </button>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        expect(screen.getByText(`size: ${state.set.size}`)).toBeInTheDocument()\n\n        const expectedSizeAfterDelete =\n          state.set.size > 1 ? state.set.size - 1 : 0\n\n        fireEvent.click(screen.getByText('button'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`size: ${expectedSizeAfterDelete}`),\n        ).toBeInTheDocument()\n      })\n    })\n\n    inputValues.forEach(({ name, value }) => {\n      it(`return false when trying to delete non-existing value of type ${name}`, () => {\n        const set = proxySet()\n\n        expect(set.delete(value)).toBe(false)\n      })\n    })\n  })\n\n  describe('proxySet internal', () => {\n    it('should be sealed', () => {\n      expect(Object.isSealed(proxySet())).toBe(true)\n    })\n\n    it('should list only enumerable properties', () => {\n      const notEnumerableProps = ['data', 'size', 'toJSON']\n      expect(\n        Object.keys(proxySet()).some((k) => notEnumerableProps.includes(k)),\n      ).toBe(false)\n    })\n  })\n\n  describe('snapshot behavior', () => {\n    it('should error when trying to mutate a snapshot', () => {\n      const state = proxySet()\n      const snap = snapshot(state)\n\n      expect(() => snap.add('foo')).toThrow(\n        'Cannot perform mutations on a snapshot',\n      )\n      // @ts-expect-error - snapshot should not be able to mutate\n      expect(() => snap.delete('foo')).toThrow(\n        'Cannot perform mutations on a snapshot',\n      )\n      // @ts-expect-error - snapshot should not be able to mutate\n      expect(() => snap.clear()).toThrow(\n        'Cannot perform mutations on a snapshot',\n      )\n    })\n\n    it('should work with deleting a key', async () => {\n      const state = proxySet(['val1', 'val2'])\n      const snap1 = snapshot(state)\n      expect(snap1.has('val1')).toBe(true)\n      expect(snap1.has('val2')).toBe(true)\n      state.delete('val1')\n      const snap2 = snapshot(state)\n      expect(snap1.has('val1')).toBe(true)\n      expect(snap1.has('val2')).toBe(true)\n      expect(snap2.has('val1')).toBe(false)\n      expect(snap2.has('val2')).toBe(true)\n    })\n  })\n\n  describe('ui updates - useSnapshot', async () => {\n    it('should update ui when calling has before and after setting anddeleting a value', async () => {\n      const state = proxySet()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has value: {`${snap.has('value')}`}</p>\n            <button\n              onClick={() => {\n                state.add('value')\n                state.add('value2')\n              }}\n            >\n              add value\n            </button>\n            <button onClick={() => state.delete('value')}>delete value</button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('add value'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete value'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling has before and after settiing and deleting multiple values', async () => {\n      const state = proxySet()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has value: {`${snap.has('value')}`}</p>\n            <p>has value2: {`${snap.has('value2')}`}</p>\n            <button\n              onClick={() => {\n                state.add('value')\n                state.add('value2')\n              }}\n            >\n              add values\n            </button>\n            <button\n              onClick={() => {\n                state.delete('value')\n                state.delete('value2')\n              }}\n            >\n              delete values\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('add values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: true')).toBeInTheDocument()\n      expect(screen.getByText('has value2: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling has before and after settiing multiple values and deleting a single one (first item)', async () => {\n      const state = proxySet()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has value: {`${snap.has('value')}`}</p>\n            <p>has value2: {`${snap.has('value2')}`}</p>\n            <button\n              onClick={() => {\n                state.add('value')\n                state.add('value2')\n              }}\n            >\n              add values\n            </button>\n            <button\n              onClick={() => {\n                state.delete('value')\n              }}\n            >\n              delete values\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('add values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: true')).toBeInTheDocument()\n      expect(screen.getByText('has value2: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: true')).toBeInTheDocument()\n    })\n\n    it('should update ui when calling has before and after settiing multiple values and deleting a single one (second item)', async () => {\n      const state = proxySet()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has value: {`${snap.has('value')}`}</p>\n            <p>has value2: {`${snap.has('value2')}`}</p>\n            <button\n              onClick={() => {\n                state.add('value')\n                state.add('value2')\n              }}\n            >\n              add values\n            </button>\n            <button\n              onClick={() => {\n                state.delete('value2')\n              }}\n            >\n              delete values\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('add values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: true')).toBeInTheDocument()\n      expect(screen.getByText('has value2: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('delete values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: true')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n    })\n\n    it('should update ui when clearing the set', async () => {\n      const state = proxySet()\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        return (\n          <>\n            <p>has value: {`${snap.has('value')}`}</p>\n            <p>has value2: {`${snap.has('value2')}`}</p>\n            <button\n              onClick={() => {\n                state.add('value')\n                state.add('value2')\n              }}\n            >\n              add values\n            </button>\n            <button\n              onClick={() => {\n                state.clear()\n              }}\n            >\n              clear set\n            </button>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('add values'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: true')).toBeInTheDocument()\n      expect(screen.getByText('has value2: true')).toBeInTheDocument()\n\n      fireEvent.click(screen.getByText('clear set'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('has value: false')).toBeInTheDocument()\n      expect(screen.getByText('has value2: false')).toBeInTheDocument()\n    })\n\n    it('should be reactive to changes when using values method', async () => {\n      const state = proxySet<number>()\n\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        const addItem = () => {\n          const item = 1\n          state.add(item)\n        }\n\n        return (\n          <>\n            <button onClick={addItem}>Add Item</button>\n            <ul>\n              {Array.from(snap.values()).map((setItem) => (\n                <li key={setItem}>{`${setItem}`}</li>\n              ))}\n            </ul>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      fireEvent.click(screen.getByText('Add Item'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('1')).toBeInTheDocument()\n    })\n\n    it('should be reactive to changes when using keys method', async () => {\n      const state = proxySet<number>()\n\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        const addItem = () => {\n          const item = 1\n          state.add(item)\n        }\n\n        return (\n          <>\n            <button onClick={addItem}>Add Item</button>\n            <ul>\n              {Array.from(snap.keys()).map((setKey) => (\n                <li key={setKey}>{`item key: ${setKey}`}</li>\n              ))}\n            </ul>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      fireEvent.click(screen.getByText('Add Item'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('item key: 1')).toBeInTheDocument()\n    })\n\n    it('should be reactive to changes when using entries method', async () => {\n      const state = proxySet<number>()\n\n      const TestComponent = () => {\n        const snap = useSnapshot(state)\n\n        const addItem = () => {\n          const item = 1\n          state.add(item)\n        }\n\n        return (\n          <>\n            <button onClick={addItem}>Add Item</button>\n            <ul>\n              {Array.from(snap.entries()).map(([setKey, setValue]) => (\n                <li key={setValue}>{`key: ${setKey}; value: ${setValue}`}</li>\n              ))}\n            </ul>\n          </>\n        )\n      }\n\n      render(\n        <StrictMode>\n          <TestComponent />\n        </StrictMode>,\n      )\n\n      fireEvent.click(screen.getByText('Add Item'))\n      await act(() => vi.advanceTimersByTimeAsync(0))\n      expect(screen.getByText('key: 1; value: 1')).toBeInTheDocument()\n    })\n  })\n\n  describe('ui updates - useSnapshot - iterator methods', () => {\n    const iteratorMethods = ['keys', 'values', 'entries'] as const\n    iteratorMethods.forEach((iteratorMethod) => {\n      it(`should be reactive to changes when using ${iteratorMethod} method`, async () => {\n        interface MapItem {\n          id: number\n          name: string\n        }\n        const state = proxySet<MapItem>()\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          const addItem = (id: number) => {\n            const item: MapItem = {\n              id,\n              name: `item ${id}`,\n            }\n            state.add(item)\n          }\n\n          const methods = {\n            entries: Array.from(snap.entries()).map(([item]) => (\n              <li\n                key={item.id}\n              >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n            )),\n            values: Array.from(snap.values()).map((item) => (\n              <li\n                key={item.id}\n              >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n            )),\n            keys: Array.from(snap.keys()).map((item) => {\n              return (\n                <li\n                  key={item.id}\n                >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n              )\n            }),\n          }\n\n          return (\n            <>\n              <button onClick={() => addItem(1)}>Add</button>\n              <ul>{methods[iteratorMethod]}</ul>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        fireEvent.click(screen.getByText('Add'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`item.name: item 1; item.id: 1`),\n        ).toBeInTheDocument()\n      })\n\n      it(`should be reactive to changes when using ${iteratorMethod} method when setting multiple values`, async () => {\n        interface MapItem {\n          id: number\n          name: string\n        }\n        const state = proxySet<MapItem>()\n\n        const TestComponent = () => {\n          const snap = useSnapshot(state)\n\n          const addItem = (id: number) => {\n            const item: MapItem = {\n              id,\n              name: `item ${id}`,\n            }\n            state.add(item)\n          }\n\n          return (\n            <>\n              <button\n                onClick={() => {\n                  addItem(1)\n                  addItem(2)\n                }}\n              >\n                Add\n              </button>\n              <ul>\n                {iteratorMethod === 'entries'\n                  ? Array.from(snap[iteratorMethod]()).map(([item]) => (\n                      <li\n                        key={item.id}\n                      >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n                    ))\n                  : iteratorMethod === 'values'\n                    ? Array.from(snap[iteratorMethod]()).map((item) => (\n                        <li\n                          key={item.id}\n                        >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n                      ))\n                    : Array.from(snap[iteratorMethod]()).map((item) => {\n                        return (\n                          <li\n                            key={item.id}\n                          >{`item.name: ${item.name}; item.id: ${item.id}`}</li>\n                        )\n                      })}\n              </ul>\n            </>\n          )\n        }\n\n        render(\n          <StrictMode>\n            <TestComponent />\n          </StrictMode>,\n        )\n\n        fireEvent.click(screen.getByText('Add'))\n        await act(() => vi.advanceTimersByTimeAsync(0))\n        expect(\n          screen.getByText(`item.name: item 1; item.id: 1`),\n        ).toBeInTheDocument()\n        expect(\n          screen.getByText(`item.name: item 2; item.id: 2`),\n        ).toBeInTheDocument()\n      })\n    })\n  })\n\n  // https://github.com/tc39/proposal-set-methods\n  describe('proposal set methods', () => {\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/intersection#examples\n    it('.intersection', () => {\n      const odds = proxySet([1, 3, 5, 7, 9])\n      const squares = proxySet([1, 4, 9])\n      const result = odds.intersection(squares) // Set(2) { 1, 9 }\n      expect(result).toEqual(proxySet([1, 9]))\n    })\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/union#examples\n    it('.union', () => {\n      const evens = proxySet([2, 4, 6, 8])\n      const squares = proxySet([1, 4, 9])\n      const result = evens.union(squares) // Set(6) { 2, 4, 6, 8, 1, 9 }\n      expect(result).toEqual(proxySet([2, 4, 6, 8, 1, 9]))\n    })\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/difference#examples\n    it('.difference', () => {\n      const odds = proxySet([1, 3, 5, 7, 9])\n      const squares = proxySet([1, 4, 9])\n      const result = odds.difference(squares) // Set(3) { 3, 5, 7 }\n      expect(result).toEqual(proxySet([3, 5, 7]))\n    })\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference#examples\n    it('.symmetricDifference', () => {\n      const evens = proxySet([2, 4, 6, 8])\n      const squares = proxySet([1, 4, 9])\n      const result = evens.symmetricDifference(squares) // Set(5) { 2, 6, 8, 1, 9 }\n      expect(result).toEqual(proxySet([2, 6, 8, 1, 9]))\n    })\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSubsetOf#examples\n    describe('.isSubsetOf', () => {\n      it('The set of multiples of 4 (<20) is a subset of even numbers (<20)', () => {\n        const fours = proxySet([4, 8, 12, 16])\n        const evens = proxySet([2, 4, 6, 8, 10, 12, 14, 16, 18])\n        expect(fours.isSubsetOf(evens)).toBe(true) // true\n      })\n\n      it('The set of prime numbers (<20) is not a subset of all odd numbers (<20), because 2 is prime but not odd', () => {\n        const primes = proxySet([2, 3, 5, 7, 11, 13, 17, 19])\n        const odds = proxySet([3, 5, 7, 9, 11, 13, 15, 17, 19])\n        expect(primes.isSubsetOf(odds)).toBe(false) // false\n      })\n\n      it('Equivalent sets are subsets of each other', () => {\n        const set1 = proxySet([1, 2, 3])\n        const set2 = proxySet([1, 2, 3])\n        expect(set1.isSubsetOf(set2)).toBe(true) // true\n        expect(set2.isSubsetOf(set1)).toBe(true) // true\n      })\n    })\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSupersetOf#examples\n    describe('.isSupersetOf', () => {\n      it('The set of even numbers (<20) is a superset of multiples of 4 (<20)', () => {\n        const evens = proxySet([2, 4, 6, 8, 10, 12, 14, 16, 18])\n        const fours = proxySet([4, 8, 12, 16])\n        expect(evens.isSupersetOf(fours)).toBe(true) // true\n      })\n\n      it('The set of all odd numbers (<20) is not a superset of prime numbers (<20), because 2 is prime but not odd', () => {\n        const primes = proxySet([2, 3, 5, 7, 11, 13, 17, 19])\n        const odds = proxySet([3, 5, 7, 9, 11, 13, 15, 17, 19])\n        expect(odds.isSupersetOf(primes)).toBe(false) // false\n      })\n\n      it('Equivalent sets are supersets of each other', () => {\n        const set1 = proxySet([1, 2, 3])\n        const set2 = proxySet([1, 2, 3])\n        expect(set1.isSupersetOf(set2)).toBe(true) // true\n        expect(set2.isSupersetOf(set1)).toBe(true) // true\n      })\n    })\n\n    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isDisjointFrom#examples\n    describe('.isDisjointFrom', () => {\n      it('The set of perfect squares (<20) is disjoint from the set of prime numbers (<20)', () => {\n        // , because a perfect square is by definition decomposable into the product of two integers, while 1 is also not considered a prime number\n        const primes = proxySet([2, 3, 5, 7, 11, 13, 17, 19])\n        const squares = proxySet([1, 4, 9, 16])\n        expect(primes.isDisjointFrom(squares)).toBe(true) // true\n      })\n\n      it('The set of perfect squares (<20) is not disjoint from the set of composite numbers (<20)', () => {\n        // , because all non-1 perfect squares are by definition composite numbers\n        const composites = proxySet([4, 6, 8, 9, 10, 12, 14, 15, 16, 18])\n        const squares = proxySet([1, 4, 9, 16])\n        expect(composites.isDisjointFrom(squares)).toBe(false) // false\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "tests/ref.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport type { ReactElement } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, ref, snapshot, subscribe, useSnapshot } from 'valtio'\nimport { useCommitCount } from './utils'\n\ndescribe('ref', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('should trigger re-render setting objects with ref wrapper', async () => {\n    const obj = proxy({ nested: ref({ count: 0 }) })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>\n            count: {snap.nested.count} ({useCommitCount(1)})\n          </div>\n          <button onClick={() => (obj.nested = ref({ count: 0 }))}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <>\n        <Counter />\n      </>,\n    )\n\n    expect(screen.getByText('count: 0 (1)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 0 (2)')).toBeInTheDocument()\n  })\n\n  it('should not track object wrapped in ref assigned to proxy state', async () => {\n    const obj = proxy<{ ui: ReactElement | null }>({ ui: null })\n\n    const Component = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          {snap.ui || <span>original</span>}\n          <button onClick={() => (obj.ui = ref(<span>replace</span>))}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Component />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('original')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('replace')).toBeInTheDocument()\n  })\n\n  it('should not trigger re-render when mutating object wrapped in ref', async () => {\n    const obj = proxy({ nested: ref({ count: 0 }) })\n\n    const Counter = () => {\n      const snap = useSnapshot(obj)\n      return (\n        <>\n          <div>count: {snap.nested.count}</div>\n          <button onClick={() => ++obj.nested.count}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n  })\n\n  it('should not update snapshot or notify subscription when mutating proxy wrapped in ref', async () => {\n    const obj = proxy({ nested: ref(proxy({ count: 0 })) })\n\n    const snap1 = snapshot(obj)\n    ++obj.nested.count\n    const snap2 = snapshot(obj)\n    expect(snap2).toBe(snap1)\n\n    const callback = vi.fn()\n    subscribe(obj, callback)\n    ++obj.nested.count\n    expect(callback).not.toBeCalled()\n  })\n})\n"
  },
  {
    "path": "tests/setup.ts",
    "content": "import '@testing-library/jest-dom/vitest'\n"
  },
  {
    "path": "tests/snapshot.test.ts",
    "content": "import { createProxy, getUntracked } from 'proxy-compare'\nimport { describe, expect, expectTypeOf, it } from 'vitest'\nimport { proxy, snapshot } from 'valtio'\nimport type { Snapshot } from 'valtio'\n\ndescribe('snapshot', () => {\n  it('should return correct snapshots without subscribe', async () => {\n    const child = proxy({ count: 0 })\n    const state = proxy({ child })\n\n    expect(snapshot(state)).toEqual({ child: { count: 0 } })\n\n    ++child.count\n    expect(snapshot(state)).toEqual({ child: { count: 1 } })\n  })\n\n  it('should not change snapshot with assigning same object', async () => {\n    const obj = {}\n    const state = proxy({ obj })\n\n    const snap1 = snapshot(state)\n    state.obj = obj\n    const snap2 = snapshot(state)\n    expect(snap1).toBe(snap2)\n  })\n\n  it('should make the snapshot immutable', () => {\n    const state = proxy<{ foo: number; bar?: string }>({ foo: 1 })\n    const snap = snapshot(state)\n\n    // Overwriting existing property\n    expect(() => {\n      ;(snap as typeof state).foo = 100\n    }).toThrow()\n\n    // Extension (adding new property)\n    expect(() => {\n      ;(snap as typeof state).bar = 'hello'\n    }).toThrow()\n\n    // Note: The current implementation does not prevent property removal.\n    // Do not add a test for this unless we come up with an implementation that\n    // supports it.\n    // See https://github.com/pmndrs/valtio/issues/749\n  })\n\n  it('should not cause proxy-compare to copy', async () => {\n    const state = proxy({ foo: 1 })\n    const snap1 = snapshot(state)\n    // Ensure configurable is true, otherwise proxy-compare will copy the object\n    // so that its Proxy.get trap can work, and we don't want that perf overhead.\n    expect(Object.getOwnPropertyDescriptor(snap1, 'foo')).toEqual({\n      configurable: true,\n      enumerable: true,\n      value: 1,\n      writable: false,\n    })\n    // Technically getUntracked is smart enough to not return the copy, so this\n    // assertion doesn't strictly mean we avoided the copy\n    const cmp = createProxy(snap1, new WeakMap())\n    expect(getUntracked(cmp)).toBe(snap1)\n  })\n\n  it('should create a new proxy from a snapshot', async () => {\n    const state = proxy({ c: 0 })\n    const snap1 = snapshot(state)\n    const state2 = proxy(snap1)\n    expect(state2.c).toBe(0)\n  })\n\n  it('should not change snapshot with modifying the original proxy', async () => {\n    const state = proxy({ obj1: {}, obj2: { nested: { count: 1 } } })\n    const snap1 = snapshot(state)\n    expect(snap1.obj1).toBeDefined()\n    state.obj2.nested.count++\n    const snap2 = snapshot(state)\n    expect(snap1.obj2.nested.count).toBe(1)\n    expect(snap2.obj2.nested.count).toBe(2)\n  })\n\n  it('should return stable nested snapshot object', async () => {\n    const state = proxy({ count: 0, obj: {} })\n    const snap1 = snapshot(state)\n    state.count++\n    const snap2 = snapshot(state)\n    expect(snap2.obj).toBe(snap1.obj)\n  })\n\n  describe('snapshot typings', () => {\n    it('converts object properties to readonly', () => {\n      type A = Snapshot<{\n        string: string\n        number: number\n        null: null\n        undefined: undefined\n        bool: boolean\n        someFunction(): number\n        ref: { x: unknown } & { $$valtioSnapshot: { x: unknown } }\n      }>\n      type B = {\n        readonly string: string\n        readonly number: number\n        readonly null: null\n        readonly undefined: undefined\n        readonly bool: boolean\n        readonly someFunction: () => number\n        readonly ref: { x: unknown }\n      }\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n\n    it('converts arrays to readonly arrays', () => {\n      type A = Snapshot<number[]>\n      type B = readonly number[]\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n\n    it('keeps builtin objects from SnapshotIgnore as-is', () => {\n      type A = Snapshot<{\n        date: Date\n        map: Map<string, unknown>\n        set: Set<string>\n        regexp: RegExp\n        error: Error\n        weakMap: WeakMap<any, any>\n        weakSet: WeakSet<any>\n      }>\n      type B = {\n        readonly date: Date\n        readonly map: Map<string, unknown>\n        readonly set: Set<string>\n        readonly regexp: RegExp\n        readonly error: Error\n        readonly weakMap: WeakMap<any, any>\n        readonly weakSet: WeakSet<any>\n      }\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n\n    it('converts collections to readonly', () => {\n      type A = Snapshot<{ key: string }[]>\n      type B = readonly { readonly key: string }[]\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n\n    it('converts object properties to readonly recursively', () => {\n      type A = Snapshot<{\n        prevPage: number | null\n        nextPage: number | null\n        rows: number\n        items: {\n          title: string\n          details: string | null\n          createdAt: Date\n          updatedAt: Date\n        }[]\n      }>\n      type B = {\n        readonly prevPage: number | null\n        readonly nextPage: number | null\n        readonly rows: number\n        readonly items: readonly {\n          readonly title: string\n          readonly details: string | null\n          readonly createdAt: Date\n          readonly updatedAt: Date\n        }[]\n      }\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n\n    it('turns class fields to readonly', () => {\n      class User {\n        firstName!: string\n        lastName!: string\n        role!: string\n\n        hasRole(role: string): boolean {\n          return this.role === role\n        }\n      }\n\n      type A = Snapshot<typeof user>\n      type B = {\n        readonly firstName: string\n        readonly lastName: string\n        readonly role: string\n        readonly hasRole: (role: string) => boolean\n      }\n\n      const user = new User()\n      expect(user).toBeDefined()\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n\n    it('ignores primitive types that have been branded/tagged', () => {\n      const symbolTag = Symbol()\n\n      type A = Snapshot<{\n        brandedWithStringKey: string & { __brand: 'Brand' }\n        brandedWithSymbolKey: number & { [symbolTag]: 'Tag' }\n      }>\n      type B = {\n        readonly brandedWithStringKey: string & { __brand: 'Brand' }\n        readonly brandedWithSymbolKey: number & { [symbolTag]: 'Tag' }\n      }\n\n      expectTypeOf<A>().toEqualTypeOf<B>()\n    })\n  })\n})\n"
  },
  {
    "path": "tests/subscribe.test.tsx",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy, ref, snapshot, subscribe, unstable_enableOp } from 'valtio'\nimport { subscribeKey } from 'valtio/utils'\n\ndescribe('subscribe', () => {\n  const consoleWarn = console.warn\n\n  beforeEach(() => {\n    console.warn = vi.fn((message: string) => {\n      if (message === 'Please use proxy object') {\n        return\n      }\n      consoleWarn(message)\n    })\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    console.warn = consoleWarn\n    vi.useRealTimers()\n  })\n\n  it('should call subscription', async () => {\n    const obj = proxy({ count: 0 })\n    const handler = vi.fn()\n\n    subscribe(obj, handler)\n\n    obj.count += 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(1)\n  })\n\n  it('should be able to unsubscribe', async () => {\n    const obj = proxy({ count: 0 })\n    const handler = vi.fn()\n\n    const unsubscribe = subscribe(obj, handler)\n    unsubscribe()\n\n    obj.count += 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(0)\n  })\n\n  it('should be able to unsubscribe from a subscriber', async () => {\n    const obj = proxy({ count: 0 })\n    const handler = vi.fn()\n\n    subscribe(obj, () => {\n      unsubscribeB()\n    })\n\n    const unsubscribeB = subscribe(obj, handler)\n\n    obj.count += 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(0)\n  })\n\n  it('should call subscription of object property', async () => {\n    const obj = proxy({ nested: { count: 0 } })\n    const handler = vi.fn()\n\n    subscribe(obj.nested, handler)\n\n    obj.nested.count += 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(1)\n  })\n\n  it('should thow if subscribing to primitive property', async () => {\n    const obj = proxy({ count: 0 })\n    const handler = vi.fn()\n\n    expect(() => subscribe(obj.count as any, handler)).toThrow()\n  })\n\n  it('should not re-run subscription if no change', async () => {\n    const obj = proxy({ count: 0 })\n    const handler = vi.fn()\n\n    subscribe(obj, handler)\n\n    obj.count = 0\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(0)\n  })\n\n  it('should not cause infinite loop', async () => {\n    const obj = proxy({ count: 0 })\n    const handler = vi.fn(() => {\n      // Reset count if above 5\n      if (obj.count > 5) {\n        obj.count = 0\n      }\n    })\n\n    subscribe(obj, handler)\n\n    obj.count = 10\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(2)\n    expect(obj.count).toBe(0)\n  })\n\n  it('should batch updates', async () => {\n    const obj = proxy({ count1: 0, count2: 0 })\n    const handler = vi.fn()\n\n    subscribe(obj, handler)\n\n    obj.count1 += 1\n    obj.count2 += 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(1)\n  })\n\n  it('should not call subscription for objects wrapped in ref', async () => {\n    const obj = proxy({ nested: ref({ count: 0 }) })\n    const handler = vi.fn()\n\n    subscribe(obj, handler)\n\n    obj.nested.count += 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(0)\n  })\n\n  it('should not notify with assigning same object', async () => {\n    const obj = {}\n    const state = proxy({ obj })\n\n    const handler = vi.fn()\n    subscribe(state, handler)\n\n    state.obj = obj\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(0)\n  })\n})\n\ndescribe('subscribeKey', () => {\n  const consoleWarn = console.warn\n\n  beforeEach(() => {\n    console.warn = vi.fn((message: string) => {\n      if (message === 'Please use proxy object') {\n        return\n      }\n      consoleWarn(message)\n    })\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    console.warn = consoleWarn\n    vi.useRealTimers()\n  })\n\n  it('should call subscription', async () => {\n    const obj = proxy({ count1: 0, count2: 0 })\n    const handler1 = vi.fn()\n    const handler2 = vi.fn()\n\n    subscribeKey(obj, 'count1', handler1)\n    subscribeKey(obj, 'count2', handler2)\n\n    obj.count1 += 10\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler1).toBeCalledTimes(1)\n    expect(handler1).lastCalledWith(10)\n    expect(handler2).toBeCalledTimes(0)\n\n    obj.count2 += 20\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler1).toBeCalledTimes(1)\n    expect(handler2).toBeCalledTimes(1)\n    expect(handler2).lastCalledWith(20)\n  })\n\n  it('snapshot changed if subscription after delete nested property', async () => {\n    const obj = proxy({ s: { a: 1 } } as any)\n    const snapshot1 = snapshot(obj)\n    delete obj.s.a\n    subscribe(obj, () => {})\n    const snapshot2 = snapshot(obj)\n    expect(snapshot1).not.toEqual(snapshot2)\n  })\n})\n\ndescribe('subscribe with op', () => {\n  const consoleWarn = console.warn\n\n  beforeEach(() => {\n    unstable_enableOp(true)\n    console.warn = vi.fn((message: string) => {\n      if (message === 'Please use proxy object') {\n        return\n      }\n      consoleWarn(message)\n    })\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    console.warn = consoleWarn\n    vi.useRealTimers()\n    unstable_enableOp(false)\n  })\n\n  it('should notify ops', async () => {\n    const obj = proxy<{ count1: number; count2?: number }>({\n      count1: 0,\n      count2: 0,\n    })\n    const handler = vi.fn()\n\n    subscribe(obj, handler)\n\n    obj.count1 += 1\n    obj.count2 = 2\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(1)\n    expect(handler).lastCalledWith([\n      ['set', ['count1'], 1, 0],\n      ['set', ['count2'], 2, 0],\n    ])\n\n    delete obj.count2\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(2)\n    expect(handler).lastCalledWith([['delete', ['count2'], 2]])\n  })\n\n  it('should notify nested ops', async () => {\n    const obj = proxy<{ nested: { count?: number } }>({\n      nested: { count: 0 },\n    })\n    const handler = vi.fn()\n\n    subscribe(obj, handler)\n\n    obj.nested.count = 1\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(1)\n    expect(handler).lastCalledWith([['set', ['nested', 'count'], 1, 0]])\n\n    delete obj.nested.count\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(handler).toBeCalledTimes(2)\n    expect(handler).lastCalledWith([['delete', ['nested', 'count'], 1]])\n  })\n})\n"
  },
  {
    "path": "tests/useProxy.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy } from 'valtio'\nimport { useProxy } from 'valtio/react/utils'\n\ndescribe('useProxy', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('should read and mutate with the same reference', async () => {\n    const state = proxy({ count: 0 })\n\n    const Counter = () => {\n      const store = useProxy(state)\n      return (\n        <>\n          <div>count: {store.count}</div>\n          {/* eslint-disable-next-line react-hooks/immutability */}\n          <button onClick={() => ++store.count}>increment</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('increment'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('should update nested object properties', async () => {\n    const state = proxy({\n      user: { name: 'Alice', age: 20 },\n    })\n\n    const Profile = () => {\n      const store = useProxy(state)\n      return (\n        <>\n          <div>\n            {store.user.name} ({store.user.age})\n          </div>\n          {/* eslint-disable-next-line react-hooks/immutability */}\n          <button onClick={() => (store.user.name = 'Bob')}>rename</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Profile />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('Alice (20)')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('rename'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('Bob (20)')).toBeInTheDocument()\n  })\n\n  it('should handle multiple mutations in one handler', async () => {\n    const state = proxy({ firstName: 'John', lastName: 'Doe' })\n\n    const Form = () => {\n      const store = useProxy(state)\n      return (\n        <>\n          <div>\n            {store.firstName} {store.lastName}\n          </div>\n          <button\n            onClick={() => {\n              // eslint-disable-next-line react-hooks/immutability\n              store.firstName = 'Jane'\n              store.lastName = 'Smith'\n            }}\n          >\n            update\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Form />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('John Doe')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('update'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('Jane Smith')).toBeInTheDocument()\n  })\n\n  it('should work with sync option', async () => {\n    const state = proxy({ count: 0 })\n\n    const Counter = () => {\n      const store = useProxy(state, { sync: true })\n      return (\n        <>\n          <div>count: {store.count}</div>\n          {/* eslint-disable-next-line react-hooks/immutability */}\n          <button onClick={() => ++store.count}>increment</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('increment'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/utils.tsx",
    "content": "import { useEffect, useRef } from 'react'\n\nexport function sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => {\n    setTimeout(resolve, ms)\n  })\n}\n\nexport function useCommitCount(initialCount = 0): number {\n  const commitCountRef = useRef(initialCount)\n  useEffect(() => {\n    commitCountRef.current += 1\n  })\n  // eslint-disable-next-line react-hooks/refs\n  return commitCountRef.current\n}\n"
  },
  {
    "path": "tests/watch.test.tsx",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxy } from 'valtio'\nimport { watch } from 'valtio/utils'\nimport { sleep } from './utils'\n\ndescribe('watch', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n  })\n\n  it('should re-run for individual proxy updates', async () => {\n    const reference = proxy({ value: 'Example' })\n\n    const callback = vi.fn()\n\n    watch((get) => {\n      get(reference)\n      callback()\n    })\n\n    expect(callback).toBeCalledTimes(1)\n\n    reference.value = 'Update'\n    await vi.advanceTimersByTimeAsync(0)\n    expect(callback).toBeCalledTimes(2)\n  })\n\n  it('should re-run for multiple proxy updates', async () => {\n    const A = proxy({ value: 'A' })\n    const B = proxy({ value: 'B' })\n\n    const callback = vi.fn()\n\n    watch((get) => {\n      get(A)\n      get(B)\n      callback()\n    })\n\n    expect(callback).toBeCalledTimes(1)\n\n    A.value = 'B'\n    await vi.advanceTimersByTimeAsync(0)\n    expect(callback).toBeCalledTimes(2)\n\n    B.value = 'C'\n    await vi.advanceTimersByTimeAsync(0)\n    expect(callback).toBeCalledTimes(3)\n  })\n\n  it('should cleanup when state updates', async () => {\n    const reference = proxy({ value: 'Example' })\n\n    const callback = vi.fn()\n\n    watch((get) => {\n      get(reference)\n\n      return () => {\n        callback()\n      }\n    })\n\n    expect(callback).toBeCalledTimes(0)\n\n    reference.value = 'Update'\n    await vi.advanceTimersByTimeAsync(0)\n    expect(callback).toBeCalledTimes(1)\n  })\n\n  it('should cleanup when stopped', () => {\n    const callback = vi.fn()\n\n    const stop = watch(() => callback)\n\n    expect(callback).toBeCalledTimes(0)\n\n    stop()\n\n    expect(callback).toBeCalledTimes(1)\n  })\n\n  it('should cleanup internal effects when stopped', () => {\n    const callback = vi.fn()\n\n    const stop = watch(() => {\n      watch(() => {\n        watch(() => {\n          watch(() => {\n            watch(() => () => {\n              callback()\n            })\n          })\n        })\n      })\n    })\n\n    expect(callback).toBeCalledTimes(0)\n\n    stop()\n\n    expect(callback).toBeCalledTimes(1)\n  })\n\n  it('should not loop infinitely with sync (#382)', () => {\n    const reference = proxy({ value: 'Example' })\n\n    const callback = vi.fn()\n\n    watch(\n      (get) => {\n        get(reference)\n        callback()\n      },\n      { sync: true },\n    )\n\n    expect(callback).toBeCalledTimes(1)\n\n    reference.value = 'Update'\n    expect(callback).toBeCalledTimes(2)\n    expect(reference.value).toBe('Update')\n  })\n\n  it('should support promise watchers', async () => {\n    const reference = proxy({ value: 'Example' })\n\n    const callback = vi.fn()\n\n    watch(async (get) => {\n      await sleep(10000)\n      get(reference)\n      callback()\n    })\n\n    await vi.advanceTimersByTimeAsync(10000)\n    expect(callback).toBeCalledTimes(1)\n\n    reference.value = 'Update'\n    await vi.advanceTimersByTimeAsync(10000)\n    expect(callback).toBeCalledTimes(2)\n  })\n\n  it('should not subscribe if the watch is stopped before the promise completes', async () => {\n    const reference = proxy({ value: 'Example' })\n\n    const callback = vi.fn()\n\n    const stop = watch(async (get) => {\n      await sleep(10000)\n      get(reference)\n      callback()\n    })\n    stop()\n\n    await vi.advanceTimersByTimeAsync(10000)\n    expect(callback).toBeCalledTimes(1)\n\n    reference.value = 'Update'\n    await vi.advanceTimersByTimeAsync(10000)\n    expect(callback).toBeCalledTimes(1)\n  })\n})\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"strict\": true,\n    \"jsx\": \"react-jsx\",\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"skipLibCheck\": true /* FIXME remove this once vite fixes it */,\n    \"allowImportingTsExtensions\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"verbatimModuleSyntax\": true,\n    \"declaration\": true,\n    \"isolatedDeclarations\": true,\n    \"types\": [\"@testing-library/jest-dom\"],\n    \"noEmit\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"valtio\": [\"./src/index.ts\"],\n      \"valtio/*\": [\"./src/*.ts\"]\n    }\n  },\n  \"include\": [\"src/**/*\", \"tests/**/*\"],\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "vitest.config.mts",
    "content": "import { resolve } from 'path'\nimport { defineConfig } from 'vitest/config'\n\nexport default defineConfig({\n  resolve: {\n    alias: [\n      { find: /^valtio$/, replacement: resolve('./src/index.ts') },\n      { find: /^valtio(.*)$/, replacement: resolve('./src/$1.ts') },\n    ],\n  },\n  test: {\n    name: 'valtio',\n    // Keeping globals to true triggers React Testing Library's auto cleanup\n    // https://vitest.dev/guide/migration.html\n    globals: true,\n    setupFiles: ['tests/setup.ts'],\n    coverage: {\n      include: ['src/**/'],\n      reporter: ['text', 'json', 'html', 'text-summary'],\n      reportsDirectory: './coverage/',\n      provider: 'v8',\n    },\n    environment: 'jsdom',\n    dir: 'tests',\n    reporters: process.env.GITHUB_ACTIONS\n      ? ['default', 'github-actions']\n      : ['default'],\n  },\n})\n"
  },
  {
    "path": "website/.eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": "website/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\n"
  },
  {
    "path": "website/README.md",
    "content": "## Basic things to know before adding docs\n\n- Docs live in `docs/` folder.\n- Website lives in `website/` folder.\n- Docs are written in `mdx` format.\n- Docs filename shouldn't have spaces.\n- Website would generate title and other metadata from graymatter in the file.\n- You should be able to render condesandbox inside `mdx` files by simply adding the url for the same\n- Once you have a doc, you can add it to the sidebar section by adding it to the nav in `getDocsNav` function inside `website/lib/mdx.ts`\n\n---\n\nThis is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nInstall the dependencies:\n\n```bash\npnpm install --ignore-workspace\n```\n\nRun the development server:\n\n```bash\npnpm run dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.\n\n[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.\n\nThe `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "website/_utils/file_helpers.ts",
    "content": "import fs from 'fs'\nimport path from 'path'\n\ntype fn = (...args: any[]) => any\n\nconst pipe =\n  (...fns: fn[]) =>\n  (x: any) =>\n    fns.reduce((v, f) => f(v), x)\n\nconst flattenArray = (input: any[]) =>\n  input.reduce(\n    (acc, item) => [...acc, ...(Array.isArray(item) ? item : [item])],\n    [],\n  )\n\nconst map = (fn: fn) => (input: string[]) => input.map(fn)\n\nconst walkDir = (fullPath: string) => {\n  return fs.statSync(fullPath).isFile()\n    ? fullPath\n    : getAllFilesRecursively(fullPath)\n}\n\nconst pathJoinPrefix = (prefix: string) => (extraPath: string) =>\n  path.join(prefix, extraPath)\n\nconst filterMDX = (files: string[]) =>\n  files.filter(\n    (file) => path.extname(file) === '.mdx' || path.extname(file) === '.md',\n  )\n\nconst getAllFilesRecursively: (folder: string) => string[] = (folder) =>\n  pipe(\n    fs.readdirSync,\n    map(pipe(pathJoinPrefix(folder), walkDir)),\n    flattenArray,\n    filterMDX,\n  )(folder)\n\nconst slugify = (str: string) => {\n  return (\n    str\n      .replace(/\\s+/g, '-') // Replace spaces with -\n      .replace(/[^\\w\\-]+/g, '') // Remove all non-word chars\n      .replace(/\\-\\-+/g, '-') // Replace multiple - with single -\n      // .replace(/([A-Z])/g, \"_$1\") // Replace uppercase with _ and lowercase\n      // .toLowerCase()\n      .replace(/^_+/, '') // Trim _ from start of text\n      .replace(/^-+/, '') // Trim - from start of text\n      .replace(/-+$/, '')\n  ) // Trim - from end of text\n}\n\nexport { getAllFilesRecursively, slugify }\n"
  },
  {
    "path": "website/_utils/index.ts",
    "content": "// we ignore this now cause we can't import fs in browser\n// and file_helpers uses fs\n// export * from \"./file_helpers\";\n\nexport function classNames(...classes: string[]) {\n  return classes.filter(Boolean).join(' ')\n}\n"
  },
  {
    "path": "website/components/LandingPage/AnimatedShapes.tsx",
    "content": "import { animated } from 'react-spring'\nimport { useFloatAnimation } from './useFloatAnimation'\n\nconst Box = () => {\n  const style = useFloatAnimation('float-mid')\n  return (\n    <animated.svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"top-left\"\n      style={style}\n    >\n      <g stroke=\"var(--theme-blue)\" className=\"three-d-box\">\n        <line\n          x1=\"101.834\"\n          y1=\"297.925\"\n          x2=\"192.834\"\n          y2=\"202.925\"\n          strokeWidth=\"6\"\n        />\n        <line\n          x1=\"299.834\"\n          y1=\"298.925\"\n          x2=\"390.834\"\n          y2=\"203.925\"\n          strokeWidth=\"6\"\n        />\n        <line\n          x1=\"301.834\"\n          y1=\"494.925\"\n          x2=\"392.834\"\n          y2=\"399.925\"\n          strokeWidth=\"6\"\n        />\n        <line\n          x1=\"104.834\"\n          y1=\"491.925\"\n          x2=\"195.834\"\n          y2=\"396.925\"\n          strokeWidth=\"6\"\n        />\n        <rect\n          x=\"103\"\n          y=\"297\"\n          width=\"200\"\n          height=\"197\"\n          fill=\"none\"\n          strokeWidth=\"6\"\n        />\n        <rect\n          x=\"192\"\n          y=\"204\"\n          width=\"200\"\n          height=\"197\"\n          fill=\"none\"\n          strokeWidth=\"6\"\n        />\n      </g>\n    </animated.svg>\n  )\n}\n\nconst Donut = () => {\n  const style = useFloatAnimation('float-hi', -2500)\n  return (\n    <animated.svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"top-left\"\n      style={style}\n    >\n      <circle\n        className=\"donut\"\n        cx=\"474.5\"\n        cy=\"350.5\"\n        r=\"12\"\n        strokeWidth=\"40\"\n        stroke=\"var(--theme-blue)\"\n      />\n    </animated.svg>\n  )\n}\n\nconst CenterText = () => {\n  const style = useFloatAnimation('float-mid', 1000)\n  return (\n    <svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"center valtio\"\n    >\n      <animated.path\n        d=\"M773.98 485.864H732.964L695.251 607.419H693.547L655.94 485.864H614.818L673.092 649.5H715.706L773.98 485.864ZM825.067 652.803C850.742 652.803 866.083 640.764 873.114 627.021H874.393V649.5H911.467V539.983C911.467 496.73 876.204 483.733 844.989 483.733C810.579 483.733 784.158 499.074 775.636 528.903L811.644 534.017C815.479 522.831 826.346 513.243 845.202 513.243C863.1 513.243 872.901 522.405 872.901 538.491V539.131C872.901 550.21 861.289 550.743 832.418 553.832C800.671 557.241 770.309 566.723 770.309 603.584C770.309 635.757 793.853 652.803 825.067 652.803ZM835.082 624.464C818.995 624.464 807.489 617.114 807.489 602.945C807.489 588.136 820.38 581.957 837.638 579.507C847.759 578.122 868.001 575.565 873.008 571.517V590.8C873.008 609.017 858.306 624.464 835.082 624.464ZM970.81 431.318H932.245V649.5H970.81V431.318ZM1074.18 485.864H1041.9V446.659H1003.33V485.864H980.11V515.693H1003.33V606.673C1003.12 637.462 1025.49 652.589 1054.47 651.737C1065.44 651.418 1073.01 649.287 1077.16 647.902L1070.66 617.753C1068.53 618.286 1064.17 619.244 1059.37 619.244C1049.68 619.244 1041.9 615.835 1041.9 600.281V515.693H1074.18V485.864ZM1088.43 649.5H1127V485.864H1088.43V649.5ZM1107.82 462.639C1120.07 462.639 1130.09 453.264 1130.09 441.759C1130.09 430.146 1120.07 420.771 1107.82 420.771C1095.46 420.771 1085.45 430.146 1085.45 441.759C1085.45 453.264 1095.46 462.639 1107.82 462.639ZM1220.03 652.696C1267.97 652.696 1298.44 618.925 1298.44 568.321C1298.44 517.611 1267.97 483.733 1220.03 483.733C1172.09 483.733 1141.62 517.611 1141.62 568.321C1141.62 618.925 1172.09 652.696 1220.03 652.696ZM1220.25 621.801C1193.72 621.801 1180.72 598.151 1180.72 568.214C1180.72 538.278 1193.72 514.308 1220.25 514.308C1246.35 514.308 1259.34 538.278 1259.34 568.214C1259.34 598.151 1246.35 621.801 1220.25 621.801Z\"\n        fill=\"var(--theme-blue)\"\n        style={style}\n      />\n    </svg>\n  )\n}\n\nconst Circle = () => {\n  const style = useFloatAnimation('float-hi', -2000)\n  return (\n    <svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"top-right\"\n    >\n      <animated.circle\n        className=\"fast-circle\"\n        cx=\"1338\"\n        cy=\"209\"\n        r=\"84\"\n        strokeWidth=\"6\"\n        stroke=\"var(--theme-blue)\"\n        style={style}\n      />\n    </svg>\n  )\n}\n\nconst DotGrid = () => {\n  const style = useFloatAnimation('float-hi', -1000)\n  return (\n    <animated.svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"top-right\"\n      style={style}\n    >\n      <g stroke=\"var(--theme-blue)\">\n        <g className=\"dot-grid\">\n          <circle cx=\"907.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"907.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"907.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1025.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1025.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1025.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1143.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1143.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1143.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1261.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1261.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1261.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1379.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1379.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1379.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1497.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1497.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1497.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1615.5\" cy=\"145.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1615.5\" cy=\"230.5\" r=\"3\" strokeWidth=\"15\" />\n          <circle cx=\"1615.5\" cy=\"315.5\" r=\"3\" strokeWidth=\"15\" />\n        </g>\n      </g>\n    </animated.svg>\n  )\n}\n\nconst ConcentricCircles = () => {\n  const style = useFloatAnimation('float-hi', -1000)\n  return (\n    <svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"bottom-left\"\n    >\n      <animated.g\n        stroke=\"var(--theme-blue)\"\n        className=\"concentric-circles\"\n        style={style}\n      >\n        <circle cx=\"-43\" cy=\"1272\" r=\"619\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"593\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"568\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"540\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"514\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"487\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"461\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"435\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"409\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"382\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"355\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"329\" strokeWidth=\"6\" />\n        <circle cx=\"-43\" cy=\"1272\" r=\"303\" strokeWidth=\"6\" />\n      </animated.g>\n    </svg>\n  )\n}\n\nconst Square = () => {\n  const style = useFloatAnimation('float-rotate-mid', -1000)\n  return (\n    <svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"bottom-right\"\n    >\n      <animated.rect\n        className=\"spinning-square\"\n        x=\"650\"\n        y=\"750\"\n        width=\"100\"\n        height=\"100\"\n        strokeWidth=\"6\"\n        stroke=\"var(--theme-blue)\"\n        style={style}\n      />\n    </svg>\n  )\n}\n\nconst Line1 = () => {\n  const style = useFloatAnimation('float-mid', 250)\n  return (\n    <animated.svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"bottom-right\"\n      style={style}\n    >\n      <line\n        className=\"line-1\"\n        x1=\"1275\"\n        y1=\"780\"\n        x2=\"1393\"\n        y2=\"780\"\n        strokeWidth=\"6\"\n        stroke=\"var(--theme-blue)\"\n      />\n    </animated.svg>\n  )\n}\n\nconst Line2 = () => {\n  const style = useFloatAnimation('float-mid', 500)\n  return (\n    <animated.svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"bottom-right\"\n      style={style}\n    >\n      <line\n        className=\"line-2\"\n        x1=\"1095\"\n        y1=\"839\"\n        x2=\"1685\"\n        y2=\"839\"\n        strokeWidth=\"6\"\n        stroke=\"var(--theme-blue)\"\n      />\n    </animated.svg>\n  )\n}\n\nconst Line3 = () => {\n  const style = useFloatAnimation('float-mid', 750)\n  return (\n    <animated.svg\n      viewBox=\"0 0 1920 1080\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"bottom-right\"\n      style={style}\n    >\n      <line\n        className=\"line-3\"\n        x1=\"1275\"\n        y1=\"898\"\n        x2=\"1518\"\n        y2=\"898\"\n        strokeWidth=\"6\"\n        stroke=\"var(--theme-blue)\"\n      />\n    </animated.svg>\n  )\n}\n\nexport const AnimatedShapes = () => {\n  return (\n    <>\n      <Box />\n      <Donut />\n      <DotGrid />\n      <Circle />\n      <CenterText />\n      <ConcentricCircles />\n      <Square />\n      <Line1 />\n      <Line2 />\n      <Line3 />\n    </>\n  )\n}\n"
  },
  {
    "path": "website/components/LandingPage/CodeExample.tsx",
    "content": "import { useSnapshot } from 'valtio'\nimport Highlight, { defaultProps } from 'prism-react-renderer'\nimport { GettingStarted } from './GettingStarted'\nimport { state, incDuration, decDuration } from './state'\n\nconst exampleCode = (dur: number, count: number) => `\n  const state = proxy({\n    dur: ${dur},\n    count: ${count}\n  });\n  const incDur = () => {++state.dur};\n  const decDur = () => {--state.dur};\n  const incCount = () => {\n    ++state.count;\n    setTimeout(incCount, 100 * state.dur);\n  };\n\n  incCount();\n\n  const snap = useSnapshot(state)\n\n  return (\n    <div>\n      <h3>{snap.dur}</h3>\n      <button\n        disabled={snap.dur <= 1}\n        onClick={decDur}>\n        -\n      </button>\n      <button\n        disabled={snap.dur >= 10}\n        onClick={incDur}>\n        +\n      </button>\n    </div>\n  );\n`\n\nexport const CodeExample = () => {\n  const snap = useSnapshot(state)\n  return (\n    <div className=\"code-container\">\n      <div className=\"code-container-inner\">\n        <div className=\"duration-changer\">\n          <h3 className=\"text-xl font-bold\">{snap.dur}</h3>\n          <div className=\"button-container\">\n            <button\n              className=\"counter\"\n              disabled={snap.dur <= 1}\n              onClick={decDuration}\n            >\n              -\n            </button>\n            <button\n              className=\"counter\"\n              disabled={snap.dur >= 10}\n              onClick={incDuration}\n            >\n              +\n            </button>\n          </div>\n        </div>\n        <Highlight\n          {...defaultProps}\n          code={exampleCode(snap.dur, snap.count)}\n          language=\"jsx\"\n          theme={undefined}\n        >\n          {({ className, style, tokens, getLineProps, getTokenProps }) => (\n            <pre className={`${className} pre`} style={style}>\n              {tokens.map((line, i) => (\n                <div key={i} {...getLineProps({ line, key: i })}>\n                  {line.map((token, key) => (\n                    <span key={key} {...getTokenProps({ token, key })} />\n                  ))}\n                </div>\n              ))}\n            </pre>\n          )}\n        </Highlight>\n        <GettingStarted className=\"small-screen\" />\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/components/LandingPage/GettingStarted.tsx",
    "content": "import Link from 'next/link'\n\nexport const GettingStarted = ({ className }: { className: string }) => (\n  <div\n    className={`get-started inset-x-0 bottom-160 md:bottom-4 grid place-items-center gap-5 z-50 ${className}`}\n  >\n    <div className=\"\">\n      <Link href=\"/docs/introduction/getting-started\">\n        <a className=\"mt-14 bg-gray-900 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto dark:bg-sky-500 dark:highlight-white/20 dark:hover:bg-sky-400\">\n          Get started\n        </a>\n      </Link>\n    </div>\n    <div>\n      <p className=\"text-gray-700 font-medium pb-8\">Proxy state made simple</p>\n    </div>\n  </div>\n)\n"
  },
  {
    "path": "website/components/LandingPage/state.ts",
    "content": "import { proxy } from 'valtio'\n\nexport const state = proxy({\n  dur: 4,\n  count: 0,\n})\n\nexport const incDuration = () => {\n  ++state.dur\n}\n\nexport const decDuration = () => {\n  --state.dur\n}\n\nconst incrementCount = () => {\n  ++state.count\n  setTimeout(incrementCount, 100 * state.dur)\n}\n\nincrementCount()\n"
  },
  {
    "path": "website/components/LandingPage/useFloatAnimation.tsx",
    "content": "import { useSpring } from 'react-spring'\nimport { easeQuadInOut } from 'd3-ease'\nimport { useSnapshot } from 'valtio'\nimport { state } from './state'\n\ntype AnimationName = 'float' | 'float-mid' | 'float-rotate-mid' | 'float-hi'\ntype Animation = {\n  from: { transform: string }\n  to: { transform: string }\n}\nconst floatAnimations = new Map<AnimationName, Animation>()\n\nfloatAnimations.set('float', {\n  from: { transform: 'translate3d(0, 0px, 0)' },\n  to: { transform: 'translate3d(0, -20px, 0)' },\n})\nfloatAnimations.set('float-mid', {\n  from: { transform: 'translate3d(0, 0px, 0)' },\n  to: { transform: 'translate3d(0, -40px, 0)' },\n})\nfloatAnimations.set('float-rotate-mid', {\n  from: { transform: 'translate3d(0, -50px, 0) rotate(0deg)' },\n  to: { transform: 'translate3d(0, 0px, 0) rotate(180deg)' },\n})\nfloatAnimations.set('float-hi', {\n  from: { transform: 'translate3d(0, 0px, 0)' },\n  to: { transform: 'translate3d(0, -60px, 0)' },\n})\n\nconst clamp = (num: number, min: number, max: number) =>\n  Math.min(Math.max(num, min), max)\n\nexport function useFloatAnimation(\n  animation: AnimationName,\n  offset: number = 0,\n) {\n  const snap = useSnapshot(state)\n  const { from, to } = floatAnimations.get(animation) as Animation\n  return useSpring({\n    config: {\n      duration: clamp(snap.dur * 1000 + offset, 500, 10000),\n      easing: easeQuadInOut,\n    },\n    from,\n    to: async (next) => {\n      while (true) {\n        await next(to)\n        await next(from)\n      }\n    },\n  })\n}\n"
  },
  {
    "path": "website/components/MDXRenderer/MDXRenderer.tsx",
    "content": "import { getMDXComponent } from 'mdx-bundler/client'\nimport { useMemo } from 'react'\nimport { useCodesandboxTheme } from '~/hooks'\n\ninterface Props {\n  mdxSource: string\n  frontMatter: Dict\n}\n\nexport default function MDXRenderer({ mdxSource, frontMatter }: Props) {\n  useCodesandboxTheme(mdxSource)\n  const Component = useMemo(() => getMDXComponent(mdxSource), [mdxSource])\n  return (\n    <>\n      <main>\n        <Component />\n      </main>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/components/MDXRenderer/index.ts",
    "content": "export { default } from './MDXRenderer'\n"
  },
  {
    "path": "website/components/SEO/SEO.tsx",
    "content": "import Head from 'next/head'\n\ninterface SeoProps {\n  title?: string\n}\n\nconst defaultTitle = 'Valtio, makes proxy-state simple for React and Vanilla'\n\nexport default function SEO({ title }: SeoProps) {\n  return (\n    <Head>\n      <title>\n        {title ? title.concat(' — ') : ''} {defaultTitle}\n      </title>\n    </Head>\n  )\n}\n"
  },
  {
    "path": "website/components/SEO/index.ts",
    "content": "export { default } from './SEO'\n"
  },
  {
    "path": "website/components/ToggleTheme/ToggleTheme.tsx",
    "content": "import { SunIcon, MoonIcon } from '@heroicons/react/outline'\nimport { useTheme } from '~/hooks'\n\nexport default function ToggleTheme() {\n  const { toggleMode } = useTheme()\n  return (\n    <button\n      type=\"button\"\n      aria-label=\"Toggle dark mode\"\n      className=\"group\"\n      onClick={toggleMode}\n    >\n      <SunIcon className=\"h-6 w-6 fill-gray-100 stroke-gray-500 transition group-hover:fill-gray-200 group-hover:stroke-gray-700 dark:hidden [@media(prefers-color-scheme:dark)]:fill-sky-50 [@media(prefers-color-scheme:dark)]:stroke-sky-500 [@media(prefers-color-scheme:dark)]:group-hover:fill-sky-50 [@media(prefers-color-scheme:dark)]:group-hover:stroke-sky-600\" />\n      <MoonIcon className=\"hidden h-6 w-6 fill-gray-700 stroke-gray-500 transition dark:block [@media(prefers-color-scheme:dark)]:group-hover:stroke-gray-400 [@media_not_(prefers-color-scheme:dark)]:fill-sky-400/10 [@media_not_(prefers-color-scheme:dark)]:stroke-sky-500\" />\n    </button>\n  )\n}\n"
  },
  {
    "path": "website/components/ToggleTheme/index.ts",
    "content": "export { default } from './ToggleTheme'\n"
  },
  {
    "path": "website/components/layouts/BasicLayout/BasicLayout.tsx",
    "content": "import React from 'react'\n\ninterface Props {}\nexport default function BasicLayout({\n  children,\n}: React.PropsWithChildren<Props>) {\n  return (\n    <>\n      <main className=\"max-w-3xl mx-auto relative z-20 pt-10 xl:max-w-none\">\n        {children}\n      </main>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/components/layouts/BasicLayout/index.ts",
    "content": "export { default } from './BasicLayout'\n"
  },
  {
    "path": "website/components/layouts/DocLayout/DocLayout.tsx",
    "content": "import React, { forwardRef, useRef } from 'react'\nimport { Dialog } from '@headlessui/react'\nimport Link from 'next/link'\nimport clsx from 'clsx'\nimport { Router, useRouter } from 'next/router'\nimport { useIsomorphicLayoutEffect } from '~/hooks'\nimport { Header } from '~/components/layouts'\nimport { createContext, useEffect, useState } from 'react'\n\ninterface NavItemProps extends Partial<Navigation> {\n  fallbackHref: string\n  isPublished?: boolean\n}\n\nconst NavItem = forwardRef<HTMLElement, React.PropsWithChildren<NavItemProps>>(\n  function NavItem(\n    { href, children, isActive, isPublished, fallbackHref },\n    ref,\n  ) {\n    return (\n      //@ts-ignore\n      <li ref={ref}>\n        <Link href={isPublished ? href! : fallbackHref}>\n          <a\n            className={clsx('block border-l pl-4 -ml-px font-normal text-md', {\n              'text-sky-500 border-current dark:text-sky-400 font-semibold':\n                isActive,\n              'border-transparent hover:border-gray-400 dark:hover:border-gray-500':\n                !isActive,\n              'text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300':\n                !isActive && isPublished,\n              'text-gray-400': !isActive && !isPublished,\n            })}\n          >\n            {children}\n          </a>\n        </Link>\n      </li>\n    )\n  },\n)\n\n/**\n * Find the nearst scrollable ancestor (or self if scrollable)\n *\n * Code adapted and simplified from the smoothscroll polyfill\n *\n *\n * @param {Element} el\n */\nfunction nearestScrollableContainer(el?: Element) {\n  if (!el) return document.body\n  /**\n   * indicates if an element can be scrolled\n   *\n   * @param {Node} el\n   */\n  function isScrollable(el: Element) {\n    const style = window.getComputedStyle(el)\n    const overflowX = style['overflowX']\n    const overflowY = style['overflowY']\n    const canScrollY = el.clientHeight < el.scrollHeight\n    const canScrollX = el.clientWidth < el.scrollWidth\n\n    const isScrollableY =\n      canScrollY && (overflowY === 'auto' || overflowY === 'scroll')\n    const isScrollableX =\n      canScrollX && (overflowX === 'auto' || overflowX === 'scroll')\n\n    return isScrollableY || isScrollableX\n  }\n\n  while (el !== document.body && isScrollable(el!) === false) {\n    // @ts-ignore\n    el = el.parentNode || el.host\n  }\n\n  return el\n}\n\nconst isNavigationRecord = (obj: object): obj is Record<string, Navigation[]> =>\n  typeof obj === 'object' && obj !== null && !Array.isArray(obj)\n\ninterface NavProps {\n  nav: Record<string, Navigation[]>\n  fallbackHref: string\n  mobile?: boolean\n}\n\nfunction Nav({\n  nav,\n  children,\n  fallbackHref,\n  mobile = false,\n}: React.PropsWithChildren<NavProps>) {\n  const router = useRouter()\n  const activeItemRef = useRef<HTMLElement | null>(null)\n  const previousActiveItemRef = useRef<HTMLElement | null>(null)\n  const scrollRef = useRef<HTMLElement | null>(null)\n\n  useIsomorphicLayoutEffect(() => {\n    function updatePreviousRef() {\n      previousActiveItemRef.current = activeItemRef.current\n    }\n\n    if (activeItemRef.current) {\n      if (activeItemRef.current === previousActiveItemRef.current) {\n        updatePreviousRef()\n        return\n      }\n\n      updatePreviousRef()\n\n      const scrollable = nearestScrollableContainer(\n        scrollRef.current ?? undefined,\n      )\n      if (!scrollable) return\n\n      const scrollRect = scrollable.getBoundingClientRect()\n      const activeItemRect = activeItemRef.current.getBoundingClientRect()\n\n      const top = activeItemRef.current.offsetTop\n      const bottom = top - scrollRect.height + activeItemRect.height\n\n      if (scrollable.scrollTop > top || scrollable.scrollTop < bottom) {\n        scrollable.scrollTop =\n          top - scrollRect.height / 2 + activeItemRect.height / 2\n      }\n    }\n  }, [router.pathname])\n\n  return (\n    <nav ref={scrollRef} id=\"nav\" className=\"lg:text-sm lg:leading-6 relative\">\n      <ul>\n        {children}\n        {nav &&\n          Object.keys(nav)\n            .map((category) => {\n              const items = nav[category]\n              const nestedMenu = isNavigationRecord(items)\n              if (nestedMenu) {\n                return (\n                  <li key={category} className=\"mt-12 lg:mt-8\">\n                    <h5 className=\"mb-8 lg:mb-3 font-medium text-gray-900 dark:text-gray-200 uppercase\">\n                      {category}\n                    </h5>\n                    <ul\n                      className={clsx(\n                        'block border-l pl-4 -ml-px',\n                        'space-y-6 lg:space-y-2 border-l border-gray-100',\n                        mobile\n                          ? 'dark:border-gray-700'\n                          : 'dark:border-gray-800',\n                      )}\n                    >\n                      {Object.keys(items).map((subcategory) => {\n                        const publishedSubItems = items[subcategory].filter(\n                          (item) => item.published !== false,\n                        )\n                        if (publishedSubItems.length === 0 && !fallbackHref)\n                          return null\n                        return (\n                          <li key={subcategory} className=\"mt-12 lg:mt-4\">\n                            <h6\n                              className={clsx(\n                                'mb-8 lg:mb-3 font-semibold uppercase',\n                                {\n                                  'text-gray-350 dark:text-gray-200':\n                                    publishedSubItems.length > 0,\n                                  'text-gray-400':\n                                    publishedSubItems.length === 0,\n                                },\n                              )}\n                            >\n                              {subcategory}\n                            </h6>\n                            <ul\n                              className={clsx(\n                                'space-y-6 lg:space-y-2 border-l border-gray-100',\n                                mobile\n                                  ? 'dark:border-gray-700'\n                                  : 'dark:border-gray-800',\n                              )}\n                            >\n                              {(fallbackHref\n                                ? items[subcategory]\n                                : publishedSubItems\n                              ).map((item, i) => {\n                                let isActive = item.match\n                                  ? item.match.test(router.asPath)\n                                  : item.href === router.asPath\n                                return (\n                                  <NavItem\n                                    key={i}\n                                    href={item.href!}\n                                    isActive={isActive}\n                                    ref={isActive ? activeItemRef : undefined}\n                                    isPublished={item.published !== false}\n                                    fallbackHref={fallbackHref}\n                                  >\n                                    {item.title}\n                                  </NavItem>\n                                )\n                              })}\n                            </ul>\n                          </li>\n                        )\n                      })}\n                    </ul>\n                  </li>\n                )\n              }\n              let publishedItems = items.filter(\n                (item) => item.published !== false,\n              )\n              if (publishedItems.length === 0 && !fallbackHref) return null\n              return (\n                <li key={category} className=\"mt-12 lg:mt-8\">\n                  <h5\n                    className={clsx('mb-8 lg:mb-3 font-medium', {\n                      'text-gray-900 dark:text-gray-200':\n                        publishedItems.length > 0,\n                      'text-gray-400': publishedItems.length === 0,\n                    })}\n                  >\n                    {category}\n                  </h5>\n                  <ul\n                    className={clsx(\n                      'space-y-6 lg:space-y-2 border-l border-gray-100',\n                      mobile ? 'dark:border-gray-700' : 'dark:border-gray-800',\n                    )}\n                  >\n                    {(fallbackHref ? nav[category] : publishedItems).map(\n                      (item, i) => {\n                        let isActive = item.match\n                          ? item.match.test(router.asPath)\n                          : item.href === router.asPath\n                        return (\n                          <NavItem\n                            key={i}\n                            href={item.href!}\n                            isActive={isActive}\n                            ref={isActive ? activeItemRef : undefined}\n                            isPublished={item.published !== false}\n                            fallbackHref={fallbackHref}\n                          >\n                            {item.title}\n                          </NavItem>\n                        )\n                      },\n                    )}\n                  </ul>\n                </li>\n              )\n            })\n            .filter(Boolean)}\n      </ul>\n    </nav>\n  )\n}\n\ninterface WrapperProps {\n  allowOverflow?: boolean\n}\n\nfunction Wrapper({\n  allowOverflow,\n  children,\n}: React.PropsWithChildren<WrapperProps>) {\n  return (\n    <div className={allowOverflow ? undefined : 'overflow-hidden'}>\n      {children}\n    </div>\n  )\n}\n\ninterface Props {\n  frontMatter: Dict\n  allowOverflow?: boolean\n  fallbackHref?: string\n  nav: Record<string, Navigation[]>\n}\n\ninterface ContextProps {\n  setNavIsOpen: (isOpen: boolean) => void\n  navIsOpen: boolean\n  nav: Record<string, Navigation[]>\n}\n\nexport const DocLayoutContext = createContext<ContextProps>({\n  setNavIsOpen: () => {},\n  navIsOpen: false,\n  nav: {},\n})\n\nexport default function DocLayout({\n  nav,\n  frontMatter,\n  children,\n  allowOverflow,\n  fallbackHref = '#',\n}: React.PropsWithChildren<Props>) {\n  let [navIsOpen, setNavIsOpen] = useState(false)\n  const router = useRouter()\n\n  useEffect(() => {\n    if (!navIsOpen) return\n    function handleRouteChange() {\n      setNavIsOpen(false)\n    }\n    Router.events.on('routeChangeComplete', handleRouteChange)\n    return () => {\n      Router.events.off('routeChangeComplete', handleRouteChange)\n    }\n  }, [navIsOpen])\n  let section =\n    frontMatter.section ||\n    Object.entries(\n      // @ts-ignore\n      nav ?? {},\n    ).find(([, items]) => {\n      if (isNavigationRecord(items)) {\n        return Object.entries(items).find(([_, subItems]) =>\n          subItems.some((item) => item.href === router.asPath),\n        )\n      }\n      return (items ?? []).find(\n        ({ href }: { href: string }) => href === router.asPath,\n      )\n    })?.[0]\n  return (\n    <>\n      <DocLayoutContext.Provider value={{ nav, navIsOpen, setNavIsOpen }}>\n        <Header\n          hasNav={true}\n          navIsOpen={navIsOpen}\n          onNavToggle={(isOpen: boolean) => setNavIsOpen(isOpen)}\n          title={frontMatter.title}\n          section={frontMatter.section}\n        />\n        <Wrapper allowOverflow={allowOverflow}>\n          <div className=\"max-w-[90rem] mx-auto px-4 sm:px-6 md:px-8\">\n            <div className=\"hidden lg:block fixed z-20 inset-0 top-[3.8125rem] left-[max(0px,calc(50%-45rem))] right-auto w-[19.5rem] pb-10 px-8 overflow-y-auto\">\n              <Nav nav={nav} fallbackHref={fallbackHref}></Nav>\n            </div>\n            <div className=\"lg:pl-[19.5rem]\">\n              <main\n                className={clsx(\n                  'prose prose-gray max-w-3xl mx-auto relative z-20 pt-10 xl:max-w-none dark:prose-invert dark:text-gray-400',\n                  // headings\n                  'prose-headings:scroll-mt-28 prose-headings:font-display prose-headings:font-normal lg:prose-headings:scroll-mt-[8.5rem]',\n                  // links\n                  'prose-a:font-semibold dark:prose-a:text-sky-400',\n                  // link underline\n                  'prose-a:no-underline prose-a:shadow-[inset_0_-2px_0_0_var(--tw-prose-background,#fff),inset_0_calc(-1*(var(--tw-prose-underline-size,4px)+2px))_0_0_var(--tw-prose-underline,theme(colors.sky.300))] hover:prose-a:[--tw-prose-underline-size:6px] dark:[--tw-prose-background:theme(colors.gray.900)] dark:prose-a:shadow-[inset_0_calc(-1*var(--tw-prose-underline-size,2px))_0_0_var(--tw-prose-underline,theme(colors.sky.800))] dark:hover:prose-a:[--tw-prose-underline-size:6px]',\n                  // pre\n                  'prose-pre:rounded-xl prose-pre:bg-gray-900 prose-pre:shadow-lg dark:prose-pre:bg-gray-800/60 dark:prose-pre:shadow-none dark:prose-pre:ring-1 dark:prose-pre:ring-gray-300/10',\n                  // hr\n                  'dark:prose-hr:border-gray-800',\n                )}\n              >\n                {children}\n              </main>\n            </div>\n          </div>\n        </Wrapper>\n        <Dialog\n          as=\"div\"\n          open={navIsOpen}\n          onClose={() => setNavIsOpen?.(false)}\n          className=\"fixed z-50 inset-0 overflow-y-auto lg:hidden\"\n        >\n          <Dialog.Overlay className=\"fixed inset-0 bg-black/20 backdrop-blur-sm dark:bg-gray-900/80\" />\n          <div className=\"relative bg-white w-80 max-w-[calc(100%-3rem)] p-6 dark:bg-gray-800\">\n            <button\n              type=\"button\"\n              onClick={() => setNavIsOpen?.(false)}\n              className=\"absolute z-10 top-5 right-5 w-8 h-8 flex items-center justify-center text-gray-500 hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300\"\n            >\n              <span className=\"sr-only\">Close navigation</span>\n              <svg viewBox=\"0 0 10 10\" className=\"w-2.5 h-2.5 overflow-visible\">\n                <path\n                  d=\"M0 0L10 10M10 0L0 10\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"2\"\n                  strokeLinecap=\"round\"\n                />\n              </svg>\n            </button>\n            <Nav nav={nav} fallbackHref={fallbackHref} mobile={true}></Nav>\n          </div>\n        </Dialog>\n      </DocLayoutContext.Provider>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/components/layouts/DocLayout/index.ts",
    "content": "export { default } from './DocLayout'\n"
  },
  {
    "path": "website/components/layouts/Header/Header.tsx",
    "content": "import Link from 'next/link'\nimport Router from 'next/router'\nimport { Dialog } from '@headlessui/react'\nimport React, { useEffect, useState } from 'react'\nimport clsx from 'clsx'\nimport SEO from '~/components/SEO'\nimport ToggleTheme from '~/components/ToggleTheme'\n\nexport function NavPopover({\n  display = 'md:hidden',\n  className = '',\n  ...props\n}) {\n  let [isOpen, setIsOpen] = useState(false)\n\n  useEffect(() => {\n    if (!isOpen) return\n    function handleRouteChange() {\n      setIsOpen(false)\n    }\n    Router.events.on('routeChangeComplete', handleRouteChange)\n    return () => {\n      Router.events.off('routeChangeComplete', handleRouteChange)\n    }\n  }, [isOpen])\n\n  return (\n    <div className={clsx(className, display)} {...props}>\n      <button\n        type=\"button\"\n        className=\"text-gray-500 w-8 h-8 flex items-center justify-center hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300\"\n        onClick={() => setIsOpen(true)}\n      >\n        <span className=\"sr-only\">Navigation</span>\n        <svg width=\"24\" height=\"24\" fill=\"none\" aria-hidden=\"true\">\n          <path\n            d=\"M12 6v.01M12 12v.01M12 18v.01M12 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm0 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm0 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"\n            stroke=\"currentColor\"\n            strokeWidth=\"1.5\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          />\n        </svg>\n      </button>\n      <Dialog\n        as=\"div\"\n        className={clsx('fixed z-50 inset-0', display)}\n        open={isOpen}\n        onClose={setIsOpen}\n      >\n        <Dialog.Overlay className=\"fixed inset-0 bg-black/20 backdrop-blur-sm dark:bg-gray-900/80\" />\n        <div className=\"fixed top-4 right-4 w-full max-w-xs bg-white rounded-lg shadow-lg p-6 text-base font-semibold text-gray-900 dark:bg-gray-800 dark:text-gray-400 dark:highlight-white/5\">\n          <button\n            type=\"button\"\n            className=\"absolute top-5 right-5 w-8 h-8 flex items-center justify-center text-gray-500 hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300\"\n            onClick={() => setIsOpen(false)}\n          >\n            <span className=\"sr-only\">Close navigation</span>\n            <svg\n              viewBox=\"0 0 10 10\"\n              className=\"w-2.5 h-2.5 overflow-visible\"\n              aria-hidden=\"true\"\n            >\n              <path\n                d=\"M0 0L10 10M10 0L0 10\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                strokeWidth=\"2\"\n                strokeLinecap=\"round\"\n              />\n            </svg>\n          </button>\n          <ul className=\"space-y-6\">\n            <NavItems />\n            <li>\n              <a\n                href=\"https://github.com/pmndrs/valtio\"\n                className=\"hover:text-sky-500 dark:hover:text-sky-400\"\n              >\n                GitHub\n              </a>\n            </li>\n          </ul>\n          <div className=\"mt-6 pt-6 border-t border-gray-200 dark:border-gray-200/10\">\n            <ToggleTheme />\n          </div>\n        </div>\n      </Dialog>\n    </div>\n  )\n}\n\nexport function NavItems() {\n  return (\n    <>\n      <li>\n        <Link href=\"/docs/introduction/getting-started\">\n          <a className=\"hover:text-sky-500 dark:hover:text-sky-400\">Docs</a>\n        </Link>\n      </li>\n    </>\n  )\n}\n\ninterface HeaderProps {\n  hasNav?: boolean\n  navIsOpen?: boolean\n  onNavToggle?: (isOpen: boolean) => void\n  title?: string\n  section?: string\n  subSection?: string\n}\n\nexport default function Header({\n  hasNav = false,\n  navIsOpen,\n  onNavToggle,\n  title,\n  section,\n  subSection,\n}: HeaderProps) {\n  let [isOpaque, setIsOpaque] = useState(false)\n\n  useEffect(() => {\n    let offset = 50\n    function onScroll() {\n      if (!isOpaque && window.scrollY > offset) {\n        setIsOpaque(true)\n      } else if (isOpaque && window.scrollY <= offset) {\n        setIsOpaque(false)\n      }\n    }\n    onScroll()\n    window.addEventListener('scroll', onScroll, { passive: true })\n    return () => {\n      window.removeEventListener('scroll', onScroll)\n    }\n  }, [isOpaque])\n\n  return (\n    <>\n      <SEO title={title} />\n      <div\n        className={clsx(\n          'sticky top-0 z-40 w-full backdrop-blur flex-none transition-colors duration-500 lg:z-50 lg:border-b lg:border-gray-900/10 dark:border-gray-50/[0.06]',\n          isOpaque\n            ? 'bg-white supports-backdrop-blur:bg-white/95 dark:bg-gray-900/75'\n            : 'bg-white/95 supports-backdrop-blur:bg-white/60 dark:bg-transparent',\n        )}\n      >\n        <div className=\"max-w-[90rem] mx-auto\">\n          <div\n            className={clsx(\n              'py-4 border-b border-gray-900/10 lg:px-8 lg:border-0 dark:border-gray-300/10',\n              hasNav ? 'mx-4 lg:mx-0' : 'px-4',\n            )}\n          >\n            <div className=\"relative flex items-center\">\n              <Link href=\"/\">\n                <a\n                  className=\"mr-3 flex-none overflow-hidden md:w-auto text-gray-900 dark:text-gray-50\"\n                  onContextMenu={(e) => {\n                    e.preventDefault()\n                    Router.push('/')\n                  }}\n                >\n                  Valtio\n                  <span className=\"sr-only\">Valtio home page</span>\n                </a>\n              </Link>\n              <div className=\"relative hidden lg:flex items-center ml-auto\">\n                <nav className=\"text-sm leading-6 font-semibold text-gray-700 dark:text-gray-200\">\n                  <ul className=\"flex space-x-8\">\n                    <NavItems />\n                  </ul>\n                </nav>\n                <div className=\"flex items-center border-l border-gray-200 ml-6 pl-6 dark:border-gray-800\">\n                  <ToggleTheme />\n                  <a\n                    href=\"https://github.com/pmndrs/valtio\"\n                    className=\"ml-6 block text-gray-400 hover:text-gray-500 dark:hover:text-gray-300\"\n                  >\n                    <span className=\"sr-only\">Valtio on GitHub</span>\n                    <svg\n                      viewBox=\"0 0 16 16\"\n                      className=\"w-5 h-5\"\n                      fill=\"currentColor\"\n                      aria-hidden=\"true\"\n                    >\n                      <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" />\n                    </svg>\n                  </a>\n                </div>\n              </div>\n              <NavPopover className=\"-my-1 ml-auto\" display=\"lg:hidden\" />\n            </div>\n          </div>\n          {hasNav && (\n            <div className=\"flex items-center p-4 border-b border-gray-900/10 lg:hidden dark:border-gray-50/[0.06]\">\n              <button\n                type=\"button\"\n                onClick={() => onNavToggle?.(!navIsOpen)}\n                className=\"text-gray-500 hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300\"\n              >\n                <span className=\"sr-only\">Navigation</span>\n                <svg width=\"24\" height=\"24\">\n                  <path\n                    d=\"M5 6h14M5 12h14M5 18h14\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"2\"\n                    strokeLinecap=\"round\"\n                  />\n                </svg>\n              </button>\n              {title && (\n                <ol className=\"ml-4 flex text-sm leading-6 whitespace-nowrap min-w-0\">\n                  {section && (\n                    <li className=\"flex items-center text-gray-900 dark:text-gray-200\">\n                      {section}\n                      <svg\n                        width=\"3\"\n                        height=\"6\"\n                        aria-hidden=\"true\"\n                        className=\"mx-3 overflow-visible text-gray-400\"\n                      >\n                        <path\n                          d=\"M0 0L3 3L0 6\"\n                          fill=\"none\"\n                          stroke=\"currentColor\"\n                          strokeWidth=\"1.5\"\n                          strokeLinecap=\"round\"\n                        />\n                      </svg>\n                    </li>\n                  )}\n                  {subSection && (\n                    <li className=\"flex items-center\">\n                      {subSection}\n                      <svg\n                        width=\"3\"\n                        height=\"6\"\n                        aria-hidden=\"true\"\n                        className=\"mx-3 overflow-visible text-gray-400\"\n                      >\n                        <path\n                          d=\"M0 0L3 3L0 6\"\n                          fill=\"none\"\n                          stroke=\"currentColor\"\n                          strokeWidth=\"1.5\"\n                          strokeLinecap=\"round\"\n                        />\n                      </svg>\n                    </li>\n                  )}\n                  <li className=\"font-semibold text-gray-900 truncate dark:text-gray-200\">\n                    {title}\n                  </li>\n                </ol>\n              )}\n            </div>\n          )}\n        </div>\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/components/layouts/Header/index.ts",
    "content": "export { default } from './Header'\n"
  },
  {
    "path": "website/components/layouts/index.ts",
    "content": "export { default as BasicLayout } from './BasicLayout'\nexport { default as DocLayout } from './DocLayout'\nexport { default as Header } from './Header'\n"
  },
  {
    "path": "website/hooks/index.ts",
    "content": "export * from './useIsomorphicLayoutEffect'\nexport * from './useTheme'\nexport * from './useCodesandboxTheme'\n"
  },
  {
    "path": "website/hooks/useCodesandboxTheme.ts",
    "content": "import { useCallback, useEffect } from 'react'\nimport { subscribe, snapshot } from 'valtio'\nimport { themeState } from '~/state'\n\nexport const useCodesandboxTheme = (mdxSource: string) => {\n  const updateCodesandboxEmbeds = useCallback(() => {\n    const isDarkMode = snapshot(themeState).isDarkMode\n    const codesandboxEmbeds = document.querySelectorAll(\n      '[src*=\"codesandbox.io/embed\"]',\n    )\n    codesandboxEmbeds.forEach((embed) => {\n      const frame = embed as HTMLIFrameElement\n      const url = new URL(frame.src)\n      const currentCodesandboxTheme = url.searchParams.get('theme')\n      const newCodesandboxTheme = isDarkMode ? 'dark' : 'light'\n      if (currentCodesandboxTheme === newCodesandboxTheme) return\n      url.searchParams.set('theme', newCodesandboxTheme)\n      frame.src = url.toString()\n    })\n  }, [])\n  useEffect(() => {\n    updateCodesandboxEmbeds()\n    return subscribe(themeState, updateCodesandboxEmbeds)\n  }, [mdxSource, updateCodesandboxEmbeds])\n}\n"
  },
  {
    "path": "website/hooks/useIsomorphicLayoutEffect.ts",
    "content": "import { useEffect, useLayoutEffect } from 'react'\n\nexport const useIsomorphicLayoutEffect =\n  typeof window !== 'undefined' ? useLayoutEffect : useEffect\n"
  },
  {
    "path": "website/hooks/useTheme.ts",
    "content": "import { useCallback, useEffect } from 'react'\nimport { themeState } from '~/state'\n\nexport const useTheme = () => {\n  const updateMode = useCallback(() => {\n    let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n    let isSystemDarkMode = darkModeMediaQuery.matches\n    let isDarkMode =\n      window.localStorage.isDarkMode === 'true' ||\n      (!('isDarkMode' in window.localStorage) && isSystemDarkMode)\n\n    themeState.isDarkMode = isDarkMode\n    if (isDarkMode) {\n      document.documentElement.classList.add('dark')\n    } else {\n      document.documentElement.classList.remove('dark')\n    }\n\n    if (isDarkMode === isSystemDarkMode) {\n      delete window.localStorage.isDarkMode\n    }\n  }, [])\n\n  const disableTransitionsTemporarily = useCallback(() => {\n    document.documentElement.classList.add('[&_*]:!transition-none')\n    window.setTimeout(() => {\n      document.documentElement.classList.remove('[&_*]:!transition-none')\n    }, 0)\n  }, [])\n\n  const updateModeWithoutTransitions = useCallback(() => {\n    disableTransitionsTemporarily()\n    updateMode()\n  }, [updateMode, disableTransitionsTemporarily])\n\n  const toggleMode = useCallback(() => {\n    disableTransitionsTemporarily()\n\n    let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n    let isSystemDarkMode = darkModeMediaQuery.matches\n    let isDarkMode = document.documentElement.classList.toggle('dark')\n\n    themeState.isDarkMode = isDarkMode\n    if (isDarkMode === isSystemDarkMode) {\n      delete window.localStorage.isDarkMode\n    } else {\n      window.localStorage.isDarkMode = isDarkMode\n    }\n  }, [disableTransitionsTemporarily])\n\n  useEffect(() => {\n    let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n\n    updateMode()\n    darkModeMediaQuery.addEventListener(\n      'change',\n      updateModeWithoutTransitions,\n      { passive: true },\n    )\n    window.addEventListener('storage', updateModeWithoutTransitions, {\n      passive: true,\n    })\n\n    return () => {\n      darkModeMediaQuery.removeEventListener(\n        'change',\n        updateModeWithoutTransitions,\n      )\n      window.removeEventListener('storage', updateModeWithoutTransitions)\n    }\n  }, [])\n  return {\n    toggleMode,\n  }\n}\n"
  },
  {
    "path": "website/lib/mdx.ts",
    "content": "import { bundleMDX } from 'mdx-bundler'\nimport fs from 'fs'\nimport matter from 'gray-matter'\nimport path from 'path'\nimport { getAllFilesRecursively, slugify } from '_utils/file_helpers'\nimport { remarkCodeSandboxURLUpdater } from './remarkCodeSandboxURLUpdater'\n\n// Remark packages\nimport remarkGfm from 'remark-gfm'\nimport oembedTransformer from '@remark-embedder/transformer-oembed'\nimport remarkEmbedder from '@remark-embedder/core'\nimport type { TransformerInfo } from '@remark-embedder/core'\nimport { remarkMdxImages } from 'remark-mdx-images'\n\n// Rehype packages\nimport rehypePrismPlus from 'rehype-prism-plus'\nimport rehypeSlug from 'rehype-slug'\nimport rehypeAutolinkHeadings from 'rehype-autolink-headings'\nimport type * as U from 'unified'\n\nconst root = path.resolve(process.cwd(), '../')\nconst docsPath = path.join(root, 'docs')\n\nfunction handleEmbedderError({ url }: { url: string }) {\n  return `<p>Error embedding <a href=\"${url}\">${url}</a>.`\n}\n\ntype GottenHTML = string | null\nfunction handleEmbedderHtml(html: GottenHTML, info: TransformerInfo) {\n  if (!html) return null\n\n  const url = new URL(info.url)\n  // matches youtu.be and youtube.com\n  if (/youtu\\.?be/.test(url.hostname)) {\n    // this allows us to set youtube embeds to 100% width and the\n    // height will be relative to that width with a good aspect ratio\n    return makeEmbed(html, 'youtube')\n  }\n  if (url.hostname.includes('codesandbox.io')) {\n    return makeEmbed(html, 'codesandbox')\n  }\n  return html\n}\n\nfunction makeEmbed(html: string, type: string) {\n  return `\n  <div class=\"embed\" data-embed-type=\"${type}\">\n    <div style=\"padding-bottom: 18px;\">\n      ${html}\n    </div>\n  </div>\n`\n}\n\nconst remarkPlugins: U.PluggableList = [\n  [\n    // @ts-expect-error\n    remarkEmbedder,\n    {\n      handleError: handleEmbedderError,\n      handleHTML: handleEmbedderHtml,\n      transformers: [oembedTransformer],\n    },\n  ],\n]\n\nexport function getAllDocs() {\n  const files = getAllFilesRecursively(docsPath)\n  // Only want to return docs/path and ignore root, replace is needed to work on Windows\n  return files.map((file) =>\n    file.slice(docsPath.length + 1).replace(/\\\\/g, '/'),\n  )\n}\n\nexport function formatSlug(slug: string) {\n  return slug.replace(/\\.(mdx|md)/, '')\n}\n\nexport function getSlugs(p: string) {\n  return formatSlug(p).split('/').map(slugify)\n}\n\nexport function dateSortDesc(a: any, b: any) {\n  if (a > b) return -1\n  if (a < b) return 1\n  return 0\n}\n\nfunction getSourceFromSlug(slug: string) {\n  const mdxPath = path.join(docsPath, slug)\n\n  return fs.readFileSync(mdxPath, 'utf8')\n}\n\nexport async function getDocBySlug(slug: string) {\n  const mdxPath = path.join(docsPath, `${slug}.mdx`)\n  const mdPath = path.join(docsPath, `${slug}.md`)\n  const source = fs.existsSync(mdxPath)\n    ? fs.readFileSync(mdxPath, 'utf8')\n    : fs.readFileSync(mdPath, 'utf8')\n\n  // https://github.com/kentcdodds/mdx-bundler#nextjs-esbuild-enoent\n  if (process.platform === 'win32') {\n    process.env.ESBUILD_BINARY_PATH = path.join(\n      root,\n      'node_modules',\n      'esbuild',\n      'esbuild.exe',\n    )\n  } else {\n    process.env.ESBUILD_BINARY_PATH = path.join(\n      root,\n      'node_modules',\n      'esbuild',\n      'bin',\n      'esbuild',\n    )\n  }\n\n  // Parsing frontmatter here to pass it in as options to rehype plugin\n  const { data: frontmatter } = matter(source)\n  const cwd = path.dirname(mdxPath)\n  const { code } = await bundleMDX({\n    source,\n    // mdx imports can be automatically source from the components directory\n    // cwd: path.join(root, \"components\"),\n    cwd,\n    // FIXME can someone eliminate any here?\n    xdmOptions(options: any) {\n      // this is the recommended way to add custom remark/rehype plugins:\n      // The syntax might look weird, but it protects you in case we add/remove\n      // plugins in the future.\n      options.remarkPlugins = [\n        ...(options.remarkPlugins ?? []),\n        remarkCodeSandboxURLUpdater,\n        rehypeSlug,\n        [rehypeAutolinkHeadings, { behavior: 'wrap' }],\n        remarkGfm,\n        remarkMdxImages,\n        ...remarkPlugins,\n      ]\n      options.rehypePlugins = [\n        ...(options.rehypePlugins ?? []),\n        rehypeSlug,\n        rehypeAutolinkHeadings,\n        [rehypePrismPlus, { ignoreMissing: true }],\n      ]\n      return options\n    },\n    esbuildOptions: (options) => {\n      options.loader = {\n        ...options.loader,\n        '.js': 'jsx',\n        '.ts': 'tsx',\n        '.svg': 'dataurl',\n        '.png': 'dataurl',\n      }\n      options.outdir = path.join(root, 'build')\n      // Set the public path to /img\n      options.publicPath = '/docs/img'\n\n      // Set write to true so that esbuild will output the files.\n      options.write = true\n\n      return options\n    },\n  })\n\n  return {\n    mdxSource: code,\n    frontMatter: {\n      slug: slug || null,\n      fileName: fs.existsSync(mdxPath) ? `${slug}.mdx` : `${slug}.md`,\n      ...frontmatter,\n      date: frontmatter.date ? new Date(frontmatter.date).toISOString() : null,\n    },\n  }\n}\n\nexport async function getAllFilesFrontMatter(folder: string) {\n  const prefixPaths = path.join(docsPath, folder)\n\n  const files = getAllFilesRecursively(prefixPaths)\n\n  const allFrontMatter: any[] = []\n\n  files.forEach((file) => {\n    // Replace is needed to work on Windows\n    const fileName = file.slice(prefixPaths.length + 1).replace(/\\\\/g, '/')\n    // Remove Unexpected File\n    if (path.extname(fileName) !== '.md' && path.extname(fileName) !== '.mdx') {\n      return\n    }\n    const source = fs.readFileSync(file, 'utf8')\n    const { data: frontmatter } = matter(source)\n    if (frontmatter.draft !== true) {\n      allFrontMatter.push({\n        ...frontmatter,\n        slug: getSlugs(fileName),\n        date: frontmatter.date\n          ? new Date(frontmatter.date).toISOString()\n          : null,\n      })\n    }\n  })\n\n  return allFrontMatter.sort((a, b) => dateSortDesc(a.date, b.date))\n}\n\nconst removeExtension = (path: string) => {\n  return path.replace(/\\.[^/.]+$/, '')\n}\n\nconst getTitle = (path: string) => {\n  return removeExtension(path.split('/').pop() || '')\n}\n\nfunction prepareDoc(doc: string) {\n  const slugs = getSlugs(doc)\n  const href = `/docs/${slugs.map(slugify).join('/')}`\n  const source = getSourceFromSlug(doc)\n  const { data: frontmatter } = matter(source)\n  const title = frontmatter.title ?? getTitle(doc)\n  return {\n    title,\n    href,\n    slug: slugs[slugs.length - 1],\n  }\n}\n\ntype PageNavigation = Record<string, Navigation[]>\n\ntype NavigationTree = Record<string, Navigation[] | PageNavigation>\n\nexport function getDocsMap(): Record<string, Navigation> {\n  const docs = getAllDocs()\n  return docs.reduce((acc, d) => {\n    const doc = prepareDoc(d)\n\n    return { ...acc, [doc.slug]: doc as Navigation }\n  }, {})\n}\n\nexport function getDocsNav(): NavigationTree {\n  const pages = getDocsMap()\n  return {\n    Introduction: [pages['getting-started']],\n    Guides: [\n      pages['async'],\n      pages['component-state'],\n      pages['computed-properties'],\n      pages['migrating-to-v2'],\n    ],\n    API: {\n      Basic: [pages['proxy'], pages['useSnapshot']],\n      Advanced: [\n        pages['ref'],\n        pages['subscribe'],\n        pages['subscribe-ops'],\n        pages['snapshot'],\n      ],\n      Utils: [\n        pages['subscribeKey'],\n        pages['watch'],\n        pages['devtools'],\n        pages['derive'],\n        pages['proxyWithHistory'],\n        pages['proxySet'],\n        pages['proxyMap'],\n        pages['unstable_deepProxy'],\n      ],\n      Hacks: [pages['getVersion'], pages['internals']],\n    },\n    \"How To's\": [\n      pages['how-to-avoid-rerenders-manually'],\n      pages['how-to-easily-access-the-state-from-anywhere-in-the-application'],\n      pages['how-to-organize-actions'],\n      pages['how-to-update-values-inside-arrays'],\n      pages['how-to-persist-states'],\n      pages['how-to-reset-state'],\n      pages['how-to-split-and-compose-states'],\n      pages['how-to-use-with-context'],\n      pages['how-valtio-works'],\n      pages['some-gotchas'],\n    ],\n    Resources: [pages['community'], pages['libraries'], pages['learn']],\n  }\n}\n"
  },
  {
    "path": "website/lib/remarkCodeSandboxURLUpdater.ts",
    "content": "import type { Node } from 'unist'\nimport { visit } from 'unist-util-visit'\n\nconst codesandboxBaseUrl = 'https://codesandbox.io/s'\n\nconst defaultParams = {\n  codemirror: 1,\n  fontsize: 14,\n  hidenavigation: 1,\n  theme: 'light',\n  hidedevtools: 1,\n}\n\ninterface LinkNode extends Node {\n  url: string\n  children: {\n    value: string\n  }[]\n}\n\nexport const remarkCodeSandboxURLUpdater = (\n  options = {\n    params: defaultParams,\n  },\n) => {\n  function visitor(linkNode: LinkNode) {\n    if (\n      linkNode.url.startsWith(codesandboxBaseUrl) &&\n      linkNode.children[0]?.value\n    ) {\n      const url = new URL(linkNode.url)\n      Object.entries(options.params).forEach(([key, value]) => {\n        url.searchParams.append(key, value + '')\n      })\n      linkNode.url = url.toString()\n      linkNode.children[0].value = url.toString()\n    }\n  }\n\n  function transform(tree: Node) {\n    visit(tree, 'link', visitor)\n  }\n\n  return transform\n}\n"
  },
  {
    "path": "website/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": "website/next.config.js",
    "content": "// Prevent crash on Node.js 25+ where SlowBuffer was removed.\n// This error originates from a compiled version of jsonwebtoken bundled inside Next.js.\nconst buffer = require('buffer')\nif (!buffer.SlowBuffer) {\n  buffer.SlowBuffer = buffer.Buffer\n}\n\n/** @type {import('next').NextConfig} */\nmodule.exports = {\n  reactStrictMode: true,\n}\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"website\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"packageManager\": \"pnpm@10.18.3\",\n  \"dependencies\": {\n    \"@headlessui/react\": \"^1.4.2\",\n    \"@heroicons/react\": \"^1.0.5\",\n    \"@remark-embedder/core\": \"^2.0.0\",\n    \"@remark-embedder/transformer-oembed\": \"^2.0.0\",\n    \"@tailwindcss/typography\": \"^0.5.9\",\n    \"clsx\": \"^1.1.1\",\n    \"d3-ease\": \"^3.0.1\",\n    \"esbuild\": \"^0.14.11\",\n    \"gray-matter\": \"^4.0.3\",\n    \"mdx-bundler\": \"^8.0.1\",\n    \"next\": \"^12.1.4\",\n    \"prism-react-renderer\": \"^1.3.1\",\n    \"react\": \"17.0.2\",\n    \"react-dom\": \"17.0.2\",\n    \"react-spring\": \"^9.4.4\",\n    \"rehype-autolink-headings\": \"^6.1.1\",\n    \"rehype-prism-plus\": \"^1.2.2\",\n    \"rehype-slug\": \"^5.0.1\",\n    \"remark-gfm\": \"^3.0.1\",\n    \"remark-mdx-images\": \"^1.0.3\",\n    \"unified\": \"^11.0.4\",\n    \"valtio\": \"^1.5.2\"\n  },\n  \"devDependencies\": {\n    \"@types/d3-ease\": \"^3.0.0\",\n    \"@types/node\": \"17.0.8\",\n    \"@types/react\": \"17.0.38\",\n    \"@types/unist\": \"^2.0.0\",\n    \"autoprefixer\": \"^10.4.13\",\n    \"eslint\": \"8.6.0\",\n    \"eslint-config-next\": \"12.0.7\",\n    \"postcss\": \"^8.4.21\",\n    \"tailwindcss\": \"^3.2.4\",\n    \"typescript\": \"4.5.4\",\n    \"unist-util-visit\": \"^4.1.0\"\n  }\n}\n"
  },
  {
    "path": "website/pages/_app.tsx",
    "content": "import type { NextPage } from 'next'\nimport type { AppProps } from 'next/app'\n\nimport '~/styles/tailwind.css'\nimport '~/styles/prism-theme.css'\nimport '~/styles/landing-page.css'\n\ntype NextPageWithLayout = NextPage & {\n  layoutProps: {\n    meta: Dict\n    Layout?: React.FunctionComponent\n  }\n}\n\ntype AppPropsWithLayout = AppProps & {\n  Component: NextPageWithLayout\n}\n\nexport default function MyApp({ Component, pageProps }: AppPropsWithLayout) {\n  return (\n    <>\n      <Component {...pageProps} />\n    </>\n  )\n}\n"
  },
  {
    "path": "website/pages/_document.tsx",
    "content": "import { Head, Html, Main, NextScript } from 'next/document'\n\nconst modeScript = `\n  let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n\n  updateMode()\n  darkModeMediaQuery.addEventListener('change', updateModeWithoutTransitions)\n  window.addEventListener('storage', updateModeWithoutTransitions)\n\n  function updateMode() {\n    let isSystemDarkMode = darkModeMediaQuery.matches\n    let isDarkMode = window.localStorage.isDarkMode === 'true' || (!('isDarkMode' in window.localStorage) && isSystemDarkMode)\n\n    if (isDarkMode) {\n      document.documentElement.classList.add('dark')\n    } else {\n      document.documentElement.classList.remove('dark')\n    }\n\n    if (isDarkMode === isSystemDarkMode) {\n      delete window.localStorage.isDarkMode\n    }\n  }\n\n  function disableTransitionsTemporarily() {\n    document.documentElement.classList.add('[&_*]:!transition-none')\n    window.setTimeout(() => {\n      document.documentElement.classList.remove('[&_*]:!transition-none')\n    }, 0)\n  }\n\n  function updateModeWithoutTransitions() {\n    disableTransitionsTemporarily()\n    updateMode()\n  }\n`\n\nexport default function Document() {\n  return (\n    <Html className=\"h-full antialiased\" lang=\"en\">\n      <Head>\n        <script dangerouslySetInnerHTML={{ __html: modeScript }} />\n      </Head>\n      <body className=\"flex h-full flex-col bg-gray-50 dark:bg-gray-900\">\n        <Main />\n        <NextScript />\n      </body>\n    </Html>\n  )\n}\n"
  },
  {
    "path": "website/pages/docs/[...slug].tsx",
    "content": "import { GetStaticPaths, GetStaticProps } from 'next'\nimport { DocLayout } from '~/components/layouts'\nimport MDXRenderer from '~/components/MDXRenderer'\nimport { getSlugs, getAllDocs, getDocBySlug, getDocsNav } from '~/lib/mdx'\n\nexport const getStaticPaths: GetStaticPaths = async () => {\n  const docs = getAllDocs()\n  return {\n    paths: docs.map((p) => ({\n      params: {\n        slug: getSlugs(p),\n      },\n    })),\n    fallback: false,\n  }\n}\n\nexport const getStaticProps: GetStaticProps = async ({ params }) => {\n  let slug = params?.slug!\n  if (Array.isArray(slug)) slug = slug.join('/')\n  const doc = await getDocBySlug(slug)\n  const nav = getDocsNav()\n  return {\n    props: {\n      doc,\n      nav,\n    },\n  }\n}\n\ninterface Props {\n  doc: Doc\n  nav: Record<string, Navigation[]>\n}\n\nexport default function Doc({ doc, nav }: Props) {\n  const { mdxSource, frontMatter } = doc\n\n  return (\n    <>\n      <DocLayout nav={nav} frontMatter={frontMatter}>\n        <MDXRenderer mdxSource={mdxSource} frontMatter={frontMatter} />\n      </DocLayout>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/pages/index.tsx",
    "content": "import { AnimatedShapes } from '~/components/LandingPage/AnimatedShapes'\nimport { GettingStarted } from '~/components/LandingPage/GettingStarted'\nimport { CodeExample } from '~/components/LandingPage/CodeExample'\nimport SEO from '~/components/SEO'\n\nconst Home = () => {\n  return (\n    <div className=\"min-h-screen landing-page-container\">\n      <SEO />\n      <CodeExample />\n      <AnimatedShapes />\n      <GettingStarted className=\"large-screen\" />\n    </div>\n  )\n}\n\nexport default Home\n"
  },
  {
    "path": "website/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "website/state/index.ts",
    "content": "export * from './useThemeState'\n"
  },
  {
    "path": "website/state/useThemeState.ts",
    "content": "import { proxy } from 'valtio'\nexport const themeState = proxy({\n  isDarkMode: false,\n})\n"
  },
  {
    "path": "website/styles/landing-page.css",
    "content": "svg {\n  z-index: -1;\n}\n\nhtml {\n  --theme-blue: #b2ebf2;\n  --base-animation-duration: 4s;\n}\n\n.top-left {\n  position: fixed;\n  top: 20px;\n  left: 20px;\n}\n\n.top-right {\n  position: fixed;\n  top: 20px;\n  right: 20px;\n}\n\n.bottom-left {\n  position: fixed;\n  bottom: 0;\n  left: 0;\n}\n\n.bottom-right {\n  position: fixed;\n  bottom: 0;\n  right: 0;\n}\n\n.center {\n  position: fixed;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n}\n\n@media (max-width: 1700px) {\n  .center {\n    left: 35%;\n  }\n}\n\n@media (max-width: 1280px) {\n  .center {\n    top: 20%;\n    left: 50%;\n  }\n}\n\n.bottom-right .spinning-square {\n  transform-origin: 700px 800px;\n}\n\n.code-container {\n  position: absolute;\n  top: 35%;\n  right: 30px;\n  height: 300px;\n  z-index: 50;\n}\n\n@media (max-width: 1280px) {\n  .code-container {\n    left: 0;\n    right: 0;\n  }\n\n  .prism-code.language-jsx {\n    border-radius: 0;\n  }\n\n  /* center code when pre is full-screen*/\n  .pre {\n    padding-left: calc(100% - 800px) !important;\n  }\n}\n\n.code-container-inner {\n  position: relative;\n  height: 100%;\n  width: 100%;\n  z-index: 60;\n}\n\n.duration-changer {\n  position: absolute;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  width: 100px;\n  height: 100px;\n  top: -75px;\n  right: 15px;\n  z-index: 50;\n  background: #fff;\n  border: 8px solid var(--theme-blue);\n  border-radius: 3px;\n}\n\n.button-container {\n  display: flex;\n}\n\nbutton.counter {\n  cursor: pointer;\n  background: transparent;\n  border-radius: 0;\n  border: 3px solid #00caff;\n  color: black;\n  font-size: 24px;\n  font-weight: 400;\n  margin: 0;\n  outline: none;\n  padding: 0 10px;\n  transition: background 0.3s;\n}\nbutton.counter + button.counter {\n  margin-left: 4px;\n}\n\nbutton.counter:hover,\nbutton.counter:active {\n  background: #00caff;\n  color: var(--theme-blue);\n}\nbutton.counter:active {\n  outline: 3px solid #00caff;\n}\nbutton.counter:disabled {\n  cursor: not-allowed;\n  background: lightgray;\n  color: #111;\n}\n\n.get-started.large-screen {\n  position: absolute;\n  display: grid;\n}\n\n.get-started.small-screen {\n  display: none;\n}\n\n@media (max-width: 1280px) {\n  .get-started.large-screen {\n    display: none;\n  }\n  .get-started.small-screen {\n    display: grid;\n  }\n}\n"
  },
  {
    "path": "website/styles/prism-theme.css",
    "content": "/*\n * Nord Theme Originally by Arctic Ice Studio\n * https://nordtheme.com\n *\n * Ported for PrismJS by Zane Hitchcox (@zwhitchcox) and Gabriel Ramos (@gabrieluizramos)\n */\n\ncode[class*='language-'],\npre[class*='language-'] {\n  color: #f8f8f2;\n  background: none;\n  font-family:\n    'Fira Code', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  -o-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  -moz-hyphens: none;\n  -ms-hyphens: none;\n  hyphens: none;\n}\n\n/* Code blocks */\npre {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n  border-radius: 0.3em;\n}\n\n:not(pre) > code[class*='language-'],\npre[class*='language-'] {\n  background: #2e3440;\n  padding: 1em;\n}\n\ncode:not([class*='language-']) {\n  padding: 0.1em 0.3em;\n  background: #eee;\n}\n\n.dark code:not([class*='language-']) {\n  background: #2e3440;\n}\n\ncode {\n  font-family:\n    'Fira Code', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n}\n\ncode::before,\ncode::after {\n  content: '' !important;\n}\n\n/* Inline code */\n:not(pre) > code[class*='language-'] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #636f88;\n}\n\n.token.punctuation {\n  color: #81a1c1;\n}\n\n.namespace {\n  opacity: 0.7;\n}\n\n.token.property,\n.token.tag,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #81a1c1;\n}\n\n.token.number {\n  color: #b48ead;\n}\n\n.token.boolean {\n  color: #81a1c1;\n}\n\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #a3be8c;\n}\n\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string,\n.token.variable {\n  color: #81a1c1;\n}\n\n.token.atrule,\n.token.attr-value,\n.token.function,\n.token.class-name {\n  color: #88c0d0;\n}\n\n.token.keyword {\n  color: #81a1c1;\n}\n\n.token.regex,\n.token.important {\n  color: #ebcb8b;\n}\n\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n\n.token.italic {\n  font-style: italic;\n}\n\n.token.entity {\n  cursor: help;\n}\n\nblockquote.tip p::before,\nblockquote.tip p::after,\nblockquote.note p::before,\nblockquote.note p::after,\nblockquote.warning p::before,\nblockquote.warning p::after,\nblockquote.important p::before,\nblockquote.important p::after {\n  content: '' !important;\n}\n\nblockquote {\n  padding-left: 1.5rem;\n}\n\n.tip {\n  border-left: 5px solid #88c0d0;\n}\n\n.note {\n  border-left: 5px solid #81a1c1;\n}\n\n.warning {\n  border-left: 5px solid #ebcb8b;\n}\n\n.important {\n  border-left: 5px solid #a3be8c;\n}\n"
  },
  {
    "path": "website/styles/tailwind.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "website/tailwind.config.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nconst colors = require('tailwindcss/colors')\n\nmodule.exports = {\n  darkMode: 'class',\n  content: [\n    './pages/**/*.{js,ts,jsx,tsx}',\n    './components/**/*.{js,ts,jsx,tsx}',\n    '../docs/**/*.{.md,.mdx}',\n  ],\n  theme: {\n    colors: {\n      ...colors,\n      gray: {\n        ...colors.neutral,\n        350: '#bcbcbc',\n      },\n    },\n    extend: {},\n  },\n  plugins: [require('@tailwindcss/typography')],\n}\n"
  },
  {
    "path": "website/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"~/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "website/types.d.ts",
    "content": "type Dict = Record<string, any>\ninterface Navigation {\n  href: string\n  match?: RegExp\n  title: string\n  published?: boolean\n  isActive?: boolean\n}\n\ninterface Doc {\n  mdxSource: string\n  frontMatter: Dict\n}\n"
  }
]