[
  {
    "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    \"next-js-uo1h0\",\n    \"next-js-with-custom-babel-config-komw9\",\n    \"react-with-custom-babel-config-z1ebx\"\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: jotai # 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\ncustom: ['https://daishi.gumroad.com/l/learn-jotai'] # 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/jotai/discussions/new?category=bug-report\n    about: Please post bug reports here.\n  - name: Questions\n    url: https://github.com/pmndrs/jotai/discussions/new?category=q-a\n    about: Please post questions here.\n  - name: Other Discussions\n    url: https://github.com/pmndrs/jotai/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/ecosystem-ci.yml",
    "content": "name: Ecosystem CI\n\non:\n  issue_comment:\n    types: [created]\n\njobs:\n  trigger:\n    runs-on: ubuntu-latest\n    if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run') }}\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          repository: 'jotaijs/jotai-ecosystem-ci'\n      - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0\n      - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 22\n          cache: 'pnpm'\n      - run: pnpm install\n      - name: Get Short SHA\n        id: short_sha\n        run: |\n          api=\"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}\"\n          sha=$(curl -s -H \"Authorization: token $GITHUB_TOKEN\" $api | jq -r '.head.sha' | cut -c1-8)\n          echo \"x=$sha\" >> $GITHUB_OUTPUT\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: Run Ecosystem CI\n        id: run_command\n        run: |\n          echo \"x<<EOF\" >> $GITHUB_OUTPUT\n          pnpm run ecosystem-ci | tee >(grep -A999 -- '---- Jotai Ecosystem CI Results ----' >> $GITHUB_OUTPUT)\n          echo \"EOF\" >> $GITHUB_OUTPUT\n        env:\n          JOTAI_PKG: https://pkg.csb.dev/pmndrs/jotai/commit/${{ steps.short_sha.outputs.x }}/jotai\n          VERBOSE: 1\n      - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0\n        with:\n          issue-number: ${{ github.event.issue.number }}\n          body: |\n            ## Ecosystem CI Output\n            ```\n            ${{ steps.run_command.outputs.x }}\n            ```\n            ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n"
  },
  {
    "path": ".github/workflows/livecodes-post-comment.yml",
    "content": "name: LiveCodes Post Comment\n\non:\n  workflow_run:\n    workflows: [LiveCodes Preview]\n    types:\n      - completed\n\njobs:\n  upload:\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n    if: >\n      github.event.workflow_run.event == 'pull_request' &&\n      github.event.workflow_run.conclusion == 'success'\n    steps:\n      - uses: live-codes/pr-comment-from-artifact@15cb31a249e2336571f5d8cc244f6132b7d90ead # v1.0.1\n        with:\n          GITHUB_TOKEN: ${{ github.token }}\n"
  },
  {
    "path": ".github/workflows/livecodes-preview.yml",
    "content": "name: LiveCodes Preview\n\non: [pull_request]\n\njobs:\n  build_and_prepare:\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: live-codes/preview-in-livecodes@636df52bd6a29316a7ad7501e7016ffdc6f83468 # v1.2.2\n        with:\n          install-command: pnpm install\n          build-command: pnpm run build\n          base-url: 'https://{{LC::REF}}.preview-in-livecodes-demo.pages.dev'\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/stale-discussions.yml",
    "content": "name: Close Stale Discussions\n\non:\n  schedule:\n    - cron: '0 0 * * *' # Runs every day at midnight UTC\n  workflow_dispatch:\n\njobs:\n  close-stale-discussions:\n    runs-on: ubuntu-latest\n    permissions:\n      discussions: write\n    steps:\n      - uses: steffen-karlsson/stalesweeper@cbed739a89b490f703d8466fb59b37ddc0915889 # v1.1.1\n        with:\n          message: 'This discussion has been closed due to inactivity.'\n          days-before-close: 180\n          close-unanswered: true\n          verbose: true\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, umd]\n        env: [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: Use React 17 for production test\n        if: ${{ matrix.env == 'production' }}\n        run: |\n          pnpm add -D react@17.0.2 react-dom@17.0.2 @testing-library/react@12.1.4\n      - name: Patch for DEV-ONLY\n        if: ${{ matrix.env == 'development' }}\n        run: |\n          sed -i~ \"s/\\(it\\|describe\\)[.a-zA-Z]*('\\[DEV-ONLY\\]/\\1('/\" tests/*/*.ts* tests/*/*/*.ts*\n          sed -i~ \"s/\\(it\\|describe\\)[.a-zA-Z]*('\\[PRD-ONLY\\]/\\1.skip('/\" tests/*/*.ts* tests/*/*/*.ts*\n      - name: Patch for PRD-ONLY\n        if: ${{ matrix.env == 'production' }}\n        run: |\n          sed -i~ \"s/\\(it\\|describe\\)[.a-zA-Z]*('\\PRD-ONLY\\]/\\1('/\" tests/*/*.ts* tests/*/*/*.ts*\n          sed -i~ \"s/\\(it\\|describe\\)[.a-zA-Z]*('\\[DEV-ONLY\\]/\\1.skip('/\" tests/*/*.ts* tests/*/*/*.ts*\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          sed -i~ \"s/import { useResetAtom } from 'jotai\\/react\\/utils'/const { useResetAtom } = require('..\\/..\\/..\\/dist\\/react\\/utils.js')/\" tests/react/utils/useResetAtom.test.tsx\n          sed -i~ \"s/import { RESET, atomWithReducer, atomWithReset } from 'jotai\\/vanilla\\/utils'/const { RESET, atomWithReducer, atomWithReset } = require('..\\/..\\/..\\/dist\\/vanilla\\/utils.js')/\" tests/react/utils/useResetAtom.test.tsx\n          perl -i~ -0777 -pe \"s/import {[^}]+} from 'jotai\\/vanilla\\/internals'/const { INTERNAL_buildStoreRev2: INTERNAL_buildStore, INTERNAL_initializeStoreHooksRev2: INTERNAL_initializeStoreHooks, INTERNAL_getBuildingBlocksRev2: INTERNAL_getBuildingBlocks } = require('..\\/..\\/dist\\/vanilla\\/internals.js')/g\" tests/vanilla/store.test.tsx tests/vanilla/internals.test.tsx tests/vanilla/derive.test.tsx tests/vanilla/effect.test.ts\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/*/*.tsx tests/*/*/*.tsx\n        env:\n          NODE_ENV: ${{ matrix.env }}\n      - name: Patch for UMD\n        if: ${{ matrix.build == 'umd' }}\n        run: |\n          sed -i~ \"s/resolve('\\.\\/src\\(.*\\)\\.ts')/resolve('\\.\\/dist\\/umd\\1.${NODE_ENV}.js')/\" vitest.config.mts\n          rm tests/react/utils/useResetAtom.test.tsx # FIXME we skip this for now. Actually I'm not sure if we really run tests with UMD build\n          rm tests/vanilla/store.test.tsx tests/vanilla/internals.test.tsx tests/vanilla/derive.test.tsx tests/vanilla/effect.test.ts # FIXME we skip this for now. Actually I'm not sure if we really run tests with UMD build\n        env:\n          NODE_ENV: ${{ matrix.env }}\n      - name: Patch for SystemJS\n        if: ${{ matrix.build == 'system' }}\n        run: |\n          sed -i~ \"s/resolve('\\.\\/src\\(.*\\)\\.ts')/resolve('\\.\\/dist\\/system\\1.${NODE_ENV}.js')/\" vitest.config.mts\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          - 16.14.0\n          - 17.0.0\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-46103596-20260305\n          - 0.0.0-experimental-46103596-20260305\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: |\n          pnpm add -D @testing-library/react@12.1.4\n      - name: Patch for React 17\n        if: ${{ startsWith(matrix.react, '17.') }}\n        run: |\n          pnpm add -D vitest@3.2.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 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 Build # we need to build for babel tests\n        run: pnpm run build\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          - 4.4.4\n          - 4.3.5\n          - 4.2.3\n          - 4.1.5\n          - 4.0.5\n          - 3.9.7\n          - 3.8.3\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 Newer TS\n        if: ${{ matrix.typescript == '4.9.5' || matrix.typescript == '4.8.4' }}\n        run: |\n          sed -i~ 's/\"moduleResolution\": \"bundler\",/\"moduleResolution\": \"node\",/' tsconfig.json\n          sed -i~ 's/\"allowImportingTsExtensions\": true,//' tsconfig.json\n          sed -i~ 's/\"jotai\": \\[\"\\.\\/src\\/index\\.ts\"\\],/\"jotai\": [\".\\/dist\\/index.d.ts\"],/' tsconfig.json\n          sed -i~ 's/\"jotai\\/\\*\": \\[\"\\.\\/src\\/\\*\\.ts\"\\]/\"jotai\\/*\": [\".\\/dist\\/*.d.ts\"]/' tsconfig.json\n          sed -i~ 's/\"include\": .*/\"include\": [\"src\\/types.d.ts\", \"dist\\/**\\/*\", \"tests\\/**\\/*\"],/' tsconfig.json\n      - name: Patch for specific TS version\n        run: |\n          ts_ver_esc=${{ matrix.typescript }}\n          ts_ver_esc=${ts_ver_esc//./\\\\.}\n          sed -i~ \"s/\\/\\/ @ts-expect-error .*\\[SKIP-TS-${ts_ver_esc}\\].*//\" tests/*/*.tsx tests/*/*/*.tsx\n          sed -i~ \"s/\\/\\/ .*\\[ONLY-TS-${ts_ver_esc}\\].* @ts-ignore/\\/\\/ @ts-ignore/\" tests/*/*.tsx tests/*/*/*.tsx\n      - name: Patch for Old TS\n        if: ${{ matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' || matrix.typescript == '4.4.4' || matrix.typescript == '4.3.5' || matrix.typescript == '4.2.3' || matrix.typescript == '4.1.5' ||  matrix.typescript == '4.0.5' || startsWith(matrix.typescript, '3.') }}\n        run: |\n          sed -i~ 's/\"target\":/\"skipLibCheck\":true,\"target\":/' tsconfig.json\n          sed -i~ 's/\"exactOptionalPropertyTypes\": true,//' tsconfig.json\n          sed -i~ 's/\"moduleResolution\": \"bundler\",/\"moduleResolution\": \"node\",/' tsconfig.json\n          sed -i~ 's/\"allowImportingTsExtensions\": true,//' tsconfig.json\n          sed -i~ 's/\"jotai\": \\[\"\\.\\/src\\/index\\.ts\"\\],/\"jotai\": [\".\\/dist\\/ts3.8\\/index.d.ts\"],/' tsconfig.json\n          sed -i~ 's/\"jotai\\/\\*\": \\[\"\\.\\/src\\/\\*\\.ts\"\\]/\"jotai\\/*\": [\".\\/dist\\/ts3.8\\/*.d.ts\"]/' tsconfig.json\n          sed -i~ 's/\"include\": .*/\"include\": [\"src\\/types.d.ts\", \"dist\\/**\\/*\", \"tests\\/**\\/*\"],/' tsconfig.json\n          pnpm add -D @testing-library/user-event@14.4.3 @types/node@22.2.0 @types/babel__traverse@7.18.2\n      - name: Patch for Older TS\n        if: ${{ matrix.typescript == '4.2.3' || matrix.typescript == '4.1.5' ||  matrix.typescript == '4.0.5' || startsWith(matrix.typescript, '3.') }}\n        run: |\n          sed -i~ 's/import\\.meta\\.env/(import.meta.env as any)/' tests/*/*.tsx tests/*/*/*.tsx\n          sed -i~ '1s/^/\\/\\/\\/ <reference types=\"react\\/experimental\" \\/>\\nimport React from \"react\";/' tests/*/*.tsx tests/*/*/*.tsx\n          sed -i~ 's/\"jsx\": \"react-jsx\",/\"jsx\": \"react\",/' tsconfig.json\n          sed -i~ 's/\"noUncheckedIndexedAccess\": true,//' tsconfig.json\n          sed -i~ 's/^import type /import /' tests/*/*.tsx tests/*/*/*.tsx\n          pnpm json -I -f package.json -e \"this.resolutions={};  this.resolutions['@types/prettier']='2.4.2'; this.resolutions['@types/node']='18.11.18'; this.resolutions['@types/react']='18.2.56';\"\n          pnpm add -D @types/prettier@2.4.2 @types/node@18.11.18 @types/yargs@17.0.13 @types/react@18.2.56\n          rm -r tests/react/vanilla-utils/atomWithObservable.*\n      - name: Install old TypeScript\n        run: pnpm add -D typescript@${{ matrix.typescript }}\n      - name: Patch testing setup for Old TS\n        if: ${{ matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' || matrix.typescript == '4.4.4' || matrix.typescript == '4.3.5' || matrix.typescript == '4.2.3' || matrix.typescript == '4.1.5' ||  matrix.typescript == '4.0.5' || startsWith(matrix.typescript, '3.') }}\n        run: |\n          pnpm add -D vitest@0.33.0 @vitest/coverage-v8@0.33.0 @vitest/ui@0.33.0\n          pnpm add -D @testing-library/jest-dom@5 @types/testing-library__jest-dom@5 \n          pnpm add -D @types/jest@27.4.1\n          sed -i~ 's/\"@testing-library\\/jest-dom\"/\"@types\\/testing-library__jest-dom\"/' tsconfig.json\n      - name: Patch testing setup for older TS\n        if: ${{ matrix.typescript == '4.0.5' || startsWith(matrix.typescript, '3.') }}\n        run: |\n          pnpm add -D @testing-library/user-event@12.1.7 @testing-library/react@11.0.4 @types/react-dom@18.3.1\n          rm node_modules/vitest/dist/*.d.ts\n          echo \"declare module 'vitest'\" >> ./src/types.d.ts\n      - name: Test ${{ matrix.typescript }}\n        run: |\n          rm -r node_modules/@types/babel__core/node_modules\n          sed -i~ 's/\">=4.2\": {/\">=4.1\": {/' node_modules/rxjs/package.json\n          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# development\n.devcontainer\n.vscode\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": ".livecodes/react.json",
    "content": "{\n  \"title\": \"React demo\",\n  \"activeEditor\": \"script\",\n  \"markup\": {\n    \"language\": \"html\",\n    \"content\": \"<div id=\\\"root\\\"></div>\"\n  },\n  \"style\": {\n    \"language\": \"css\",\n    \"content\": \".App {\\n  font-family: sans-serif;\\n  text-align: center;\\n}\\n\"\n  },\n  \"script\": {\n    \"language\": \"jsx\",\n    \"content\": \"import { StrictMode, Suspense } from 'react';\\nimport { createRoot } from 'react-dom/client';\\nimport { atom, useAtom } from 'jotai';\\n\\nconst countAtom = atom(0);\\n\\nconst Counter = () => {\\n  const [count, setCount] = useAtom(countAtom);\\n  const inc = () => setCount((c) => c + 1);\\n  return (\\n    <>\\n      {count} <button onClick={inc}>+1</button>\\n    </>\\n  );\\n};\\n\\nconst App = () => (\\n  <Suspense fallback=\\\"Loading...\\\">\\n    <div className=\\\"App\\\">\\n      <h1>Hello Jotai</h1>\\n      <h2>Enjoy coding!</h2>\\n      <Counter />\\n    </div>\\n  </Suspense>\\n);\\n\\nconst rootElement = document.getElementById('root');\\nconst root = createRoot(rootElement);\\n\\nroot.render(\\n  <StrictMode>\\n    <App />\\n  </StrictMode>\\n);\\n\"\n  },\n  \"customSettings\": {\n    \"jotai commit sha\": \"{{LC::SHORT_SHA}}\",\n    \"imports\": {\n      \"jotai\": \"{{LC::TO_DATA_URL(./dist/esm/index.mjs)}}\",\n      \"jotai/vanilla\": \"{{LC::TO_DATA_URL(./dist/esm/vanilla.mjs)}}\",\n      \"jotai/utils\": \"{{LC::TO_DATA_URL(./dist/esm/utils.mjs)}}\",\n      \"jotai/react\": \"{{LC::TO_DATA_URL(./dist/esm/react.mjs)}}\",\n      \"jotai/vanilla/utils\": \"{{LC::TO_DATA_URL(./dist/esm/vanilla/utils.mjs)}}\",\n      \"jotai/react/utils\": \"{{LC::TO_DATA_URL(./dist/esm/react/utils.mjs)}}\"\n    }\n  }\n}\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/jotai/discussions/new?category=bug-report).\n\nFor any usage questions, please [start a discussion](https://github.com/pmndrs/jotai/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/jotai/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## Jotai-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:9000`](http://localhost:9000) 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": "<br>\n\n![Jotai (light mode)](./img/jotai-header-light.png#gh-light-mode-only)\n![Jotai (dark mode)](./img/jotai-header-dark.png#gh-dark-mode-only)\n\n<br>\n\nvisit [jotai.org](https://jotai.org) or `npm i jotai`\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/pmndrs/jotai/test.yml?branch=main&style=flat&colorA=000000&colorB=000000)](https://github.com/pmndrs/jotai/actions/workflows/test.yml?query=branch%3Amain)\n[![Build Size](https://img.shields.io/bundlephobia/minzip/jotai?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=jotai)\n[![Version](https://img.shields.io/npm/v/jotai?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/jotai)\n[![Downloads](https://img.shields.io/npm/dt/jotai.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/jotai)\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[![Open Collective](https://img.shields.io/opencollective/all/jotai?style=flat&colorA=000000&colorB=000000)](https://opencollective.com/jotai)\n\nJotai scales from a simple useState replacement to an enterprise TypeScript application.\n\n- Minimal core API (2kb)\n- Many utilities and extensions\n- No string keys (compared to Recoil)\n\nExamples: [Demo 1](https://codesandbox.io/s/jotai-demo-47wvh) |\n[Demo 2](https://codesandbox.io/s/jotai-demo-forked-x2g5d)\n\n### First, create a primitive atom\n\nAn atom represents a piece of state. All you need is to specify an initial\nvalue, which can be primitive values like strings and numbers, objects, and\narrays. You can create as many primitive atoms as you want.\n\n```jsx\nimport { atom } from 'jotai'\n\nconst countAtom = atom(0)\nconst countryAtom = atom('Japan')\nconst citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])\nconst mangaAtom = atom({ 'Dragon Ball': 1984, 'One Piece': 1997, Naruto: 1999 })\n```\n\n### Use the atom in your components\n\nIt can be used like `React.useState`:\n\n```jsx\nimport { useAtom } from 'jotai'\n\nfunction Counter() {\n  const [count, setCount] = useAtom(countAtom)\n  return (\n    <h1>\n      {count}\n      <button onClick={() => setCount((c) => c + 1)}>one up</button>\n      ...\n```\n\n### Create derived atoms with computed values\n\nA new read-only atom can be created from existing atoms by passing a read\nfunction as the first argument. `get` allows you to fetch the contextual value\nof any atom.\n\n```jsx\nconst doubledCountAtom = atom((get) => get(countAtom) * 2)\n\nfunction DoubleCounter() {\n  const [doubledCount] = useAtom(doubledCountAtom)\n  return <h2>{doubledCount}</h2>\n}\n```\n\n### Creating an atom from multiple atoms\n\nYou can combine multiple atoms to create a derived atom.\n\n```jsx\nconst count1 = atom(1)\nconst count2 = atom(2)\nconst count3 = atom(3)\n\nconst sum = atom((get) => get(count1) + get(count2) + get(count3))\n```\n\nOr if you like fp patterns ...\n\n```jsx\nconst atoms = [count1, count2, count3, ...otherAtoms]\nconst sum = atom((get) => atoms.map(get).reduce((acc, count) => acc + count))\n```\n\n### Derived async atoms [<img src=\"https://img.shields.io/badge/-needs_suspense-black\" alt=\"needs suspense\" />](https://react.dev/reference/react/Suspense)\n\nYou can make the read function an async function too.\n\n```jsx\nconst urlAtom = atom('https://json.host.com')\nconst fetchUrlAtom = atom(async (get) => {\n  const response = await fetch(get(urlAtom))\n  return await response.json()\n})\n\nfunction Status() {\n  // Re-renders the component after urlAtom is changed and the async function above concludes\n  const [json] = useAtom(fetchUrlAtom)\n  ...\n```\n\n### You can create a writable derived atom\n\nSpecify a write function at the second argument. `get` will return the current\nvalue of an atom. `set` will update the value of an atom.\n\n```jsx\nconst decrementCountAtom = atom(\n  (get) => get(countAtom),\n  (get, set, _arg) => set(countAtom, get(countAtom) - 1)\n)\n\nfunction Counter() {\n  const [count, decrement] = useAtom(decrementCountAtom)\n  return (\n    <h1>\n      {count}\n      <button onClick={decrement}>Decrease</button>\n      ...\n```\n\n### Write only derived atoms\n\nJust do not define a read function.\n\n```jsx\nconst multiplyCountAtom = atom(null, (get, set, by) =>\n  set(countAtom, get(countAtom) * by),\n)\n\nfunction Controls() {\n  const [, multiply] = useAtom(multiplyCountAtom)\n  return <button onClick={() => multiply(3)}>triple</button>\n}\n```\n\n### Async actions\n\nJust make the write function an async function and call `set` when you're ready.\n\n```jsx\nconst fetchCountAtom = atom(\n  (get) => get(countAtom),\n  async (_get, set, url) => {\n    const response = await fetch(url)\n    set(countAtom, (await response.json()).count)\n  }\n)\n\nfunction Controls() {\n  const [count, compute] = useAtom(fetchCountAtom)\n  return (\n    <button onClick={() => compute('http://count.host.com')}>compute</button>\n    ...\n```\n\n### Note about functional programming\n\nJotai's fluid interface is no accident — atoms are monads, just like promises!\nMonads are an [established](<https://en.wikipedia.org/wiki/Monad_(functional_programming)>)\npattern for modular, pure, robust and understandable code which is [optimized for change](https://overreacted.io/optimized-for-change/).\nRead more about [Jotai and monads.](https://jotai.org/docs/basics/functional-programming-and-jotai)\n\n## Links\n\n- [website](https://jotai.org)\n- [documentation](https://jotai.org/docs)\n- [course](https://egghead.io/courses/manage-application-state-with-jotai-atoms-2c3a29f0)\n"
  },
  {
    "path": "babel.config.mjs",
    "content": "export default (api, targets) => {\n  // https://babeljs.io/docs/en/config-files#config-function-api\n  const isTestEnv = api.env('test')\n\n  return {\n    babelrc: false,\n    ignore: ['./node_modules'],\n    presets: [\n      [\n        '@babel/preset-env',\n        {\n          loose: true,\n          modules: isTestEnv ? 'commonjs' : false,\n          targets: isTestEnv ? { node: 'current' } : targets,\n        },\n      ],\n    ],\n    plugins: [\n      [\n        '@babel/plugin-transform-react-jsx',\n        {\n          runtime: 'automatic',\n        },\n      ],\n      ['@babel/plugin-transform-typescript', { isTSX: true }],\n    ],\n  }\n}\n"
  },
  {
    "path": "benchmarks/.gitignore",
    "content": "/*.json\n/*.html\n"
  },
  {
    "path": "benchmarks/simple-read.ts",
    "content": "#!/usr/bin/env npx tsx\n\n/// <reference types=\"node\" />\n\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { add, complete, cycle, save, suite } from 'benny'\nimport { atom } from '../src/vanilla/atom.ts'\nimport type { PrimitiveAtom } from '../src/vanilla/atom.ts'\nimport { createStore } from '../src/vanilla/store.ts'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nconst createStateWithAtoms = (n: number) => {\n  let targetAtom: PrimitiveAtom<number> | undefined\n  const store = createStore()\n  for (let i = 0; i < n; ++i) {\n    const a = atom(i)\n    if (!targetAtom) {\n      targetAtom = a\n    }\n    store.set(a, i)\n  }\n  if (!targetAtom) {\n    throw new Error()\n  }\n  return [store, targetAtom] as const\n}\n\nconst main = async () => {\n  await suite(\n    'simple-read',\n    ...[2, 3, 4, 5, 6].map((n) =>\n      add(`atoms=${10 ** n}`, () => {\n        const [store, targetAtom] = createStateWithAtoms(10 ** n)\n        return () => store.get(targetAtom)\n      }),\n    ),\n    cycle(),\n    complete(),\n    save({\n      folder: __dirname,\n      file: 'simple-read',\n      format: 'json',\n    }),\n    save({\n      folder: __dirname,\n      file: 'simple-read',\n      format: 'chart.html',\n    }),\n  )\n}\n\nmain()\n"
  },
  {
    "path": "benchmarks/simple-write.ts",
    "content": "#!/usr/bin/env npx tsx\n\n/// <reference types=\"node\" />\n\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { add, complete, cycle, save, suite } from 'benny'\nimport { atom } from '../src/vanilla/atom.ts'\nimport type { PrimitiveAtom } from '../src/vanilla/atom.ts'\nimport { createStore } from '../src/vanilla/store.ts'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nconst createStateWithAtoms = (n: number) => {\n  let targetAtom: PrimitiveAtom<number> | undefined\n  const store = createStore()\n  for (let i = 0; i < n; ++i) {\n    const a = atom(i)\n    if (!targetAtom) {\n      targetAtom = a\n    }\n    store.set(a, i)\n  }\n  if (!targetAtom) {\n    throw new Error()\n  }\n  return [store, targetAtom] as const\n}\n\nconst main = async () => {\n  await suite(\n    'simple-write',\n    ...[2, 3, 4, 5, 6].map((n) =>\n      add(`atoms=${10 ** n}`, () => {\n        const [store, targetAtom] = createStateWithAtoms(10 ** n)\n        return () => store.set(targetAtom, (c) => c + 1)\n      }),\n    ),\n    cycle(),\n    complete(),\n    save({\n      folder: __dirname,\n      file: 'simple-write',\n      format: 'json',\n    }),\n    save({\n      folder: __dirname,\n      file: 'simple-write',\n      format: 'chart.html',\n    }),\n  )\n}\n\nmain()\n"
  },
  {
    "path": "benchmarks/subscribe-write.ts",
    "content": "#!/usr/bin/env npx tsx\n\n/// <reference types=\"node\" />\n\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { add, complete, cycle, save, suite } from 'benny'\nimport { atom } from '../src/vanilla/atom.ts'\nimport type { PrimitiveAtom } from '../src/vanilla/atom.ts'\nimport { createStore } from '../src/vanilla/store.ts'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nconst cleanupFns = new Set<() => void>()\nconst cleanup = () => {\n  cleanupFns.forEach((fn) => fn())\n  cleanupFns.clear()\n}\n\nconst createStateWithAtoms = (n: number) => {\n  let targetAtom: PrimitiveAtom<number> | undefined\n  const store = createStore()\n  for (let i = 0; i < n; ++i) {\n    const a = atom(i)\n    if (!targetAtom) {\n      targetAtom = a\n    }\n    store.get(a)\n    const unsub = store.sub(a, () => {\n      store.get(a)\n    })\n    cleanupFns.add(unsub)\n  }\n  if (!targetAtom) {\n    throw new Error()\n  }\n  return [store, targetAtom] as const\n}\n\nconst main = async () => {\n  await suite(\n    'subscribe-write',\n    ...[2, 3, 4, 5, 6].map((n) =>\n      add(`atoms=${10 ** n}`, () => {\n        cleanup()\n        const [store, targetAtom] = createStateWithAtoms(10 ** n)\n        return () => store.set(targetAtom, (c) => c + 1)\n      }),\n    ),\n    cycle(),\n    complete(),\n    save({\n      folder: __dirname,\n      file: 'subscribe-write',\n      format: 'json',\n    }),\n    save({\n      folder: __dirname,\n      file: 'subscribe-write',\n      format: 'chart.html',\n    }),\n  )\n}\n\nmain()\n"
  },
  {
    "path": "docs/basics/comparison.mdx",
    "content": "---\ntitle: Comparison\nnav: 7.02\n---\n\nJotai was born to solve extra re-render issues in React.\nAn extra re-render is when the render process produces the same UI result,\nwhere users won't see any differences.\n\nTo tackle this issue with React context (useContext + useState),\none would require many contexts and face some issues.\n\n- Provider hell: It's likely that your root component has many context providers, which is technically okay, and sometimes desirable to provide context in different subtree.\n- Dynamic addition/deletion: Adding a new context at runtime is not very nice, because you need to add a new provider and its children will be re-mounted.\n\nTraditionally, a top-down solution to this is to use a selector function.\nThe [use-context-selector](https://github.com/dai-shi/use-context-selector) library is one example.\nThe issue with this approach is the selector function needs to return\nreferentially equal values to prevent re-renders, and this often requires a memoization technique.\n\nJotai takes a bottom-up approach with the atomic model, inspired by [Recoil](https://recoiljs.org/).\nOne can build state combining atoms, and optimize renders based on atom dependency.\nThis avoids the need for memoization.\n\nJotai has two principles.\n\n- Primitive: Its basic API is simple, like `useState`.\n- Flexible: Atoms can derive another atom and form a graph. Atoms can also be updated by another arbitrary atom. It allows abstracting complex state models.\n\n### How is Jotai different from useContext of React?\n\nJotai's core API is minimalistic and makes it easy to build utilities based upon it.\n\n#### Analogy\n\nYou can view Jotai as a drop-in replacement to `useContext`. Except Jotai is aiming for simplicity, minimalistic API and can do much more than `useContext` & `useState`.\n\n#### Usage difference\n\nWe can see how we used to share data to children, compared to how we do it with Jotai. But let's use a real-world example where we have multiple `Context` in our app.\n\n```jsx\n// 1. useState local state\nconst Component = () => {\n  const [state, setState] = useState(0)\n}\n\n// 2. Lift local state up and share it via context\nconst StateContext = createContext()\nconst Parent = ({ children }) => {\n  return (\n    <StateContext.Provider value={useState(0)}>\n      {children}\n    </StateContext.Provider>\n  )\n}\nconst Component = () => {\n  const [state, setState] = useContext(StateContext)\n}\n\n// 3. Have multiple states and contexts\nconst State1Context = createContext()\nconst State2Context = createContext()\nconst Parent = ({ children }) => (\n  <State1Context.Provider value={useState(0)}>\n    <State2Context.Provider value={useState(0)}>\n      {children}\n    </State2Context.Provider>\n  </State1Context.Provider>\n)\nconst Component1 = () => {\n  const [state, setState] = useContext(State1Context)\n}\nconst Component2 = () => {\n  const [state, setState] = useContext(State2Context)\n}\n```\n\nNow let's see how Jotai simplify it for us. You can just use atoms instead of multiple `Context`.\n\n```jsx\nimport { Provider, atom, useAtom } from 'jotai'\nconst atom1 = atom(0)\nconst atom2 = atom(0)\n// Optional: you can use Provider's just like useContext,\n// ...but if you only need one,\n// ...you can just omit it and Jotai will use a default one (called Provider-less mode).\nconst Parent = ({ children }) => {\n  return <Provider>{children}</Provider>\n}\nconst Component1 = () => {\n  const [state, setState] = useAtom(atom1)\n}\nconst Component2 = () => {\n  const [state, setState] = useAtom(atom2)\n}\n```\n\n### How is Jotai different from Zustand?\n\n#### Name\n\nJotai means \"state\" in Japanese.\nZustand means \"state\" in German.\n\n#### Analogy\n\nJotai is like Recoil.\nZustand is like Redux.\n\n#### Where state resides\n\nTo hold states,\nBoth have stores that can exist either at module level or at context level.\nJotai is designed to be context first, and module second.\nZustand is designed to be module first, and context second.\n\n#### How to structure state\n\nJotai state consists of atoms (i.e. bottom-up).\nZustand state is one object (i.e. top-down).\n\n#### Technical difference\n\nThe major difference is the state model. Zustand is a single store (although you could create multiple separate stores), while Jotai consists of primitive atoms and allows composing them together. In this sense, it's the matter of programming mental model.\n\n#### When to use which\n\n- If you need a replacement for useState+useContext, Jotai fits well.\n- If you want a simple module state, Zustand fits well.\n- If code splitting is important, Jotai should perform well.\n- If you prefer Redux devtools, Zustand is good to go.\n- If you want to make use of Suspense, Jotai is the one.\n\n### How is Jotai different from Recoil?\n\n(Disclaimer: the author is not very familiar with Recoil, this may be biased and inaccurate.)\n\n#### Developer\n\n- Jotai is developed with collective work by a few developers in Poimandres (formerly react-spring) org.\n- Recoil is developed by a team at Facebook.\n\n#### Basis\n\n- Jotai is focusing on primitive APIs for easy learning, and it's unopinionated. (The same philosophy with Zustand)\n- Recoil is all-in-one, and it has various cache strategies.\n\n#### Technical difference\n\n- Jotai depends on atom object referential identities.\n- Recoil depends on atom string keys.\n\n#### When to use which\n\n- If you want to learn something new, either should work.\n- If you like Zustand, Jotai would also be pleasant.\n- If you need React Context alternatives, Jotai comes with enough features.\n- If you need to read and write atoms outside React, Jotai provides store API.\n- If you would try to create a new library, Jotai might give good primitives.\n- Otherwise, both are pretty similar about the general goals and basic techniques, so please try both and share your feedback with us.\n"
  },
  {
    "path": "docs/basics/concepts.mdx",
    "content": "---\ntitle: Concepts\nnav: 7.01\n---\n\nJotai is a library that will make you return to the basics of React development & keep everything simple.\n\n### From scratch\n\nBefore trying to compare Jotai with what we may have known previously, let's just dive straight into something very simple.\n\nThe React world is very much like our world, it's a big set of small entities, we call them components, and we know that they have their own state. Structuring your components to interact altogether will create your app.\n\nNow, the Jotai world also has its small entities, atoms, and they also have their state. Composing atoms will create your app state!\n\nJotai considers anything to be an atom, so you may say: `Huh, I need objects and arrays, filter them and then sort them out`.\nAnd here's the beauty of it, Jotai gracefully lets you create dumb atoms derived from even more dumb atoms.\n\nIf, for example, I have a page with 2 tabs: online friends and offline friends.\nI will have 2 atoms simply derived from a common, dumber source.\n\n```js\nconst dumbAtom = atom([{ name: 'Friend 1', online: false }])\nconst onlineAtom = atom((get) => get(dumbAtom).filter((item) => item.online))\nconst offlineAtom = atom((get) => get(dumbAtom).filter((item) => !item.online))\n```\n\nAnd you could keep going on complexity forever.\n\nAnother incredible feature of Jotai is the built-in ability to suspend when using asynchronous atoms. This is a relatively new feature that needs more experimentation, but is definitely the future of how we will build React apps. [Check out the docs](https://react.dev/blog/2022/03/29/react-v18#new-suspense-features) for more info.\n"
  },
  {
    "path": "docs/basics/functional-programming-and-jotai.mdx",
    "content": "---\ntitle: Functional programming and Jotai\nnav: 7.04\n---\n\n### Unexpected similarities\n\nIf you look at getter functions long enough, you may see a striking resemblance\nto a certain JavaScript language feature.\n\n```tsx\nconst nameAtom = atom('Visitor')\nconst countAtom = atom(1)\nconst greetingAtom = atom((get) => {\n  const name = get(nameAtom)\n  const count = get(countAtom)\n  return (\n    <div>\n      Hello, {name}! You have visited this page {count} times.\n    </div>\n  )\n})\n```\n\nCompare that code with `async`–`await`:\n\n```tsx\nconst namePromise = Promise.resolve('Visitor')\nconst countPromise = Promise.resolve(1)\nconst greetingPromise = (async function () {\n  const name = await namePromise\n  const count = await countPromise\n  return (\n    <div>\n      Hello, {name}! You have visited this page {count} times.\n    </div>\n  )\n})()\n```\n\nThis similarity is no coincidence. Both atoms and promises are **Monads**†, a\nconcept from functional programming. The syntax used in both `greetingAtom` and\n`greetingPromise` is known as **do-notation**, a syntax sugar for the plainer\nmonad interface.\n\n### About monads\n\nThe monad interface is responsible for the fluidity of the atom and promise\ninterfaces. The monad interface allowed us to define `greetingAtom` in terms of\n`nameAtom` and `countAtom`, and allowed us to define `greetingPromise` in terms\nof `namePromise` and `countPromise`.\n\nIf you're curious, a structure (like `Atom` or `Promise`) is a monad if you can\nimplement the following functions for it. A fun exercise is trying to implement\n`of`, `map` and `join` for Arrays.\n\n```ts\ntype SomeMonad<T> = /* for example... */ Array<T>\ndeclare function of<T>(plainValue: T): SomeMonad<T>\ndeclare function map<T, V>(\n  anInstance: SomeMonad<T>,\n  transformContents: (contents: T) => V,\n): SomeMonad<V>\ndeclare function join<T>(nestedInstances: SomeMonad<SomeMonad<T>>): SomeMonad<T>\n```\n\nThe shared heritage of Promises and Atoms means many patterns and best-practices\ncan be reused between them. Let's take a look at one.\n\n### Sequencing\n\nWhen talking about callback hell, we often mention the boilerplate, the\nindentation and the easy-to-miss mistakes. However, plumbing a single async\noperation into another single async operation was not the end of the callback\nstruggle. What if we made four network calls and needed to wait for them all?\nA snippet like this was common:\n\n```ts\nconst nPending = 4\nconst results: string[]\nfunction callback(err, data) {\n  if (err) throw err\n  results.push(data)\n  if (results.length === nPending) {\n    // do something with results...\n  }\n}\n```\n\nBut what if the results have different types? and the order was important? Well,\nwe'd have a lot more frustrating work to do! This logic would be duplicated at\neach usage, and would be easy to mess up. Since ES6, we simply call `Promise.all`:\n\n```ts\ndeclare function promiseAll<T>(promises: Array<Promise<T>>): Promise<Array<T>>\n```\n\n`Promise.all` \"rearranges\" `Array` and `Promise`. It turns out this concept,\n_sequencing_, can be implemented for all monad–_Traversable_ pairs. Many kinds\nof collections are Traversables, including Arrays. For example, this is a case\nof sequencing specialized for atoms and arrays:\n\n```ts\nfunction sequenceAtomArray<T>(atoms: Array<Atom<T>>): Atom<Array<T>> {\n  return atom((get) => atoms.map(get))\n}\n```\n\n### Culmination\n\nMonads have been an interest to mathematicians for 60 years, and to programmers\nfor 40. There are many resources out there on patterns for monads. Take a look\nat them! Here are a select few:\n\n- [_Inventing Monads_](https://stopa.io/post/247) by Stepan Parunashvili\n- [_How Monads Solve Problems_](https://thatsnomoon.dev/posts/ts-monads/) by ThatsNoMoon\n- Wiki page [list of monad tutorials](https://wiki.haskell.org/Monad_tutorials_timeline)\n- [Typeclassopedia](https://wiki.haskell.org/Typeclassopedia) (for the curious)\n\nLearning a neat trick on using promises may well translate to atoms, as\n`Promise.all` and `sequenceAtomArray` did. Monads are not magic, just unusually\nuseful, and a tool worth knowing.\n\n---\n\n_Notes_\n\n**[†]** The ES6 Promise is not a completely valid monad because it cannot nest other\nPromises, e.g. `Promise<Promise<number>>` is semantically equivalent to\n`Promise<number>`. This is why Promises only have a `.then`, and not both a\n`.map` and `.flatMap`. ES6 Promises are probably more properly described as\n\"monadic\" rather than as monads.\n\nUnlike ES6 Promises, the ES6 Array is a completely lawful monad.\n"
  },
  {
    "path": "docs/basics/showcase.mdx",
    "content": "---\ntitle: Showcase\nnav: 7.03\n---\n\n- Text Length example [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://githubbox.com/pmndrs/jotai/tree/main/examples/text_length)\n\n  Count the length and show the uppercase of any text.\n\n- Hacker News example [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://githubbox.com/pmndrs/jotai/tree/main/examples/hacker_news)\n\n  Demonstrate a news article with Jotai, hit next to see more articles.\n\n- Todos example [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://githubbox.com/pmndrs/jotai/tree/main/examples/todos)\n\n  Record your todo list by typing them into this app, check them off if you have completed the task, and switch between `Completed` and `Incompleted` to see the status of your task.\n\n- Todos example with atomFamily and localStorage [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://githubbox.com/pmndrs/jotai/tree/main/examples/todos_with_atomFamily)\n\n  Implement a todo list using atomFamily and localStorage. You can store your todo list to localStorage by clicking `Save to localStorage`, then remove your todo list and try restoring them by clicking `Load from localStorage`.\n\n- Clock with Next.js [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/nextjs-with-jotai-5ylrj)\n\n  An UTC time electronic clock implementation using Next.js and Jotai.\n\n- Tic Tac Toe game [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/jotai-tic-tac-6cg3h)\n\n  A game of tic tac toe implemented with Jotai.\n\n- React Three Fiber demo [![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square&logo=codesandbox)](https://codesandbox.io/s/jotai-r3f-fri9d)\n\n  A simple demo to use Jotai with react-three-fiber\n"
  },
  {
    "path": "docs/core/atom.mdx",
    "content": "---\ntitle: atom\ndescription: This doc describes core `jotai` bundle.\nnav: 2.01\nkeywords: atom,primitive,derived,debug,label,onmount\n---\n\n## atom\n\nThe `atom` function is to create an atom config.\nWe call it \"atom config\" as it's just a definition and it doesn't yet hold a value.\nWe may also call it just \"atom\" if the context is clear.\n\nAn atom config is an immutable object. The atom config object doesn't hold a value. The atom value exists in a store.\n\nTo create a primitive atom (config), all you need is to provide an initial value.\n\n```js\nimport { atom } from 'jotai'\n\nconst priceAtom = atom(10)\nconst messageAtom = atom('hello')\nconst productAtom = atom({ id: 12, name: 'good stuff' })\n```\n\nYou can also create derived atoms. We have three patterns:\n\n- Read-only atom\n- Write-only atom\n- Read-Write atom\n\nTo create derived atoms, we pass a read function and an optional write function.\n\n```js\nconst readOnlyAtom = atom((get) => get(priceAtom) * 2)\nconst writeOnlyAtom = atom(\n  null, // it's a convention to pass `null` for the first argument\n  (get, set, update) => {\n    // `update` is any single value we receive for updating this atom\n    set(priceAtom, get(priceAtom) - update.discount)\n    // or we can pass a function as the second parameter\n    // the function will be invoked,\n    //  receiving the atom's current value as its first parameter\n    set(priceAtom, (price) => price - update.discount)\n  },\n)\nconst readWriteAtom = atom(\n  (get) => get(priceAtom) * 2,\n  (get, set, newPrice) => {\n    set(priceAtom, newPrice / 2)\n    // you can set as many atoms as you want at the same time\n  },\n)\n```\n\n`get` in the read function is to read the atom value.\nIt's reactive and read dependencies are tracked.\n\n`get` in the write function is also to read atom value, but it's not tracked.\nFurthermore, it can't read unresolved async values in Jotai v1 API.\n\n`set` in the write function is to write atom value.\nIt will invoke the write function of the target atom.\n\n### Note about creating an atom in render function\n\nAtom configs can be created anywhere, but referential equality is important.\nThey can be created dynamically too.\nTo create an atom in render function, `useMemo` or `useRef` is required to get a stable reference. If in doubt about using `useMemo` or `useRef` for memoization, use `useMemo`.\nOtherwise, it can cause infinite loop with `useAtom`.\n\n```js\nconst Component = ({ value }) => {\n  const valueAtom = useMemo(() => atom({ value }), [value])\n  // ...\n}\n```\n\n### Signatures\n\n```ts\n// primitive atom\nfunction atom<Value>(initialValue: Value): PrimitiveAtom<Value>\n\n// read-only atom\nfunction atom<Value>(read: (get: Getter) => Value): Atom<Value>\n\n// writable derived atom\nfunction atom<Value, Args extends unknown[], Result>(\n  read: (get: Getter) => Value,\n  write: (get: Getter, set: Setter, ...args: Args) => Result,\n): WritableAtom<Value, Args, Result>\n\n// write-only derived atom\nfunction atom<Value, Args extends unknown[], Result>(\n  read: Value,\n  write: (get: Getter, set: Setter, ...args: Args) => Result,\n): WritableAtom<Value, Args, Result>\n```\n\n- `initialValue`: the initial value that the atom will return until its value is changed.\n- `read`: a function that's evaluated whenever the atom is read. The signature of `read` is `(get) => Value`, and `get` is a function that takes an atom config and returns its value stored in Provider as described below. Dependency is tracked, so if `get` is used for an atom at least once, the `read` will be reevaluated whenever the atom value is changed.\n- `write`: a function mostly used for mutating atom's values, for a better description; it gets called whenever we call the second value of the returned pair of `useAtom`, the `useAtom()[1]`. The default value of this function in the primitive atom will change the value of that atom. The signature of `write` is `(get, set, ...args) => Result`. `get` is similar to the one described above, but it doesn't track the dependency. `set` is a function that takes an atom config and a new value which then updates the atom value in Provider. `...args` is the arguments that we receive when we call `useAtom()[1]`. `Result` is the return value of the `write` function.\n\n```js\nconst primitiveAtom = atom(initialValue)\nconst derivedAtomWithRead = atom(read)\nconst derivedAtomWithReadWrite = atom(read, write)\nconst derivedAtomWithWriteOnly = atom(null, write)\n```\n\nThere are two kinds of atoms: a writable atom and a read-only atom. Primitive atoms are always writable. Derived atoms are writable if the `write` is specified. The `write` of primitive atoms is equivalent to the `setState` of `React.useState`.\n\n### `debugLabel` property\n\nThe created atom config can have an optional property `debugLabel`. The debug label is used to display the atom in debugging. See [Debugging guide](../guides/debugging.mdx) for more information.\n\nNote: While, the debug labels don’t have to be unique, it’s generally recommended to make them distinguishable.\n\n### `onMount` property\n\nThe created atom config can have an optional property `onMount`. `onMount` is a function which takes a function `setAtom` and returns `onUnmount` function optionally.\n\nThe `onMount` function is called when the atom is subscribed in the provider in the first time,\nand `onUnmount` is called when it’s no longer subscribed.\nIn some cases (like [React strict mode](https://react.dev/reference/react/StrictMode)),\nan atom can be unmounted and then mounted immediately.\n\n```js\nconst anAtom = atom(1)\nanAtom.onMount = (setAtom) => {\n  console.log('atom is mounted in provider')\n  setAtom(c => c + 1) // increment count on mount\n  return () => { ... } // return optional onUnmount function\n}\n\nconst Component = () => {\n  // `onMount` will be called when the component is mounted in the following cases:\n  useAtom(anAtom)\n  useAtomValue(anAtom)\n\n  // however, in the following cases,\n  //  `onMount` will not be called because the atom is not subscribed:\n  useSetAtom(anAtom)\n  useAtomCallback(\n    useCallback((get) => get(anAtom), []),\n  )\n  // ...\n}\n```\n\nCalling `setAtom` function will invoke the atom’s `write`. Customizing `write` allows changing the behavior.\n\n```js\nconst countAtom = atom(1)\nconst derivedAtom = atom(\n  (get) => get(countAtom),\n  (get, set, action) => {\n    if (action.type === 'init') {\n      set(countAtom, 10)\n    } else if (action.type === 'inc') {\n      set(countAtom, (c) => c + 1)\n    }\n  },\n)\nderivedAtom.onMount = (setAtom) => {\n  setAtom({ type: 'init' })\n}\n```\n\n### Advanced API\n\nSince Jotai v2, the `read` function has the second argument `options`.\n\n#### `options.signal`\n\nIt uses [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) so that you can abort async functions.\nAbort is triggered before new calculation (invoking `read` function) is started.\n\nHow to use it:\n\n```ts\nconst readOnlyDerivedAtom = atom(async (get, { signal }) => {\n  // use signal to abort your function\n})\n\nconst writableDerivedAtom = atom(\n  async (get, { signal }) => {\n    // use signal to abort your function\n  },\n  (get, set, arg) => {\n    // ...\n  },\n)\n```\n\nThe `signal` value is [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).\nYou can check `signal.aborted` boolean value, or use `abort` event with `addEventListener`.\n\nFor `fetch` use case, we can simply pass `signal`.\n\nSee the below example for `fetch` usage.\n\n#### `options.setSelf`\n\nIt's a special function to invoke the write function of the self atom.\n\n⚠️ It's provided primarily for internal usage and third-party library authors. Read the source code carefully to understand the behavior. Check release notes for any breaking/non-breaking changes.\n\n## Stackblitz\n\n<Stackblitz id=\"vitejs-vite-ccqxix\" file=\"src%2FApp.tsx\" />\n\n```tsx\nimport { Suspense } from 'react'\nimport { atom, useAtom } from 'jotai'\n\nconst userIdAtom = atom(1)\nconst userAtom = atom(async (get, { signal }) => {\n  const userId = get(userIdAtom)\n  const response = await fetch(\n    `https://jsonplaceholder.typicode.com/users/${userId}?_delay=2000`,\n    { signal },\n  )\n  return response.json()\n})\n\nconst Controls = () => {\n  const [userId, setUserId] = useAtom(userIdAtom)\n  return (\n    <div>\n      User Id: {userId}\n      <button onClick={() => setUserId((c) => c - 1)}>Prev</button>\n      <button onClick={() => setUserId((c) => c + 1)}>Next</button>\n    </div>\n  )\n}\n\nconst UserName = () => {\n  const [user] = useAtom(userAtom)\n  return <div>User name: {user.name}</div>\n}\n\nconst App = () => (\n  <>\n    <Controls />\n    <Suspense fallback=\"Loading...\">\n      <UserName />\n    </Suspense>\n  </>\n)\n\nexport default App\n```\n"
  },
  {
    "path": "docs/core/provider.mdx",
    "content": "---\ntitle: Provider\ndescription: This doc describes core `jotai` bundle.\nnav: 2.04\nkeywords: provider,usestore,ssr\n---\n\n## Provider\n\nThe `Provider` component is to provide state for a component sub tree.\nMultiple Providers can be used for multiple subtrees, and they can even be nested.\nThis works just like React Context.\n\nIf an atom is used in a tree without a Provider,\nit will use the default state. This is so-called provider-less mode.\n\nProviders are useful for three reasons:\n\n1. To provide a different state for each sub tree.\n2. To accept initial values of atoms.\n3. To clear all atoms by remounting.\n\n```jsx\nconst SubTree = () => (\n  <Provider>\n    <Child />\n  </Provider>\n)\n```\n\n### Signatures\n\n```ts\nconst Provider: React.FC<{\n  store?: Store\n}>\n```\n\nAtom configs don't hold values. Atom values reside in separate stores. A Provider is a component that contains a store and provides atom values under the component tree. A Provider works like React context provider. If you don't use a Provider, it works as provider-less mode with a default store. A Provider will be necessary if we need to hold different atom values for different component trees. Provider can take an optional prop `store`.\n\n```jsx\nconst Root = () => (\n  <Provider>\n    <App />\n  </Provider>\n)\n```\n\n### `store` prop\n\nA Provider accepts an optional prop `store` that you can use for the Provider subtree.\n\n#### Example\n\n```jsx\nconst myStore = createStore()\n\nconst Root = () => (\n  <Provider store={myStore}>\n    <App />\n  </Provider>\n)\n```\n\n## useStore\n\nThis hook returns a store within the component tree.\n\n```jsx\nconst Component = () => {\n  const store = useStore()\n  // ...\n}\n```\n"
  },
  {
    "path": "docs/core/store.mdx",
    "content": "---\ntitle: Store\ndescription: This doc describes core `jotai` bundle.\nnav: 2.03\nkeywords: store,createstore,getdefaultstore,defaultstore\n---\n\n## createStore\n\nThis function is to create a new empty store.\nThe store can be used to pass in `Provider`.\n\nThe store has three methods: `get` for getting atom values,\n`set` for setting atom values, and `sub` for subscribing to atom changes.\n\n```jsx\nconst myStore = createStore()\n\nconst countAtom = atom(0)\nmyStore.set(countAtom, 1)\nconst unsub = myStore.sub(countAtom, () => {\n  console.log('countAtom value is changed to', myStore.get(countAtom))\n})\n// unsub() to unsubscribe\n\nconst Root = () => (\n  <Provider store={myStore}>\n    <App />\n  </Provider>\n)\n```\n\n## getDefaultStore\n\nThis function returns a default store that is used in provider-less mode.\n\n```js\nconst defaultStore = getDefaultStore()\n```\n"
  },
  {
    "path": "docs/core/use-atom.mdx",
    "content": "---\ntitle: useAtom\ndescription: This doc describes core `jotai` bundle.\nnav: 2.02\nkeywords: use,useatom,useatomvalue,usesetatom,atomvalue,setatom\n---\n\n## useAtom\n\nThe `useAtom` hook is used to read an atom from the state.\nThe state can be seen as a WeakMap of atom configs and atom values.\n\nThe `useAtom` hook returns the atom value and an update function as a tuple,\njust like React's `useState`.\nIt takes an atom config created with `atom()` as a parameter.\n\nAt the creation of the atom config, there is no value associated with it.\nOnly once the atom is used via `useAtom`, does the initial value get stored in the state.\nIf the atom is a derived atom, the read function is called to compute its initial value.\nWhen an atom is no longer used, meaning all the components using it are unmounted\nand the atom config no longer exists, the value in the state is garbage collected.\n\n```js\nconst [value, setValue] = useAtom(anAtom)\n```\n\nThe `setValue` takes just one argument, which will be passed\nto the write function of the atom as the third parameter.\nThe end result depends on how the write function is implemented.\nIf the write function is not explicitly set, the atom will simply receive the value passed as a parameter to `setValue`.\n\n**Note:** as mentioned in the _atom_ section, referential equality is important when creating atoms,\nso you need to handle it properly otherwise it can cause infinite loops.\n\n```js\nconst stableAtom = atom(0)\nconst Component = () => {\n  const [atomValue] = useAtom(atom(0)) // This will cause an infinite loop since the atom instance is being recreated in every render\n  const [atomValue] = useAtom(stableAtom) // This is fine\n  const [derivedAtomValue] = useAtom(\n    useMemo(\n      // This is also fine\n      () => atom((get) => get(stableAtom) * 2),\n      [],\n    ),\n  )\n}\n```\n\n**Note**: Remember that React is responsible for calling your component, meaning it has to be idempotent, ready to be called multiple times. You will often see an extra re-render even if no props or atoms have changed. An extra re-render without a commit is an expected behavior, since it is the default behavior of useReducer in React 18.\n\n### Signatures\n\n```ts\n// primitive or writable derived atom\nfunction useAtom<Value, Update>(\n  atom: WritableAtom<Value, Update>,\n  options?: { store?: Store },\n): [Value, SetAtom<Update>]\n\n// read-only atom\nfunction useAtom<Value>(\n  atom: Atom<Value>,\n  options?: { store?: Store },\n): [Value, never]\n```\n\n### How atom dependency works\n\nEvery time we invoke the \"read\" function, we refresh the dependencies and dependents.\n\n> The read function is the first parameter of the atom.\n> If B depends on A, it means that A is a dependency of B, and B is a dependent on A.\n\n```js\nconst uppercaseAtom = atom((get) => get(textAtom).toUpperCase())\n```\n\nWhen you create the atom, the dependency will not be present. On first use, we run the read function and conclude that `uppercaseAtom` depends on `textAtom`. So `uppercaseAtom` is added to the dependents of `textAtom`.\nWhen we re-run the read function of `uppercaseAtom` (because its `textAtom` dependency is updated),\nthe dependency is created again, which is the same in this case. We then remove stale dependents from `textAtom` and replace them with their latest versions.\n\n### Atoms can be created on demand\n\nWhile the basic examples here show defining atoms globally outside components,\nthere's no restrictions about where or when we can create an atom.\nAs long as we remember that atoms are identified by their object referential identity,\nwe can create them anytime.\n\nIf you create atoms in render functions, you would typically want to use\na hook like `useRef` or `useMemo` for memoization. If not, the atom would be re-created each time the component renders.\n\nYou can create an atom and store it with `useState` or even in another atom.\nSee an example in [issue #5](https://github.com/pmndrs/jotai/issues/5).\n\nYou can also cache atoms somewhere globally.\nSee [this example](https://twitter.com/dai_shi/status/1317653548314718208) or\n[that example](https://github.com/pmndrs/jotai/issues/119#issuecomment-706046321).\n\nCheck [`atomFamily`](../utilities/family.mdx) in utils for parameterized atoms.\n\n## useAtomValue\n\n```jsx\nconst countAtom = atom(0)\n\nconst Counter = () => {\n  const setCount = useSetAtom(countAtom)\n  const count = useAtomValue(countAtom)\n\n  return (\n    <>\n      <div>count: {count}</div>\n      <button onClick={() => setCount(count + 1)}>+1</button>\n    </>\n  )\n}\n```\n\nSimilar to the `useSetAtom` hook, `useAtomValue` allows you to access a read-only atom. Nonetheless, it can also be used to access read-write atom's values.\n\n## useSetAtom\n\n```jsx\nconst switchAtom = atom(false)\n\nconst SetTrueButton = () => {\n  const setCount = useSetAtom(switchAtom)\n  const setTrue = () => setCount(true)\n\n  return (\n    <div>\n      <button onClick={setTrue}>Set True</button>\n    </div>\n  )\n}\n\nconst SetFalseButton = () => {\n  const setCount = useSetAtom(switchAtom)\n  const setFalse = () => setCount(false)\n\n  return (\n    <div>\n      <button onClick={setFalse}>Set False</button>\n    </div>\n  )\n}\n\nexport default function App() {\n  const state = useAtomValue(switchAtom)\n\n  return (\n    <div>\n      State: <b>{state.toString()}</b>\n      <SetTrueButton />\n      <SetFalseButton />\n    </div>\n  )\n}\n```\n\nIn case you need to update a value of an atom without reading it, you can use `useSetAtom()`.\n\nThis is especially useful when the performance is a concern, as the `const [, setValue] = useAtom(valueAtom)` will cause unnecessary rerenders on each `valueAtom` update.\n"
  },
  {
    "path": "docs/extensions/cache.mdx",
    "content": "---\ntitle: Cache\ndescription: This doc describes cache extension.\nnav: 4.07\nkeywords: cache\n---\n\nJotai provides primitive functions to optimize re-renders.\nIt's designed to hold only \"current\" atom values,\nand it doesn't cache older values.\n\nCaching is sometimes useful. For example, if an async atom\ntriggers network requests, we may want to cache the responses.\n\n[jotai-cache](https://github.com/jotai-labs/jotai-cache) is\na third-party library to help such use cases.\n\n### Install\n\n```\nnpm install jotai-cache\n```\n\n## atomWithCache\n\n```js\natomWithCache(read, options): Atom\n```\n\n`atomWithCache` creates a new read-only atom with cache.\n\n### Parameters\n\n**read**: read function to define the read-only atom.\n\n**options** (optional): an object of options to customize the behavior of the atom\n\n### Options\n\n**size** (optional): maximum size of cache items.\n\n**shouldRemove** (optional): a function to check if cache items should be removed.\n\n**areEqual** (optional): a function to compare atom values.\n\n### Examples\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { atomWithCache } from 'jotai-cache'\n\nconst idAtom = atom(1)\n\nconst normalAtom = atom(async (get) => {\n  const id = get(idAtom)\n  const response = await fetch(`https://reqres.in/api/users/${id}?delay=1`)\n  return response.json()\n})\n\nconst cachedAtom = atomWithCache(async (get) => {\n  const id = get(idAtom)\n  const response = await fetch(`https://reqres.in/api/users/${id}?delay=1`)\n  return response.json()\n})\n\nconst NormalUser = () => {\n  const [{ data }] = useAtom(normalAtom)\n  return (\n    <div>\n      <h1>User (normal atom)</h1>\n      <ul>\n        <li>ID: {data.id}</li>\n        <li>First name: {data.first_name}</li>\n        <li>Last name: {data.last_name}</li>\n      </ul>\n    </div>\n  )\n}\n\nconst CachedUser = () => {\n  const [{ data }] = useAtom(cachedAtom)\n  return (\n    <div>\n      <h1>User (cached atom)</h1>\n      <ul>\n        <li>ID: {data.id}</li>\n        <li>First name: {data.first_name}</li>\n        <li>Last name: {data.last_name}</li>\n      </ul>\n    </div>\n  )\n}\n\nconst App = () => {\n  const [id, setId] = useAtom(idAtom)\n  return (\n    <div>\n      ID: {id}{' '}\n      <button type=\"button\" onClick={() => setId((c) => c - 1)}>\n        Prev\n      </button>{' '}\n      <button type=\"button\" onClick={() => setId((c) => c + 1)}>\n        Next\n      </button>\n      <hr />\n      <Suspense fallback=\"Loading...\">\n        <CachedUser />\n      </Suspense>\n      <hr />\n      <Suspense fallback=\"Loading...\">\n        <NormalUser />\n      </Suspense>\n    </div>\n  )\n}\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-p86ajq\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/extensions/effect.mdx",
    "content": "---\ntitle: Effect\ndescription: A Jōtai utility package for reactive side effects\nnav: 4.03\nkeywords: effect, atom effect, side effect, side-effect, sideeffect\n---\n\n[jotai-effect](https://github.com/jotaijs/jotai-effect) is a utility package for reactive side effects in Jotai.\n\n## Install\n\n```\nnpm install jotai-effect\n```\n\n## observe\n\n`observe` mounts an `effect` to watch state changes on a Jotai `store`. It's useful for running global side effects or logic at the store level.\n\nIf you don't have access to the store object and are not using the default store, use `atomEffect` or `withAtomEffect` instead.\n\n### Signature\n\n```ts\ntype Cleanup = () => void\n\ntype Effect = (\n  get: Getter & { peek: Getter }\n  set: Setter & { recurse: Setter }\n) => Cleanup | void\n\ntype Unobserve = () => void\n\nfunction observe(effect: Effect, store?: Store): Unobserve\n```\n\n**effect:** A function for observing and reacting to atom state changes.\n\n**store:** A Jotai store to mount the effect on. Defaults to the global store if not provided.\n\n**returns:** A stable function that removes the effect from the store and cleans up any internal references.\n\n### Usage\n\n```js\nimport { observe } from 'jotai-effect'\n\nconst unobserve = observe((get, set) => {\n  set(logAtom, `someAtom changed: ${get(someAtom)}`)\n})\n\nunobserve()\n```\n\nThis allows you to run Jotai state-dependent logic outside React's lifecycle, ideal for application-wide effects.\n\n### Usage With React\n\nPass the store to both `observe` and the `Provider` to ensure the effect is mounted to the correct store.\n\n```tsx\nconst store = createStore()\nconst unobserve = observe((get, set) => {\n  set(logAtom, `someAtom changed: ${get(someAtom)}`)\n}, store)\n\n<Provider store={store}>...</Provider>\n```\n\n<Stackblitz id=\"vitejs-vite-uk7p8i5q\" file=\"src%2FApp.tsx\" />\n\n## atomEffect\n\n`atomEffect` creates an atom for declaring side effects that react to state changes when mounted.\n\n### Signature\n\n```ts\nfunction atomEffect(effect: Effect): Atom<void>\n```\n\n**effect:** A function for observing and reacting to atom state changes.\n\n### Usage\n\n```js\nimport { atomEffect } from 'jotai-effect'\n\nconst logEffect = atomEffect((get, set) => {\n  set(logAtom, get(someAtom)) // Runs on mount or when someAtom changes\n  return () => {\n    set(logAtom, 'unmounting') // Cleanup on unmount\n  }\n})\n\n// activates the atomEffect while Component is mounted\nfunction Component() {\n  useAtom(logEffect)\n}\n```\n\n## withAtomEffect\n\n`withAtomEffect` binds an effect to a clone of the target atom. The effect is active while the cloned atom is mounted.\n\n### Signature\n\n```ts\nfunction withAtomEffect<T>(targetAtom: Atom<T>, effect: Effect): Atom<T>\n```\n\n**targetAtom:** The atom to which the effect is bound.\n\n**effect:** A function for observing and reacting to atom state changes.\n\n**Returns:** An atom that is equivalent to the target atom but having a bound effect.\n\n### Usage\n\n```js\nimport { withAtomEffect } from 'jotai-effect'\n\nconst valuesAtom = withAtomEffect(atom(null), (get, set) => {\n  set(valuesAtom, get(countAtom))\n  return () => {\n    // cleanup\n  }\n})\n```\n\n## Dependency Management\n\nAside from mount events, the effect runs when any of its dependencies change value.\n\n- **Sync:**\n  All atoms accessed with `get` inside the effect are added to the atom's dependencies.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    // updates whenever `anAtom` changes value\n    get(anAtom)\n  })\n  ```\n\n  </details>\n\n- **Async:**\n  Asynchronous `get` calls do not add dependencies.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    setTimeout(() => {\n      // does not add `anAtom` as a dependency\n      get(anAtom)\n    })\n  })\n  ```\n\n  </details>\n\n- **Cleanup:**\n  `get` calls in cleanup do not add dependencies.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    return () => {\n      // does not add `anAtom` as a dependency\n      get(anAtom)\n    }\n  })\n  ```\n\n  </details>\n\n- **Dependency Map Recalculation:**\n  Dependencies are recalculated on every run.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    if (get(isEnabledAtom)) {\n      // `isEnabledAtom` and `anAtom` are dependencies\n      const aValue = get(anAtom)\n    } else {\n      // `isEnabledAtom` and `anotherAtom` are dependencies\n      const anotherValue = get(anotherAtom)\n    }\n  })\n  ```\n\n  </details>\n\n## Effect Behavior\n\n- **Executes Synchronously:**\n  `effect` runs synchronous in the current task after synchronous evaluations complete.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  const logCounts = atomEffect((get, set) => {\n    set(logAtom, `count is ${get(countAtom)}`)\n  })\n  const actionAtom = atom(null, (get, set) => {\n    get(logAtom) // 'count is 0'\n    set(countAtom, (value) => value + 1) // effect runs synchronously\n    get(logAtom) // 'count is 1'\n  })\n  store.sub(logCounts, () => {})\n  store.set(actionAtom)\n  ```\n\n  </details>\n\n- **Batched Updates:**\n  Multiple synchronous updates are batched as a single atomic transaction.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  const tensAtom = atom(0)\n  const onesAtom = atom(0)\n  const updateTensAndOnes = atom(null, (get, set) => {\n    set(tensAtom, (value) => value + 1)\n    set(onesAtom, (value) => value + 1)\n  })\n  const combos = atom([])\n  const effectAtom = atomEffect((get, set) => {\n    const value = get(tensAtom) * 10 + get(onesAtom)\n    set(combos, (arr) => [...arr, value])\n  })\n  store.sub(effectAtom, () => {})\n  store.set(updateTensAndOnes)\n  store.get(combos) // [00, 11]\n  ```\n\n  </details>\n\n- **Resistant to Infinite Loops:**\n  `atomEffect` avoids rerunning when it updates a value that it is watching.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    get(countAtom)\n    set(countAtom, (value) => value + 1) // Will not loop\n  })\n  ```\n\n  </details>\n\n- **Cleanup Function:**\n  The cleanup function is invoked on unmount or before re-evaluation.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    const intervalId = setInterval(() => set(clockAtom, Date.now()))\n    return () => clearInterval(intervalId)\n  })\n  ```\n\n  </details>\n\n- **Idempotency:**\n  `atomEffect` runs once per state change, regardless of how many times it is referenced.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  let i = 0\n  const effectAtom = atomEffect(() => {\n    get(countAtom)\n    i++\n  })\n  store.sub(effectAtom, () => {})\n  store.sub(effectAtom, () => {})\n  store.set(countAtom, (value) => value + 1)\n  console.log(i) // 1\n  ```\n\n  </details>\n\n- **Conditionally Running Effects:**\n  `atomEffect` only runs when mounted.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atom((get) => {\n    if (get(isEnabledAtom)) {\n      get(effectAtom)\n    }\n  })\n  ```\n\n  </details>\n\n- **Supports Peek:**\n  Use `get.peek` to read atom data without subscribing.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  const countAtom = atom(0)\n  atomEffect((get, set) => {\n    const count = get.peek(countAtom) // Will not add `countAtom` as a dependency\n  })\n  ```\n\n  </details>\n\n- **Supports Recursion:**\n  Recursion is supported with `set.recurse` but not in cleanup.\n\n  <!-- prettier-ignore -->\n  <details style=\"cursor: pointer; user-select: none;\">\n    <summary>Example</summary>\n\n  ```js\n  atomEffect((get, set) => {\n    const count = get(countAtom)\n    if (count % 10 === 0) {\n      return\n    }\n    set.recurse(countAtom, (value) => value + 1)\n  })\n  ```\n\n  </details>\n"
  },
  {
    "path": "docs/extensions/immer.mdx",
    "content": "---\ntitle: Immer\ndescription: This doc describes Immer extension.\nnav: 4.04\nkeywords: immer\n---\n\n### Install\n\nYou have to install `immer` and `jotai-immer` to use this feature.\n\n```\nnpm install immer jotai-immer\n```\n\n## atomWithImmer\n\n`atomWithImmer` creates a new atom similar to the regular [`atom`](../core/atom.mdx) with a different `writeFunction`. In this bundle, we don't have read-only atoms, because the point of these functions is the immer produce(mutability) function.\nThe signature of writeFunction is `(get, set, update: (draft: Draft<Value>) => void) => void`.\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithImmer } from 'jotai-immer'\n\nconst countAtom = atomWithImmer({ value: 0 })\n\nconst Counter = () => {\n  const [count] = useAtom(countAtom)\n  return <div>count: {count.value}</div>\n}\n\nconst Controls = () => {\n  const [, setCount] = useAtom(countAtom)\n  // setCount === update : (draft: Draft<Value>) => void\n  const inc = () =>\n    setCount((draft) => {\n      ++draft.value\n    })\n  return <button onClick={inc}>+1</button>\n}\n```\n\n### Examples\n\nCheck this example with atomWithImmer:\n\n<Stackblitz id=\"vitejs-vite-tblppw\" file=\"src%2FApp.tsx\" />\n\n## withImmer\n\n`withImmer` takes an atom and returns a derived atom, same as `atomWithImmer` it has a different `writeFunction`.\n\n```jsx\nimport { useAtom, atom } from 'jotai'\nimport { withImmer } from 'jotai-immer'\n\nconst primitiveAtom = atom({ value: 0 })\nconst countAtom = withImmer(primitiveAtom)\n\nconst Counter = () => {\n  const [count] = useAtom(countAtom)\n  return <div>count: {count.value}</div>\n}\n\nconst Controls = () => {\n  const [, setCount] = useAtom(countAtom)\n  // setCount === update : (draft: Draft<Value>) => void\n  const inc = () =>\n    setCount((draft) => {\n      ++draft.value\n    })\n  return <button onClick={inc}>+1</button>\n}\n```\n\n### Examples\n\nCheck this example with withImmer:\n\n<Stackblitz id=\"vitejs-vite-jwfjqm\" file=\"src%2FApp.tsx\" />\n\n## useImmerAtom\n\nThis hook takes an atom and replaces the atom's `writeFunction` with the new immer-like `writeFunction` like the previous helpers.\n\n```jsx\nimport { atom } from 'jotai'\nimport { useImmerAtom } from 'jotai-immer'\n\nconst primitiveAtom = atom({ value: 0 })\n\nconst Counter = () => {\n  const [count] = useImmerAtom(primitiveAtom)\n  return <div>count: {count.value}</div>\n}\n\nconst Controls = () => {\n  const [, setCount] = useImmerAtom(primitiveAtom)\n  // setCount === update : (draft: Draft<Value>) => void\n  const inc = () =>\n    setCount((draft) => {\n      ++draft.value\n    })\n  return <button onClick={inc}>+1</button>\n}\n```\n\nIt would be better if you don't use `withImmer` and `atomWithImmer` with `useImmerAtom` because they provide the immer-like `writeFunction` and we don't need to create a new one.\n\nYou can use `useSetImmerAtom` if you need only the setter part of `useImmerAtom`.\n\n### Examples\n\nCheck this example with useImmerAtom:\n\n<Stackblitz id=\"vitejs-vite-k2rixl\" file=\"src%2FApp.tsx\" />\n\n## Demo\n\n<Stackblitz id=\"vitejs-vite-tksbwq\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/extensions/location.mdx",
    "content": "---\ntitle: Location\ndescription: This doc describes window.location extension.\nnav: 4.06\nkeywords: location,hash\n---\n\nTo deal with `window.location`, we have some functions to create atoms.\n\n### Install\n\nYou have to install `jotai-location` to use this feature.\n\n```\nnpm install jotai-location\n```\n\n## atomWithLocation\n\n```js\natomWithLocation(options): PrimitiveAtom\n```\n\n`atomWithLocation` creates a new atom that links to `window.location`.\n\nTypically, you should only instantiate `atomWithLocation` once per application. This is because changes made to one instance might not be reflected in others.\nAs this atom is designed to synchronize with the `window.location` object, using multiple instances can lead to unpredictable behavior.\n\n### Parameters\n\n**options** (optional): an object of options to customize the behavior of the atom\n\n### Options\n\n**preloaded** (optional): preloaded location value to avoid getting location at initialization.\n\n**replace** (optional): a boolean to indicate to use `replaceState` instead of `pushState`.\n\n**getLocation** (optional): a custom function to get location value.\n\n**applyLocation** (optional): a custom function to set location value.\n\n**subscribe** (optional): a custom function to subscribe to location change.\n\n### Examples\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithLocation } from 'jotai-location'\n\nconst locationAtom = atomWithLocation()\n\nconst App = () => {\n  const [loc, setLoc] = useAtom(locationAtom)\n  return (\n    <ul>\n      <li>\n        <button\n          style={{\n            fontWeight: loc.pathname === '/' ? 'bold' : 'normal',\n          }}\n          onClick={() => setLoc((prev) => ({ ...prev, pathname: '/' }))}\n        >\n          Home\n        </button>\n      </li>\n      <li>\n        <button\n          style={{\n            fontWeight:\n              loc.pathname === '/foo' && !loc.searchParams?.get('bar')\n                ? 'bold'\n                : 'normal',\n          }}\n          onClick={() =>\n            setLoc((prev) => ({\n              ...prev,\n              pathname: '/foo',\n              searchParams: new URLSearchParams(),\n            }))\n          }\n        >\n          Foo\n        </button>\n      </li>\n      <li>\n        <button\n          style={{\n            fontWeight:\n              loc.pathname === '/foo' && loc.searchParams?.get('bar') === '1'\n                ? 'bold'\n                : 'normal',\n          }}\n          onClick={() =>\n            setLoc((prev) => ({\n              ...prev,\n              pathname: '/foo',\n              searchParams: new URLSearchParams([['bar', '1']]),\n            }))\n          }\n        >\n          Foo?bar=1\n        </button>\n      </li>\n    </ul>\n  )\n}\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-9v72fq\" file=\"src%2FApp.tsx\" />\n\n## atomWithHash\n\n```js\natomWithHash(key, initialValue, options): PrimitiveAtom\n```\n\nThis creates a new atom that is connected with URL hash.\nThe hash must be in the URLSearchParams format.\nIt's a two-way binding: changing the atom value will change the hash and\nchanging the hash will change the atom value.\nThis function works only with DOM.\n\n### Parameters\n\n**key** (required): a unique string used as the key when syncing state with localStorage, sessionStorage, or AsyncStorage\n\n**initialValue** (required): the initial value of the atom\n\n**options** (optional): an object of options to customize the behavior of the atom\n\n### Options\n\n**serialize** (optional): a custom function to serialize the atom value to the hash. Defaults to `JSON.stringify`.\n\n**deserialize** (optional): a custom function to deserialize the hash to the atom value. Defaults to `JSON.parse`.\n\n**subscribe** (optional): custom hash change subscribe function\n\n**setHash** (optional): `replaceState` or a custom function that describes how hash gets updated on the side of the browser. Defaults to pushing a new entry to the history via `window.location.hash = searchParams` ([jotai-location#4](https://github.com/jotaijs/jotai-location/pull/4))\n\n### Examples\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithHash } from 'jotai-location'\n\nconst countAtom = atomWithHash('count', 1)\n\nconst Counter = () => {\n  const [count, setCount] = useAtom(countAtom)\n  return (\n    <div>\n      <div>count: {count}</div>\n      <button onClick={() => setCount((c) => c + 1)}>+1</button>\n    </div>\n  )\n}\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-zngpjt\" file=\"src%2FApp.tsx\" />\n\n### Deleting Item\n\nPlease refer [atomWithStorage](../utilities/storage.mdx)\nfor the usage about deleting items.\n"
  },
  {
    "path": "docs/extensions/optics.mdx",
    "content": "---\ntitle: Optics\ndescription: This doc describes Optics-ts extension.\nnav: 4.09\nkeywords: optics\n---\n\n### Install\n\nYou have to install `optics-ts` and `jotai-optics` to use this feature.\n\n```\nnpm install optics-ts jotai-optics\n```\n\n## focusAtom\n\n`focusAtom` creates a new atom, based on the focus that you pass to it. This creates a derived atom that will focus on the specified part of the atom,\nand when the derived atom is updated, the derivee is notified of the update, and the equivalent update is done on the derivee.\n\nSee this:\n\n```js\nconst baseAtom = atom({ a: 5 }) // PrimitiveAtom<{a: number}>\nconst derivedAtom = focusAtom(baseAtom, (optic) => optic.prop('a')) // PrimitiveAtom<number>\n```\n\nSo basically, we started with a `PrimitiveAtom<{a: number}>`, which has a getter and a setter, and then used `focusAtom` to zoom in on the `a`-property of\nthe `baseAtom`, and got a `PrimitiveAtom<number>`. What is noteworthy here is that this `derivedAtom` is not only a getter, it is also a setter. If `derivedAtom` is updated, then equivalent update is done on the `baseAtom`.\n\nThe example below is simple, but it's a starting point. `focusAtom` supports many kinds of optics, including `Lens`, `Prism`, `Isomorphism`.\n\nTo see more advanced optics, please see the example at: https://github.com/akheron/optics-ts\n\n### Example\n\n```jsx\nimport { atom } from 'jotai'\nimport { focusAtom } from 'jotai-optics'\n\nconst objectAtom = atom({ a: 5, b: 10 })\nconst aAtom = focusAtom(objectAtom, (optic) => optic.prop('a'))\nconst bAtom = focusAtom(objectAtom, (optic) => optic.prop('b'))\n\nconst Controls = () => {\n  const [a, setA] = useAtom(aAtom)\n  const [b, setB] = useAtom(bAtom)\n  return (\n    <div>\n      <span>Value of a: {a}</span>\n      <span>Value of b: {b}</span>\n      <button onClick={() => setA((oldA) => oldA + 1)}>Increment a</button>\n      <button onClick={() => setB((oldB) => oldB + 1)}>Increment b</button>\n    </div>\n  )\n}\n```\n\n#### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-jjunki\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/extensions/query.mdx",
    "content": "---\ntitle: Query\ndescription: This doc describes TanStack Query extension.\nnav: 4.02\nkeywords: tanstack,query\n---\n\n[TanStack Query](https://tanstack.com/query/) provides a set of functions for managing async state (typically external data).\n\nFrom the [Overview docs](https://tanstack.com/query/v5/docs/framework/react/overview):\n\n> React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your React applications a breeze.\n\n[jotai-tanstack-query](https://github.com/jotai-labs/jotai-tanstack-query) is a Jotai extension library for TanStack Query. It provides a wonderful interface with all of the TanStack Query features, providing you the ability to use those features in combination with your existing Jotai state.\n\n### Support\n\njotai-tanstack-query currently supports TanStack Query v5.\n\n### Install\n\nIn addition to `jotai`, you have to install `jotai-tanstack-query` and `@tanstack/query-core` to use the extension.\n\n```bash\nnpm install jotai-tanstack-query @tanstack/query-core\n```\n\n### Incremental Adoption\n\nYou can incrementally adopt `jotai-tanstack-query` in your app. It's not an all or nothing solution. You just have to ensure you are using the same QueryClient instance. [QueryClient Setup](#referencing-the-same-instance-of-query-client-in-your-project).\n\n```jsx\n// existing useQueryHook\nconst { data, isPending, isError } = useQuery({\n  queryKey: ['todos'],\n  queryFn: fetchTodoList,\n})\n\n// jotai-tanstack-query\nconst todosAtom = atomWithQuery(() => ({\n  queryKey: ['todos'],\n}))\n\nconst [{ data, isPending, isError }] = useAtom(todosAtom)\n```\n\n### Exported functions\n\n- `atomWithQuery` for [useQuery](https://tanstack.com/query/v5/docs/react/reference/useQuery)\n- `atomWithInfiniteQuery` for [useInfiniteQuery](https://tanstack.com/query/v5/docs/react/reference/useInfiniteQuery)\n- `atomWithMutation` for [useMutation](https://tanstack.com/query/v5/docs/react/reference/useMutation)\n- `atomWithSuspenseQuery` for [useSuspenseQuery](https://tanstack.com/query/v5/docs/react/reference/useSuspenseQuery)\n- `atomWithSuspenseInfiniteQuery` for [useSuspenseInfiniteQuery](https://tanstack.com/query/v5/docs/react/reference/useSuspenseInfiniteQuery)\n- `atomWithMutationState` for [useMutationState](https://tanstack.com/query/v5/docs/react/reference/useMutationState)\n\nAll functions follow the same signature.\n\n```ts\nconst dataAtom = atomWithSomething(getOptions, getQueryClient)\n```\n\nThe first `getOptions` parameter is a function that returns an input to the observer.\nThe second optional `getQueryClient` parameter is a function that return [QueryClient](https://tanstack.com/query/v5/docs/reference/QueryClient).\n\n### atomWithQuery usage\n\n`atomWithQuery` creates a new atom that implements a standard [`Query`](https://tanstack.com/query/v5/docs/react/guides/queries) from TanStack Query.\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { atomWithQuery } from 'jotai-tanstack-query'\n\nconst idAtom = atom(1)\nconst userAtom = atomWithQuery((get) => ({\n  queryKey: ['users', get(idAtom)],\n  queryFn: async ({ queryKey: [, id] }) => {\n    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)\n    return res.json()\n  },\n}))\n\nconst UserData = () => {\n  const [{ data, isPending, isError }] = useAtom(userAtom)\n\n  if (isPending) return <div>Loading...</div>\n  if (isError) return <div>Error</div>\n\n  return <div>{JSON.stringify(data)}</div>\n}\n```\n\n### atomWithInfiniteQuery usage\n\n`atomWithInfiniteQuery` is very similar to `atomWithQuery`, however it is for an `InfiniteQuery`, which is used for data that is meant to be paginated. You can [read more about Infinite Queries here](https://tanstack.com/query/v5/docs/framework/react/guides/infinite-queries).\n\n> Rendering lists that can additively \"load more\" data onto an existing set of data or \"infinite scroll\" is also a very common UI pattern. React Query supports a useful version of useQuery called useInfiniteQuery for querying these types of lists.\n\nA notable difference between a standard query atom is the additional option `getNextPageParam` and `getPreviousPageParam`, which is what you'll use to instruct the query on how to fetch any additional pages.\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { atomWithInfiniteQuery } from 'jotai-tanstack-query'\n\nconst postsAtom = atomWithInfiniteQuery(() => ({\n  queryKey: ['posts'],\n  queryFn: async ({ pageParam }) => {\n    const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_page=${pageParam}`)\n    return res.json()\n  },\n  getNextPageParam: (lastPage, allPages, lastPageParam) => lastPageParam + 1,\n  initialPageParam: 1,\n}))\n\nconst Posts = () => {\n  const [{ data, fetchNextPage, isPending, isError, isFetching }] =\n    useAtom(postsAtom)\n\n  if (isPending) return <div>Loading...</div>\n  if (isError) return <div>Error</div>\n\n  return (\n    <>\n      {data.pages.map((page, index) => (\n        <div key={index}>\n          {page.map((post: any) => (\n            <div key={post.id}>{post.title}</div>\n          ))}\n        </div>\n      ))}\n      <button onClick={() => fetchNextPage()}>Next</button>\n    </>\n  )\n}\n```\n\n### atomWithMutation usage\n\n`atomWithMutation` creates a new atom that implements a standard [`Mutation`](https://tanstack.com/query/v5/docs/framework/react/guides/mutations) from TanStack Query.\n\n> Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects.\n\n```tsx\nconst postAtom = atomWithMutation(() => ({\n  mutationKey: ['posts'],\n  mutationFn: async ({ title }: { title: string }) => {\n    const res = await fetch(`https://jsonplaceholder.typicode.com/posts`, {\n      method: 'POST',\n      body: JSON.stringify({\n        title,\n        body: 'body',\n        userId: 1,\n      }),\n      headers: {\n        'Content-type': 'application/json; charset=UTF-8',\n      },\n    })\n    const data = await res.json()\n    return data\n  },\n}))\n\nconst Posts = () => {\n  const [{ mutate, status }] = useAtom(postAtom)\n  return (\n    <div>\n      <button onClick={() => mutate({ title: 'foo' })}>Click me</button>\n      <pre>{JSON.stringify(status, null, 2)}</pre>\n    </div>\n  )\n}\n```\n\n### atomWithMutationState usage\n\n`atomWithMutationState` creates a new atom that gives you access to all mutations in the [`MutationCache`](https://tanstack.com/query/v5/docs/react/reference/useMutationState).\n\n```jsx\nconst mutationStateAtom = atomWithMutationState((get) => ({\n  filters: {\n    mutationKey: ['posts'],\n  },\n}))\n```\n\n### Suspense\n\njotai-tanstack-query can also be used with React's Suspense.\n\n### atomWithSuspenseQuery usage\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { atomWithSuspenseQuery } from 'jotai-tanstack-query'\n\nconst idAtom = atom(1)\nconst userAtom = atomWithSuspenseQuery((get) => ({\n  queryKey: ['users', get(idAtom)],\n  queryFn: async ({ queryKey: [, id] }) => {\n    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)\n    return res.json()\n  },\n}))\n\nconst UserData = () => {\n  const [{ data }] = useAtom(userAtom)\n\n  return <div>{JSON.stringify(data)}</div>\n}\n```\n\n### atomWithSuspenseInfiniteQuery usage\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { atomWithSuspenseInfiniteQuery } from 'jotai-tanstack-query'\n\nconst postsAtom = atomWithSuspenseInfiniteQuery(() => ({\n  queryKey: ['posts'],\n  queryFn: async ({ pageParam }) => {\n    const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_page=${pageParam}`)\n    return res.json()\n  },\n  getNextPageParam: (lastPage, allPages, lastPageParam) => lastPageParam + 1,\n  initialPageParam: 1,\n}))\n\nconst Posts = () => {\n  const [{ data, fetchNextPage, isPending, isError, isFetching }] =\n    useAtom(postsAtom)\n\n  return (\n    <>\n      {data.pages.map((page, index) => (\n        <div key={index}>\n          {page.map((post: any) => (\n            <div key={post.id}>{post.title}</div>\n          ))}\n        </div>\n      ))}\n      <button onClick={() => fetchNextPage()}>Next</button>\n    </>\n  )\n}\n```\n\n### Referencing the same instance of Query Client in your project\n\nPerhaps you have some custom hooks in your project that utilises the `useQueryClient()` hook to obtain the `QueryClient` object and call its methods.\n\nTo ensure that you reference the same `QueryClient` object, be sure to wrap the root of your project in a `<Provider>` and initialise `queryClientAtom` with the same `queryClient` value you provided to `QueryClientProvider`.\n\nWithout this step, `useQueryAtom` will reference a separate `QueryClient` from any hooks that utilise the `useQueryClient()` hook to get the queryClient.\n\nAlternatively, you can specify your `queryClient` with `getQueryClient` parameter.\n\n#### Example\n\nIn the example below, we have a mutation hook, `useTodoMutation` and a query `todosAtom`.\n\nWe included an initialisation step in our root `<App>` node.\n\nAlthough they reference methods same query key (`'todos'`), the `onSuccess` invalidation in `useTodoMutation` will not trigger **if the `Provider` initialisation step was not done.**\n\nThis will result in `todosAtom` showing stale data as it was not prompted to refetch.\n\n⚠️ Note: When using **Typescript**, it is recommended to use a Map when passing the queryClient value to useHydrateAtoms. You can find a working example in the [Initializing State on Render docs](https://jotai.org/docs/guides/initialize-atom-on-render#using-typescript)\n\n```jsx\nimport { Provider } from 'jotai/react'\nimport { useHydrateAtoms } from 'jotai/react/utils'\nimport {\n  useMutation,\n  useQueryClient,\n  QueryClient,\n  QueryClientProvider,\n} from '@tanstack/react-query'\nimport { atomWithQuery, queryClientAtom } from 'jotai-tanstack-query'\n\nconst queryClient = new QueryClient()\n\nconst HydrateAtoms = ({ children }) => {\n  useHydrateAtoms([[queryClientAtom, queryClient]])\n  return children\n}\n\nexport const App = () => {\n  return (\n    <QueryClientProvider client={queryClient}>\n      <Provider>\n        {/*\n   This Provider initialisation step is needed so that we reference the same\n   queryClient in both atomWithQuery and other parts of the app. Without this,\n   our useQueryClient() hook will return a different QueryClient object\n\t*/}\n        <HydrateAtoms>\n          <App />\n        </HydrateAtoms>\n      </Provider>\n    </QueryClientProvider>\n  )\n}\n\nexport const todosAtom = atomWithQuery((get) => {\n  return {\n    queryKey: ['todos'],\n    queryFn: () => fetch('/todos'),\n  }\n})\n\nexport const useTodoMutation = () => {\n  const queryClient = useQueryClient()\n\n  return useMutation(\n    async (body: todo) => {\n      await fetch('/todo', { Method: 'POST', Body: body })\n    },\n    {\n      onSuccess: () => {\n        void queryClient.invalidateQueries(['todos'])\n      },\n      onError,\n    }\n  )\n}\n```\n\n### SSR support\n\nAll atoms can be used within the context of a server side rendered app, such as a next.js app or Gatsby app. You can [use both options](https://tanstack.com/query/v5/docs/framework/react/guides/ssr) that React Query supports for use within SSR apps, [hydration](https://tanstack.com/query/v5/docs/react/guides/ssr#using-the-hydration-apis) or [`initialData`](https://tanstack.com/query/v5/docs/react/guides/ssr#get-started-fast-with-initialdata).\n\n### Error handling\n\nFetch error will be thrown and can be caught with ErrorBoundary.\nRefetching may recover from a temporary error.\n\nSee [a working example](https://codesandbox.io/s/4gfp6z) to learn more.\n\n### Devtools\n\nIn order to use the Devtools, you need to install it additionally.\n\n```bash\nnpm install @tanstack/react-query-devtools\n```\n\nAll you have to do is put the `<ReactQueryDevtools />` within `<QueryClientProvider />`.\n\n```tsx\nimport {\n  QueryClientProvider,\n  QueryClient,\n  QueryCache,\n} from '@tanstack/react-query'\nimport { ReactQueryDevtools } from '@tanstack/react-query-devtools'\nimport { queryClientAtom } from 'jotai-tanstack-query'\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: Infinity,\n    },\n  },\n})\n\nconst HydrateAtoms = ({ children }) => {\n  useHydrateAtoms([[queryClientAtom, queryClient]])\n  return children\n}\n\nexport const App = () => {\n  return (\n    <QueryClientProvider client={queryClient}>\n      <Provider>\n        <HydrateAtoms>\n          <App />\n        </HydrateAtoms>\n      </Provider>\n      <ReactQueryDevtools />\n    </QueryClientProvider>\n  )\n}\n```\n\n## Migrate to v0.8.0\n\n### Change in atom signature\n\nAll atom signatures have changed to be more consistent with TanStack Query.\nv0.8.0 returns only a single atom, instead of a tuple of atoms, and hence the name change from `atomsWithSomething` to`atomWithSomething`.\n\n```diff\n\n- const [dataAtom, statusAtom] = atomsWithSomething(getOptions, getQueryClient)\n+ const dataAtom = atomWithSomething(getOptions, getQueryClient)\n\n```\n\n### Simplified Return Structure\n\nIn the previous version of `jotai-tanstack-query`, the query atoms `atomsWithQuery` and `atomsWithInfiniteQuery` returned a tuple of atoms: `[dataAtom, statusAtom]`. This design separated the data and its status into two different atoms.\n\n#### atomWithQuery and atomWithInfiniteQuery\n\n- `dataAtom` was used to access the actual data (`TData`).\n- `statusAtom` provided the status object (`QueryObserverResult<TData, TError>`), which included additional attributes like `isPending`, `isError`, etc.\n\nIn v0.8.0, they have been replaced by `atomWithQuery` and `atomWithInfiniteQuery` to return only a single `dataAtom`. This `dataAtom` now directly provides the `QueryObserverResult<TData, TError>`, aligning it closely with the behavior of Tanstack Query's bindings.\n\nTo migrate to the new version, replace the separate `dataAtom` and `statusAtom` usage with the unified `dataAtom` that now contains both data and status information.\n\n```diff\n- const [dataAtom, statusAtom] = atomsWithQuery(/* ... */);\n- const [data] = useAtom(dataAtom);\n- const [status] = useAtom(statusAtom);\n\n+ const dataAtom = atomWithQuery(/* ... */);\n+ const [{ data, isPending, isError }] = useAtom(dataAtom);\n```\n\n#### atomWithMutation\n\nSimilar to `atomsWithQuery` and `atomsWithInfiniteQuery`, `atomWithMutation` also returns a single atom instead of a tuple of atoms. The return type of the atom value is `MutationObserverResult<TData, TError, TVariables, TContext>`.\n\n```diff\n\n- const [, postAtom] = atomsWithMutation(/* ... */);\n- const [post, mutate] = useAtom(postAtom); // Accessing mutation status from post; and mutate() to execute the mutation\n\n+ const postAtom = atomWithMutation(/* ... */);\n+ const [{ data, error, mutate }] = useAtom(postAtom); // Accessing mutation result and mutate method from the same atom\n\n```\n\n### Examples\n\n#### Basic demo\n\n<Stackblitz id=\"vitejs-vite-pr9eaf\" file=\"src%2FApp.tsx\" />\n\n#### Devtools demo\n\n<Stackblitz id=\"vitejs-vite-btdqkh\" file=\"src%2FApp.tsx\" />\n\n#### Hackernews\n\n<Stackblitz id=\"vitejs-vite-wqsyj8\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/extensions/redux.mdx",
    "content": "---\ntitle: Redux\ndescription: This doc describes Redux extension.\nnav: 4.98\nkeywords: redux\npublished: false\n---\n\nJotai's state resides in React, but sometimes it would be nice\nto interact with the world outside React.\n\nRedux provides a store interface that can be used to store some values\nand sync with atoms in Jotai.\n\n### Install\n\nYou have to install `redux` and `jotai-redux` to use this feature.\n\n```\nnpm install redux jotai-redux\n```\n\n## atomWithStore\n\n`atomWithStore` creates a new atom with redux store.\nIt's two-way binding and you can change the value from both ends.\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithStore } from 'jotai-redux'\nimport { createStore } from 'redux'\n\nconst initialState = { count: 0 }\nconst reducer = (state = initialState, action: { type: 'INC' }) => {\n  if (action.type === 'INC') {\n    return { ...state, count: state.count + 1 }\n  }\n  return state\n}\nconst store = createStore(reducer)\nconst storeAtom = atomWithStore(store)\n\nconst Counter = () => {\n  const [state, dispatch] = useAtom(storeAtom)\n\n  return (\n    <>\n      count: {state.count}\n      <button onClick={() => dispatch({ type: 'INC' })}>button</button>\n    </>\n  )\n}\n```\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-qqsnwc\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/extensions/relay.mdx",
    "content": "---\ntitle: Relay\ndescription: This doc describes Relay extension.\nnav: 4.98\nkeywords: relay\npublished: false\n---\n\nYou can use Jotai with [Relay](https://relay.dev).\n\n### Install\n\nYou have to install `jotai-relay` and `relay-runtime`.\n\n```\nnpm install jotai-relay relay-runtime\n```\n\n### Usage\n\nSee [Relay Docs](https://relay.dev/docs/) to\nlearn about basics and how to use compiler in advance.\n\n### atomWithQuery\n\n`atomWithQuery` creates a new atom with [fetchQuery](https://relay.dev/docs/api-reference/fetch-query/).\n\n```jsx\nimport React, { Suspense } from 'react'\nimport { Provider, useAtom } from 'jotai'\nimport { useHydrateAtoms } from 'jotai/utils'\nimport { environmentAtom, atomWithQuery } from 'jotai-relay'\nimport { Environment, Network, RecordSource, Store } from 'relay-runtime'\nimport graphql from 'babel-plugin-relay/macro'\n\nconst myEnvironment = new Environment({\n  network: Network.create(async (params, variables) => {\n    const response = await fetch('https://countries.trevorblades.com/', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n      body: JSON.stringify({\n        query: params.text,\n        variables,\n      }),\n    })\n    return response.json()\n  }),\n  store: new Store(new RecordSource()),\n})\n\nconst countriesAtom = atomWithQuery(\n  graphql`\n    query AppCountriesQuery {\n      countries {\n        name\n      }\n    }\n  `,\n  () => ({}),\n)\n\nconst Main = () => {\n  const [data] = useAtom(countriesAtom)\n  return (\n    <ul>\n      {data.countries.map(({ name }) => (\n        <li key={name}>{name}</li>\n      ))}\n    </ul>\n  )\n}\n\nconst HydrateAtoms = ({ children }) => {\n  useHydrateAtoms([[environmentAtom, myEnvironment]])\n  return children\n}\n\nconst App = () => {\n  return (\n    <Provider>\n      <HydrateAtoms>\n        <Suspense fallback=\"Loading...\">\n          <Main />\n        </Suspense>\n      </HydrateAtoms>\n    </Provider>\n  )\n}\n```\n\n#### Examples\n\n<Stackblitz id=\"vitejs-vite-divyhe\" file=\"src%2FApp.tsx\" />\n\n### atomWithMutation\n\n`atomWithMutation` creates a new atom with [commitMutation](https://relay.dev/docs/api-reference/commit-mutation/).\n\nFIXME: add code example and codesandbox\n\n### atomWithSubscription\n\n`atomWithSubscription` creates a new atom with [requestSubscription](https://relay.dev/docs/api-reference/request-subscription/).\n\nFIXME: add code example and codesandbox\n"
  },
  {
    "path": "docs/extensions/scope.mdx",
    "content": "---\ntitle: Scope\ndescription: This doc describes scope extension.\nnav: 4.08\nkeywords: scope\n---\n\nThere are a few libraries to extend Jotai's usage in React.\n\n## `jotai-scope`\n\nWhile Jotai's Provider allows to scope Jotai's store under a subtree,\nwe can't use the store above the tree within the subtree.\n\nA workaround is to use `store` option in useAtom and other hooks.\n\nInstead of specifying the `store` option,\n`<ScopeProvider>` lets you reuse the _same_ atoms in different parts\nof the React tree **without sharing state** while still being able to\nread other atoms from the parent store.\n\n### Install\n\n```\nnpm install jotai-scope\n```\n\n### Counter Example\n\n```tsx\nimport { atom, useAtom } from 'jotai'\nimport { ScopeProvider } from 'jotai-scope'\n\nconst countAtom = atom(0)\n\nfunction Counter() {\n  const [count, setCount] = useAtom(countAtom)\n  const [anotherCount, setAnotherCount] = useAtom(anotherCountAtom)\n  return (\n    <>\n      <div>\n        <span>count: {count}</span>\n        <button type=\"button\" onClick={() => setCount((v) => v + 1)}>\n          increment\n        </button>\n      </div>\n      <div>\n        <span>another count: {anotherCount}</span>\n        <button type=\"button\" onClick={() => setAnotherCount((v) => v + 1)}>\n          increment\n        </button>\n      </div>\n    </>\n  )\n}\n\nfunction App() {\n  return (\n    <div>\n      <Counter />\n      <ScopeProvider atoms={[countAtom]}>\n        <Counter />\n      </ScopeProvider>\n    </div>\n  )\n}\n```\n\n<Stackblitz id=\"vitejs-vite-ctcuhj\" file=\"src%2FApp.tsx\" />\n\n## `createIsolation`\n\nBoth Jotai's Provider and `jotai-scope`'s scoped provider\nare still using global contexts.\n\nIf you are developing a library that depends on Jotai and\nthe library user may use Jotai separately in their apps,\nthey can share the same context. This can be troublesome\nbecause they point to unexpected Jotai stores.\n\nTo avoid conflicting the contexts,\na utility function called `createIsolation` is exported from `jotai-scope`.\n\n```tsx\nimport { createIsolation } from 'jotai-scope'\n\nconst { Provider, useStore, useAtom, useAtomValue, useSetAtom } =\n  createIsolation()\n\nfunction Library() {\n  return (\n    <Provider>\n      <LibraryComponent />\n    </Provider>\n  )\n}\n```\n\n## `bunshi` (formerly `jotai-molecules`)\n\nJotai atoms provide a basic solution to optimize re-renders.\nAtoms defined globally can depend on other atoms,\nbut they can't depend on props and state within a component tree.\nIt's possible to define atoms within a component tree,\nbut then you would need to pass those atoms in some ways\n(for example, [atoms-in-atom](../guides/atoms-in-atom.mdx).)\n\n[bunshi](https://github.com/saasquatch/bunshi) is\na third-party library to help such use cases.\n\nSee [Motivation](https://github.com/saasquatch/bunshi/tree/v1.1.1#motivation) for more details.\n\n### Install\n\n```\nnpm install bunshi\n```\n\n### Counter Example\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { molecule, useMolecule, createScope, ScopeProvider } from 'bunshi/react'\n\nconst InitialCountScope = createScope({ initialCount: 0 })\nconst countMolecule = molecule((getMol, getScope) => {\n  const { initialCount } = getScope(InitialCountScope)\n  return atom(initialCount)\n})\n\nfunction Counter() {\n  const countAtom = useMolecule(countMolecule)\n  const [count, setCount] = useAtom(countAtom)\n  return (\n    <div>\n      {count} <button onClick={() => setCount((v) => v + 1)}>+1</button>\n    </div>\n  )\n}\n\nfunction App() {\n  return (\n    <div>\n      <h1>With initial value 1</h1>\n      <ScopeProvider scope={InitialCountScope} value={{ initialCount: 1 }}>\n        <Counter />\n        <Counter />\n      </ScopeProvider>\n      <h1>With initial value 2</h1>\n      <ScopeProvider scope={InitialCountScope} value={{ initialCount: 2 }}>\n        <Counter />\n        <Counter />\n      </ScopeProvider>\n      <h1>Default</h1>\n      <Counter />\n      <Counter />\n    </div>\n  )\n}\n```\n\n<Stackblitz id=\"vitejs-vite-8akpt6\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/extensions/trpc.mdx",
    "content": "---\ntitle: tRPC\ndescription: This doc describes tRPC extension.\nnav: 4.01\nkeywords: rpc,trpc,typescript,t3\n---\n\nYou can use Jotai with [tRPC](https://trpc.io).\n\n### Install\n\nYou have to install `jotai-trpc`, `@trpc/client` and `@trpc/server` to use the extension.\n\n```\nnpm install jotai-trpc @trpc/client @trpc/server\n```\n\n### Usage\n\n```ts\nimport { createTRPCJotai } from 'jotai-trpc'\n\nconst trpc = createTRPCJotai<MyRouter>({\n  links: [\n    httpLink({\n      url: myUrl,\n    }),\n  ],\n})\n\nconst idAtom = atom('foo')\nconst queryAtom = trpc.bar.baz.atomWithQuery((get) => get(idAtom))\n```\n\n### atomWithQuery\n\n`...atomWithQuery` creates a new atom with \"query\". It internally uses [Vanilla Client](https://trpc.io/docs/vanilla)'s `...query` procedure.\n\n```tsx\nimport { atom, useAtom } from 'jotai'\nimport { httpLink } from '@trpc/client'\nimport { createTRPCJotai } from 'jotai-trpc'\nimport { trpcPokemonUrl } from 'trpc-pokemon'\nimport type { PokemonRouter } from 'trpc-pokemon'\n\nconst trpc = createTRPCJotai<PokemonRouter>({\n  links: [\n    httpLink({\n      url: trpcPokemonUrl,\n    }),\n  ],\n})\n\nconst NAMES = [\n  'bulbasaur',\n  'ivysaur',\n  'venusaur',\n  'charmander',\n  'charmeleon',\n  'charizard',\n  'squirtle',\n  'wartortle',\n  'blastoise',\n]\n\nconst nameAtom = atom(NAMES[0])\n\nconst pokemonAtom = trpc.pokemon.byId.atomWithQuery((get) => get(nameAtom))\n\nconst Pokemon = () => {\n  const [data, refresh] = useAtom(pokemonAtom)\n  return (\n    <div>\n      <div>ID: {data.id}</div>\n      <div>Height: {data.height}</div>\n      <div>Weight: {data.weight}</div>\n      <button onClick={refresh}>Refresh</button>\n    </div>\n  )\n}\n```\n\n#### Examples\n\n<Stackblitz id=\"vitejs-vite-8kgfpj\" file=\"src%2FApp.tsx\" />\n\n### atomWithMutation\n\n`...atomWithMutation` creates a new atom with \"mutate\". It internally uses [Vanilla Client](https://trpc.io/docs/vanilla)'s `...mutate` procedure.\n\nFIXME: add code example and codesandbox\n\n### atomWithSubscription\n\n`...atomWithSubscription` creates a new atom with \"subscribe\". It internally uses [Vanilla Client](https://trpc.io/docs/vanilla)'s `...subscribe` procedure.\n\nFIXME: add code example and codesandbox\n"
  },
  {
    "path": "docs/extensions/urql.mdx",
    "content": "---\ntitle: URQL\ndescription: This doc describes URQL extension.\nnav: 4.03\nkeywords: urql\n---\n\n[urql](https://formidable.com/open-source/urql/) offers a toolkit for GraphQL querying, caching, and state management.\n\nFrom the [Overview docs](https://formidable.com/open-source/urql/docs/):\n\n> urql is a highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow. It's built to be both easy to use for newcomers to GraphQL, and extensible, to grow to support dynamic single-app applications and highly customized GraphQL infrastructure. In short, urql prioritizes usability and adaptability.\n\n[jotai-urql](https://github.com/jotaijs/jotai-urql) is a Jotai extension library for URQL. It offers a cohesive interface that incorporates all of URQL's GraphQL features, allowing you to leverage these functionalities alongside your existing Jotai state.\n\n### Install\n\nYou have to install `jotai-urql`, `@urql/core` and `wonka` to use the extension.\n\n```\nnpm install jotai-urql @urql/core wonka\n```\n\n### Exported functions\n\n- `atomWithQuery` for [client.query](https://formidable.com/open-source/urql/docs/api/core/#clientquery)\n- `atomWithMutation` for [client.mutation](https://formidable.com/open-source/urql/docs/api/core/#clientmutation)\n- `atomWithSubscription` for [client.subscription](https://formidable.com/open-source/urql/docs/api/core/#clientsubscription)\n\n### Basic usage\n\n#### Query:\n\n```tsx\nimport { useAtom } from 'jotai'\n\nconst countQueryAtom = atomWithQuery<{ count: number }>({\n  query: 'query Count { count }',\n  getClient: () => client, // This option is optional if `useRehydrateAtom([[clientAtom, client]])` is used globally\n})\n\nconst Counter = () => {\n  // Will suspend until first operation result is resolved. Either with error, partial data, data\n  const [operationResult, reexecute] = useAtom(countQueryAtom)\n\n  if (operationResult.error) {\n    // This shall be handled in the parent ErrorBoundary above\n    throw operationResult.error\n  }\n\n  // You have to use optional chaining here, as data may be undefined at this point (only in case of error)\n  return <>{operationResult.data?.count}</>\n}\n```\n\n#### Mutation:\n\n```tsx\nimport { useAtom } from 'jotai'\n\nconst incrementMutationAtom = atomWithMutation<{ increment: number }>({\n  query: 'mutation Increment { increment }',\n})\n\nconst Counter = () => {\n  const [operationResult, executeMutation] = useAtom(incrementMutationAtom)\n  return (\n    <div>\n      <button\n        onClick={() =>\n          executeMutation().then((it) => console.log(it.data?.increment))\n        }\n      >\n        Increment\n      </button>\n      <div>{operationResult.data?.increment}</div>\n    </div>\n  )\n}\n```\n\n### Simplified type of options passed to functions\n\n```tsx\ntype AtomWithQueryOptions<\n  Data = unknown,\n  Variables extends AnyVariables = AnyVariables,\n> = {\n  // Supports string query, typed-document-node, document node etc.\n  query: DocumentInput<Data, Variables>\n  // Will be enforced dynamically based on generic/typed-document-node types.\n  getVariables?: (get: Getter) => Variables\n  getContext?: (get: Getter) => Partial<OperationContext>\n  getPause?: (get: Getter) => boolean\n  getClient?: (get: Getter) => Client\n}\n\ntype AtomWithMutationOptions<\n  Data = unknown,\n  Variables extends AnyVariables = AnyVariables,\n> = {\n  query: DocumentInput<Data, Variables>\n  getClient?: (get: Getter) => Client\n}\n\n// Subscription type is the same as AtomWithQueryOptions\n```\n\n### Disable suspense\n\nUsage of `import { loadable } from \"jotai/utils\"` is preferred instead as proven more stable. However is you still want that\nhere is how you do it:\n\n```tsx\nimport { suspenseAtom } from 'jotai-urql'\n\nexport const App = () => {\n  // We disable suspense for the entire app\n  useHydrateAtoms([[suspenseAtom, false]])\n  return <Counter />\n}\n```\n\n### Useful helper hook\n\nHere is the helper hook, to cover one rare corner case, and make use of these bindings similar to `@tanstack/react-query`\ndefault behavior where errors are treated as errors (in case of Promise reject) and are handled mostly in the nearby\nErrorBoundaries. Only valid for suspended version.\n\n#### useQueryAtomData\n\nNeatly returns `data` after the resolution + handles all the error throwing/reexecute cases/corner cases.\nNote that Type is overridden so `data` it never `undefined` nor `null` (unless that's expected return type of the query itself)\n\n```tsx\nimport type { AnyVariables, OperationResult } from '@urql/core'\nimport { useAtom } from 'jotai'\nimport type { AtomWithQuery } from 'jotai-urql'\n\nexport const useQueryAtomData = <\n  Data = unknown,\n  Variables extends AnyVariables = AnyVariables,\n>(\n  queryAtom: AtomWithQuery<Data, Variables>,\n) => {\n  const [opResult, dispatch] = useAtom(queryAtom)\n\n  if (opResult.error && opResult.stale) {\n    use(\n      // Here we suspend the tree. This will only be triggered in the scenario\n      // when you use `network-only` for refetch in Error Boundary retry logic, in that case tree doesn't suspend\n      // causing possible \"throwed - retry in boundary - throwed - retry in boundary\" cycle.\n      // (in case of Jotai URQL bindings only).\n      // eslint-disable-next-line promise/avoid-new\n      new Promise((resolve) => {\n        setTimeout(resolve, 10000) // This timeout time is going to cause suspense of this component up until\n        // new operation result will come. After 10 second it will simply try to render component itself and suspend again\n        // in an endless loop\n      }),\n    )\n  }\n\n  if (opResult.error) {\n    throw opResult.error\n  }\n\n  if (!opResult.data) {\n    throw Error(\n      'Query data is undefined. Probably you paused the query? In that case use `useQueryAtom` instead.',\n    )\n  }\n  return [opResult.data, dispatch, opResult] as [\n    Exclude<typeof opResult.data, undefined>,\n    typeof dispatch,\n    typeof opResult,\n  ]\n}\n\n// Suspense tree while promise is resolving (not going to be needed in next versions of React)\nfunction use(promise: Promise<any> | any) {\n  if (promise.status === 'fulfilled') {\n    return promise.value\n  }\n  if (promise.status === 'rejected') {\n    throw promise.reason\n  } else if (promise.status === 'pending') {\n    throw promise\n  } else {\n    promise.status = 'pending'\n    // eslint-disable-next-line promise/catch-or-return\n    ;(promise as Promise<any>).then(\n      (result: any) => {\n        promise.status = 'fulfilled'\n        promise.value = result\n      },\n      (reason: any) => {\n        promise.status = 'rejected'\n        promise.reason = reason\n      },\n    )\n    throw promise\n  }\n}\n```\n\n#### Basic demo\n\n<Stackblitz id=\"vitejs-vite-1nhtrj\" file=\"src%2FApp.tsx\" />\n\n### Referencing the same instance of the client for both atoms and urql provider\n\nTo ensure that you reference the same urqlClient object, be sure to wrap the root of your project in a `<Provider>` and initialise clientAtom with the same urqlClient value you provided to UrqlProvider.\n\nWithout this step, you may end up specifying client each time when you use `atomWithQuery`. Now you can just ignore the optional `getClient` parameter, and it will use the client from the context.\n\n```jsx\nimport { Suspense } from 'react'\nimport { Provider } from 'jotai/react'\nimport { useHydrateAtoms } from 'jotai/react/utils'\nimport { clientAtom } from 'jotai-urql'\n\nimport {\n  createClient,\n  cacheExchange,\n  fetchExchange,\n  Provider as UrqlProvider,\n} from 'urql'\n\nconst urqlClient = createClient({\n  url: 'https://countries.trevorblades.com/',\n  exchanges: [cacheExchange, fetchExchange],\n  fetchOptions: () => {\n    return { headers: {} }\n  },\n})\n\nconst HydrateAtoms = ({ children }) => {\n  useHydrateAtoms([[clientAtom, urqlClient]])\n  return children\n}\n\nexport default function MyApp({ Component, pageProps }) {\n  return (\n    <UrqlProvider value={urqlClient}>\n      <Provider>\n        <HydrateAtoms>\n          <Suspense fallback=\"Loading...\">\n            <Component {...pageProps} />\n          </Suspense>\n        </HydrateAtoms>\n      </Provider>\n    </UrqlProvider>\n  )\n}\n```\n"
  },
  {
    "path": "docs/extensions/valtio.mdx",
    "content": "---\ntitle: Valtio\ndescription: This doc describes Valtio extension.\nnav: 4.98\nkeywords: valtio,proxy\npublished: false\n---\n\nJotai's state resides in React, but sometimes it would be nice\nto interact with the world outside React.\n\nValtio provides a proxy interface that can be used to store some values\nand sync with atoms in Jotai.\n\nThis only uses the vanilla api of valtio.\n\n### Install\n\nYou have to install `valtio` and `jotai-valtio` to use this feature.\n\n```\nnpm install valtio jotai-valtio\n```\n\n## atomWithProxy\n\n`atomWithProxy` creates a new atom with valtio proxy.\nIt's two-way binding and you can change the value from both ends.\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithProxy } from 'jotai-valtio'\nimport { proxy } from 'valtio/vanilla'\n\nconst proxyState = proxy({ count: 0 })\nconst stateAtom = atomWithProxy(proxyState)\nconst Counter = () => {\n  const [state, setState] = useAtom(stateAtom)\n\n  return (\n    <>\n      count: {state.count}\n      <button\n        onClick={() => setState((prev) => ({ ...prev, count: prev.count + 1 }))}\n      >\n        inc atom\n      </button>\n      <button\n        onClick={() => {\n          ++proxyState.count\n        }}\n      >\n        inc proxy\n      </button>\n    </>\n  )\n}\n```\n\n### Parameters\n\n```\natomWithProxy(proxyObject, options?)\n```\n\n**proxyObject** (required): the Valtio proxy object you want to derive the atom from\n\n**options.sync** (optional): makes the atom update synchronously instead of waiting for batched updates, similar to [`valtio/useSnapshot`](https://github.com/pmndrs/valtio#update-synchronously). This will result in more renders, but have more guarantees that it syncs with other Jotai atoms.\n\n```\natomWithProxy(proxyObject, { sync: true })\n```\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-wmsazx\" file=\"src%2FApp.tsx\" />\n\n## mutableAtom\n\n`mutableAtom` wraps a value in a self-aware Valtio proxy. You can make changes to it in the same way you would to a normal js-object.\n\nCount value is stored under the `value` property.\n\n```jsx\nconst countProxyAtom = mutableAtom(0)\n\nfunction IncrementButton() {\n  const countProxy = useAtomValue(countProxyAtom)\n  return <button onClick={() => ++countProxy.value}>+</button>\n}\n```\n\n### Parameters\n\n```js\nmutableAtom(value, options?)\n```\n\n**value** (required): the value to proxy.\n\n**options.proxyFn** (optional): allows customization with `proxyFn` for custom proxy functions. Can be `proxy` (default) or a custom function.\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-wmsazx\" file=\"src%2FApp.tsx\" />\n\n### Caution on Mutating Proxies\n\nBe careful to not mutate the proxy directly in the atom's read function or during render. Doing so could cause an infinite render loop.\n\n```ts\nconst countProxyAtom = mutableAtom(0)\n\natom(\n  (get) => {\n    const countProxy = get(countProxyAtom)\n    ++countProxy.value // This will cause an infinite loop\n  },\n  (get, set) => {\n    const countProxy = get(countProxyAtom)\n    ++countProxy.value // This is fine\n  },\n)\n```\n"
  },
  {
    "path": "docs/extensions/xstate.mdx",
    "content": "---\ntitle: XState\ndescription: This doc describes XState extension.\nnav: 4.05\nkeywords: xstate,machine,atomwithmachine\n---\n\nJotai's state management is primitive and flexible,\nbut that sometimes means too free.\nXState is a sophisticated library to provide\na better and safer abstraction for state management.\n\n### Install\n\nYou have to install `xstate` and `jotai-xstate` to use this feature.\n\n```\nnpm install xstate jotai-xstate\n```\n\n## atomWithMachine\n\n`atomWithMachine` creates a new atom with XState machine.\nIt receives a function `getMachine` to create a new machine.\n`getMachine` is invoked at the first use with `get` argument,\nwith which you can read other atom values.\n\n```tsx\nimport { useAtom } from 'jotai'\nimport { atomWithMachine } from 'jotai-xstate'\nimport { assign, createMachine } from 'xstate'\n\nconst createEditableMachine = (value: string) =>\n  createMachine<{ value: string }>({\n    id: 'editable',\n    initial: 'reading',\n    context: {\n      value,\n    },\n    states: {\n      reading: {\n        on: {\n          dblclick: 'editing',\n        },\n      },\n      editing: {\n        on: {\n          cancel: 'reading',\n          commit: {\n            target: 'reading',\n            actions: assign({\n              value: (_, { value }) => value,\n            }),\n          },\n        },\n      },\n    },\n  })\n\nconst defaultTextAtom = atom('edit me')\nconst editableMachineAtom = atomWithMachine((get) =>\n  // `get` is available only for initializing a machine\n  createEditableMachine(get(defaultTextAtom)),\n)\n\nconst Toggle = () => {\n  const [state, send] = useAtom(editableMachineAtom)\n\n  return (\n    <div>\n      {state.matches('reading') && (\n        <strong onDoubleClick={send}>{state.context.value}</strong>\n      )}\n      {state.matches('editing') && (\n        <input\n          autoFocus\n          defaultValue={state.context.value}\n          onBlur={(e) => send({ type: 'commit', value: e.target.value })}\n          onKeyDown={(e) => {\n            if (e.key === 'Enter') {\n              send({ type: 'commit', value: e.target.value })\n            }\n            if (e.key === 'Escape') {\n              send('cancel')\n            }\n          }}\n        />\n      )}\n      <br />\n      <br />\n      <div>\n        Double-click to edit. Blur the input or press <code>enter</code> to\n        commit. Press <code>esc</code> to cancel.\n      </div>\n    </div>\n  )\n}\n```\n\n### Restartable machine stored in a global Provider (provider-less mode)\n\nWhen your machine reaches its final state it cannot receive any more events.\nIf your atomWithMachine is initialized in global store (aka provider-less mode),\nto restart it you need to send a `RESTART` event to your machine like so:\n\n```tsx\nimport { RESTART } from 'jotai-xstate'\n\nconst YourComponent = () => {\n  const [current, send] = useAtom(yourMachineAtom)\n\n  const isFinalState = current.matches('myFinalState')\n\n  useEffect(() => {\n    // restart globally initialized machine on component unmount\n    return () => {\n      if (isFinalState) send(RESTART)\n    }\n  }, [isFinalState])\n}\n```\n\n### Examples\n\nCheck examples with atomWithMachine:\n\n<Stackblitz id=\"vitejs-vite-oqfhy4\" file=\"src%2FApp.tsx\" />\n\nRestartable machine:\n\n<Stackblitz id=\"vitejs-vite-tqdtym\" file=\"src%2FApp.tsx\" />\n\n### Tutorials\n\nCheck out a course about Jotai and XState.\n\n[Complex State Management in React with Jotai and XState](https://egghead.io/courses/complex-state-management-in-react-with-jotai-and-xstate-3be0a740)\n\n(Note: In the course, it uses `jotai/xstate` which is supersede by `jotai-xstate`.)\n"
  },
  {
    "path": "docs/extensions/zustand.mdx",
    "content": "---\ntitle: Zustand\ndescription: This doc describes Zustand extension.\nnav: 4.98\nkeywords: zustand\npublished: false\n---\n\nJotai's state resides in React, but sometimes it would be nice\nto interact with the world outside React.\n\nZustand provides a store interface that can be used to hold some values\nand sync with atoms in Jotai.\n\nThis only uses the vanilla api of zustand.\n\n### Install\n\nYou have to install `zustand` and `jotai-zustand` to use this feature.\n\n```\nnpm install zustand jotai-zustand\n```\n\n## atomWithStore\n\n`atomWithStore` creates a new atom with zustand store.\nIt's two-way binding and you can change the value from both ends.\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithStore } from 'jotai-zustand'\nimport create from 'zustand/vanilla'\n\nconst store = create(() => ({ count: 0 }))\nconst stateAtom = atomWithStore(store)\nconst Counter = () => {\n  const [state, setState] = useAtom(stateAtom)\n\n  return (\n    <>\n      count: {state.count}\n      <button\n        onClick={() => setState((prev) => ({ ...prev, count: prev.count + 1 }))}\n      >\n        button\n      </button>\n    </>\n  )\n}\n```\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-kfzyun\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/guides/async.mdx",
    "content": "---\ntitle: Async\ndescription: This doc describes about the behavior with async.\nnav: 8.99\nkeywords: async\npublished: false\n---\n\nUsing async atoms, you gain access to real-world data while still managing them directly from your atoms and with incredible ease.\n\nWe can separate them in two main categories:\n\n- Async read atoms: async request is started instantly as soon as you try to get its value. You could relate to them as \"smart getters\".\n- Async write atoms: async request is started at a specific moment. You could relate to them as \"actions\".\n\n## Async read atom\n\nThe `read` function of an atom can return a promise.\n\n```js\nconst countAtom = atom(1)\nconst asyncAtom = atom(async (get) => get(countAtom) * 2)\n```\n\nJotai is inherently leveraging `Suspense` to handle asynchronous flows.\n\n```jsx\nconst ComponentUsingAsyncAtoms = () => {\n  const [num] = useAtom(asyncAtom)\n  // here `num` is always `number` even though asyncAtom returns a Promise\n}\nconst App = () => {\n  return (\n    <Suspense fallback={/* What to show while suspended */}>\n      <ComponentUsingAsyncAtoms />\n    </Suspense>\n  )\n}\n```\n\nAlternatively, you could avoid the inherent suspending that Jotai does for you, by wrapping your atoms with the [`loadable` API](../utilities/async.mdx).\n\nIf another atom uses an async atom, it will return a promise. So, we need to make the atom also async.\n\n```js\nconst anotherAtom = atom(async (get) => (await get(asyncAtom)) / 2)\n```\n\nThis also applies to an atom with write function.\n\n```js\nconst asyncAtom = atom(async (get) => ...)\nconst writeAtom = atom(null, async (get, set, payload) => {\n  await get(asyncAtom)\n  // ...\n})\n```\n\n## Async write atom\n\nAsync write atoms are another kind of async atom. When the `write` function of atom returns a promise.\n\n```js\nconst countAtom = atom(1)\nconst asyncIncrementAtom = atom(null, async (get, set) => {\n  // await something\n  set(countAtom, get(countAtom) + 1)\n})\n\nconst Component = () => {\n  const [, increment] = useAtom(asyncIncrementAtom)\n  const handleClick = () => {\n    increment()\n  }\n  // ...\n}\n```\n\n## Async sometimes\n\nAn interesting pattern that can be achieved with Jotai is switching from async to sync to trigger suspending when wanted.\n\n```js\nconst request = async () =>\n  fetch('https://jsonplaceholder.typicode.com/todos/1').then((res) =>\n    res.json(),\n  )\nconst baseAtom = atom(0)\nconst Component = () => {\n  const [value, setValue] = useAtom(baseAtom)\n  const handleClick = () => {\n    setValue(request()) // Will suspend until request resolves\n  }\n  // ...\n}\n```\n\n### Usage in TypeScript\n\nIn TypeScript `atom(0)` is inferred as `PrimitiveAtom<number>`. It cannot accept `Promise<number>` as a value so preceding code would not typecheck. To accommodate for that you need to type your atom explicitly and add `Promise<number>` as accepted value.\n\n```ts\nconst baseAtom = atom<number | Promise<number>>(0) // Will accept sync and async values\n```\n\n## Async forever\n\nSometimes you may want to suspend until an unpredetermined moment (or never).\n\n```js\nconst baseAtom = atom(new Promise(() => {})) // Will be suspend until set otherwise\n```\n\n## Suspense\n\nAsync support is first class in Jotai. It fully leverages React Suspense at its core.\n\n> Technically, Suspense usage other than React.lazy is still unsupported / undocumented in React 17. If this is blocking, so you can still use the [`loadable` API](../utilities/async.mdx) to avoid suspending\n\nTo use async atoms, you need to wrap your component tree with `<Suspense>`.\n\n> If you have a `<Provider>`, place **at least one** `<Suspense>` inside said `<Provider>`; otherwise, it may cause an endless loop while rendering the components.\n\n```jsx\nconst App = () => (\n  <Provider>\n    <Suspense fallback=\"Loading...\">\n      <Layout />\n    </Suspense>\n  </Provider>\n)\n```\n\nHaving more `<Suspense>`s in the component tree is also possible and must be considered to profit from Jotai inherent handling at best.\n"
  },
  {
    "path": "docs/guides/atoms-in-atom.mdx",
    "content": "---\ntitle: Atoms in atom\nnav: 8.12\n---\n\n`atom()` creates an atom config, which is an object, but it doesn't hold a value.\nAtom configs don't have string keys and we identify them with referential equality.\nIn other words, we can use an atom config like a key.\n\n### Storing an atom config in useState\n\nFirst things first, we can store an atom config in useState.\n\n```jsx\nconst Component = ({ atom1, atom2 }) => {\n  const [selectedAtom, setSelectedAtom] = useState(atom1)\n  const [value] = useAtom(selectedAtom)\n  return (\n    <div>\n      Selected value: {value}\n      <button onClick={() => setSelectedAtom(atom1)}>Select an atom</button>\n      <button onClick={() => setSelectedAtom(atom2)}>\n        Select another atom\n      </button>\n    </div>\n  )\n}\n```\n\nNote that we can pass atoms configs as props.\n\nIt might not make any sense, but we could create an atom config on demand.\n\n```jsx\nconst Component = () => {\n  const [currentAtom, setCurrentAtom] = useState(() => atom(0))\n  const [count, setCount] = useAtom(currentAtom)\n  return (\n    <div>\n      Count: {count} <button onClick={() => setCount((c) => c + 1)}>+1</button>\n      <button onClick={() => setCurrentAtom(atom(0))}>Create new</button>\n    </div>\n  )\n}\n```\n\n### Storing an atom config in atom\n\nLikewise, we can store an atom config as a value of another atom.\n\n```jsx\nconst firstNameAtom = atom('Tanjiro')\nconst lastNameAtom = atom('Kamado')\n\nconst showingNameAtom = atom(firstNameAtom)\n\nconst Component = () => {\n  const [nameAtom, setNameAtom] = useAtom(showingNameAtom)\n  const [name] = useAtom(nameAtom)\n  return (\n    <div>\n      Name: {name}\n      <button onClick={() => setNameAtom(firstNameAtom)}>\n        Show First Name\n      </button>\n      <button onClick={() => setNameAtom(lastNameAtom)}>Show Last Name</button>\n    </div>\n  )\n}\n```\n\nIt's possible to create a derived atom.\n\n```js\nconst derivedNameAtom = atom((get) => {\n  const nameAtom = get(showingNameAtom)\n  return get(nameAtom)\n})\n\n// Or a shorter version\nconst derivedNameAtom = atom((get) => get(get(showingNameAtom)))\n```\n\nTo avoid confusing what is in atoms, naming atoms explicitly would be important.\nAlso, TypeScript type information would be helpful.\n\n### Storing an array of atom configs in atom\n\nFinally, the atoms in atom pattern is to store an array of atom config into an atom.\n\n```jsx\nconst countsAtom = atom([atom(1), atom(2), atom(3)])\n\nconst Counter = ({ countAtom }) => {\n  const [count, setCount] = useAtom(countAtom)\n  return (\n    <div>\n      {count} <button onClick={() => setCount((c) => c + 1)}>+1</button>\n    </div>\n  )\n}\n\nconst Parent = () => {\n  const [counts, setCounts] = useAtom(countsAtom)\n  const addNewCount = () => {\n    const newAtom = atom(0)\n    setCounts((prev) => [...prev, newAtom])\n  }\n  return (\n    <div>\n      {counts.map((countAtom) => (\n        <Counter countAtom={countAtom} key={countAtom} />\n      ))}\n      <button onClick={addNewCount}>Add</button>\n    </div>\n  )\n}\n```\n\nThe benefit of this approach is, if you increment a count,\nonly the corresponding Counter component re-renders and no other components re-render.\n\nIt is important to note that `anAtom.toString()` returns a unique id, which can be used as a `key` in a map.\n\n#### Hint for TypeScript users\n\n```jsx\n<Counter countAtom={countAtom} key={`${countAtom}`} />\n```\n\n### Storing a map of atom configs in atom\n\nLikewise, we can store an object map instead of an array.\n\n```jsx\nconst pricesAtom = atom({\n  apple: atom(15),\n  orange: atom(12),\n  pineapple: atom(25),\n})\n\nconst Fruit = ({ name, priceAtom }) => {\n  const [price] = useAtom(priceAtom)\n  return (\n    <div>\n      {name}: {price}\n    </div>\n  )\n}\n\nconst Parent = () => {\n  const [prices] = useAtom(pricesAtom)\n  return (\n    <div>\n      {Object.keys(prices).map((name) => (\n        <Fruit name={name} priceAtom={prices[name]} key={name} />\n      ))}\n    </div>\n  )\n}\n```\n"
  },
  {
    "path": "docs/guides/composing-atoms.mdx",
    "content": "---\ntitle: Composing atoms\nnav: 8.11\n---\n\nThe `atom` function provided by library is very primitive,\nbut it's also so flexible that you can combine multiple atoms\nto implement a functionality.\n\n> Note again that `atom()` creates an atom config, which is an object\n> to define a behavior of the atom.\n\nLet's recap how we can derive an atom.\n\n### Basic derived atoms\n\nHere's one of the simplest examples of a derived atom:\n\n```js\nexport const textAtom = atom('hello')\nexport const textLenAtom = atom((get) => get(textAtom).length)\n```\n\nThe `textLenAtom` is called read-only atom, because\nit doesn't have a `write` function defined.\n\nThe following is another simple example with the `write` function:\n\n```js\nconst textAtom = atom('hello')\nexport const textUpperCaseAtom = atom(\n  (get) => get(textAtom).toUpperCase(),\n  (_get, set, newText) => set(textAtom, newText),\n)\n```\n\nIn this case, `textUpperCaseAtom` is capable to set the original `textAtom`.\nSo, we can only export `textUpperCaseAtom` and can hide\n`textAtom` in a smaller scope.\n\nNow, let's see some real examples.\n\n### Overriding default atom values\n\nSuppose we have a read-only atom.\nObviously read-only atoms are not writable, but we can combine two atoms\nto override the read-only atom value.\n\n```js\nconst rawNumberAtom = atom(10.1) // can be exported\nconst roundNumberAtom = atom((get) => Math.round(get(rawNumberAtom)))\nconst overwrittenAtom = atom(null)\nexport const numberAtom = atom(\n  (get) => get(overwrittenAtom) ?? get(roundNumberAtom),\n  (get, set, newValue) => {\n    const nextValue =\n      typeof newValue === 'function' ? newValue(get(numberAtom)) : newValue\n    set(overwrittenAtom, nextValue)\n  },\n)\n```\n\nThe final `numberAtom` just works like a normal primitive atom like `atom(10)`.\nIf you set a number value, it will override the `overwrittenAtom` value,\nand if you set `null`, it will be the `roundNumberAtom` value.\n\nThe reusable implementation is available as `atomWithDefault`\nin `jotai/utils`. See [atomWithDefault](../utilities/resettable.mdx).\n\nNext, let's see another example to sync with external value.\n\n### Syncing atom values with external values\n\nThere are some external values we want to deal with.\n`localStorage` is the one. Another is `window.title`.\n\nLet's see how to create an atom that is in sync with `localStorage`.\n\n```js\nconst baseAtom = atom(localStorage.getItem('mykey') || '')\nexport const persistedAtom = atom(\n  (get) => get(baseAtom),\n  (get, set, newValue) => {\n    const nextValue =\n      typeof newValue === 'function' ? newValue(get(baseAtom)) : newValue\n    set(baseAtom, nextValue)\n    localStorage.setItem('mykey', nextValue)\n  },\n)\n```\n\nThe `persistedAtom` works like a primitive atom, but its value\nis persisted in `localStorage`.\n\nThe reusable implementation is available as `atomWithStorage`\nin `jotai/utils`. See [atomWithStorage](../utilities/storage.mdx).\n\nThere is a caveat with this usage. While atom config doesn't hold a value,\nthe external value is a singleton value.\nSo, if we use this atom in two different Providers,\nThere will be an inconsistency between the two `persistedAtom` values.\nThis could be solved if the external value had a subscription mechanism.\n\nFor example, `atomWithProxy` in `jotai-valtio` comes with subscription,\nso we don't have such a limitation. Values in different Providers\nwill be in sync.\n\nBack to the main topic, let's explore another example.\n\n### Extending atoms with `atomWith*` utils\n\nWe have several utils whose names start with `atomWith`.\nThey create an atom with a certain functionality.\nUnfortunately, we can't combine two atom utils.\nFor example, `atomWithStorage` and `atomWithReducer`\ncan't be used to define a single atom.\n\nIn such a case, we need to derive an atom by ourselves.\nLet's try adding reducer functionality to `atomWithStorage`:\n\n```js\nconst reducer = ...\nconst baseAtom = atomWithStorage('mykey', '')\nexport const derivedAtom = atom(\n  (get) => get(baseAtom),\n  (get, set, action) => {\n    set(baseAtom, reducer(get(baseAtom), action))\n  }\n)\n```\n\nThis is easy, because in this case, `atomWithReducer`\nis a simple implementation compared to `atomWithStorage`.\n\nFor more complex cases, it wouldn't be very easy.\nIt would still be a open research field.\n\nFinally, let's see another example with actions.\n\n### Action atoms\n\nThis should be known pattern as it's described in README.\nNonetheless, it might to be useful to revisit.\n\nLet's create a counter that you can only increment or decrement by one.\n\nOne solution is `atomWithReducer`:\n\n```js\nconst countAtom = atomWithReducer(0, (prev, action) => {\n  if (action === 'INC') {\n    return prev + 1\n  }\n  if (action === 'DEC') {\n    return prev - 1\n  }\n  throw new Error('unknown action')\n})\n```\n\nThis is fine, but not very atomic.\nIf we want to get benefit from code splitting / lazy loading,\nWe want to create write only atoms, or action atoms.\n\n```js\nconst baseAtom = atom(0) // do not export\nexport const countAtom = atom((get) => get(baseAtom)) // read only\nexport const incAtom = atom(null, (_get, set) => {\n  set(baseAtom, (prev) => prev + 1)\n})\nexport const decAtom = atom(null, (_get, set) => {\n  set(baseAtom, (prev) => prev - 1)\n})\n```\n\nThis is more atomic and looks like a Jotai way.\n\nYou can also create an action atom that will call another action atom:\n\n```js\n// continued from the previous code\nexport const dispatchAtom = atom(null, (_get, set, action) => {\n  if (action === 'INC') {\n    set(incAtom)\n  } else if (action === 'DEC') {\n    set(decAtom)\n  } else {\n    throw new Error('unknown action')\n  }\n})\n```\n\nWhy do we want it? Because it will be used only when needed.\nIt allows code splitting and dead code elimination.\n\n### In summary\n\nAtoms are building block.\nBy composing atoms based on other atoms,\nwe can implement complicated logic.\nThis is not only for read derived atoms, but also for write action atoms.\n\nEssentially, atoms are like functions, so composing atoms is\nlike composing functions with other functions.\n\n**Note**: We mentioned that our atoms can contain any kind of data, it can be a string, Blob, Observer, anything really. There is just one exception. Because derived atoms are defined using a function, Jotai will not understand if we pass it a function that isn't exactly a pure getter.\nSo what you can do is simply wrap your function in an object.\n\n```js\nconst doublerAtom = atom({ callback: (n) => n * 2 })\n// Usage\nconst [doubler] = useAtom(doublerAtom)\nconst doubledValue = doubler.callback(50) // Will compute to 100\n```\n"
  },
  {
    "path": "docs/guides/core-internals.mdx",
    "content": "---\ntitle: Core internals\ndescription: A simplified version of the core implementation\nnav: 8.10\n---\n\nThis guide is beneficial for those who want to understand the core implementation of Jotai. It's not meant to be a complete example of the\ncore implementation but rather a simplified version. It's inspired by the collection of tweets by Daishi Kato([@dai_shi](https://twitter.com/dai_shi)).\n\n### First Version\n\nLet's start with an easy example. An atom is just a function that will return a configuration object. We are using WeakMap to map atom with their state.\nWeakMap doesn't keep its keys in memory, so if an atom is garbage collected, its state will be garbage collected too. This helps avoid memory leaks.\n\n```js\nimport { useState, useEffect } from 'react'\n\n// atom function returns a config object which contains initial value\nexport const atom = (initialValue) => ({ init: initialValue })\n\n// we need to keep track of the state of the atom.\n// we are using weakmap to avoid memory leaks\nconst atomStateMap = new WeakMap()\nconst getAtomState = (atom) => {\n  let atomState = atomStateMap.get(atom)\n  if (!atomState) {\n    atomState = { value: atom.init, listeners: new Set() }\n    atomStateMap.set(atom, atomState)\n  }\n  return atomState\n}\n\n// useAtom hook returns a tuple of the current value\n// and a function to update the atom's value\nexport const useAtom = (atom) => {\n  const atomState = getAtomState(atom)\n  const [value, setValue] = useState(atomState.value)\n  useEffect(() => {\n    const callback = () => setValue(atomState.value)\n\n    // same atom can be used at multiple components, so we need to\n    // keep listening for atom's state change till component is unmounted.\n    atomState.listeners.add(callback)\n    callback()\n    return () => atomState.listeners.delete(callback)\n  }, [atomState])\n\n  const setAtom = (nextValue) => {\n    atomState.value = nextValue\n\n    // let all the subscribed components know that the atom's state has changed\n    atomState.listeners.forEach((l) => l())\n  }\n\n  return [value, setAtom]\n}\n```\n\nHere's an example using our simplified atom implementation. [Counter example](https://codesandbox.io/s/zealous-field-z2xk6?file=/src/App.js)\n\nRef tweet: [Demystifying the internal of jotai](https://twitter.com/dai_shi/status/1484835169475653634)\n\n### Second Version\n\nHang on! We can do better. In Jotai, we can create derived atom. A derived atom is an atom that depends on other atoms.\n\n```js\nconst priceAtom = atom(10)\nconst readOnlyAtom = atom((get) => get(priceAtom) * 2)\nconst writeOnlyAtom = atom(\n  null, // it's a convention to pass `null` for the first argument\n  (get, set, args) => {\n    set(priceAtom, get(priceAtom) - args)\n  },\n)\nconst readWriteAtom = atom(\n  (get) => get(priceAtom) * 2,\n  (get, set, newPrice) => {\n    set(priceAtom, newPrice / 2)\n    // you can set as many atoms as you want at the same time\n  },\n)\n```\n\nTo keep track of all the dependents, we need to add one more property to the atom's state. Let's say atom X depends on atom Y,\nso when we update atom Y, we also update atom X. This is called dependency tracking.\n\n```js\nconst atomState = {\n  value: atom.init,\n  listeners: new Set(),\n  dependents: new Set(),\n}\n```\n\nWe now need to create functions for reading and writing an atom that can handle updating dependent atoms' state.\n\n```js\nimport { useState, useEffect } from 'react'\n\nexport const atom = (read, write) => {\n  if (typeof read === 'function') {\n    return { read, write }\n  }\n  const config = {\n    init: read,\n\n    // get in the read function is to read the atom value.\n    // It's reactive and read dependencies are tracked.\n    read: (get) => get(config),\n\n    // get in the write function is also to read atom value, but it's not tracked.\n    // set in the write function is to write atom value and\n    // it will invoke the write function of the target atom.\n    write:\n      write ||\n      ((get, set, arg) => {\n        if (typeof arg === 'function') {\n          set(config, arg(get(config)))\n        } else {\n          set(config, arg)\n        }\n      }),\n  }\n  return config\n}\n\n// same as above but the state has one extra property: dependents\nconst atomStateMap = new WeakMap()\nconst getAtomState = (atom) => {\n  let atomState = atomStateMap.get(atom)\n  if (!atomState) {\n    atomState = {\n      value: atom.init,\n      listeners: new Set(),\n      dependents: new Set(),\n    }\n    atomStateMap.set(atom, atomState)\n  }\n  return atomState\n}\n\n// If atom is primitive, we return it's value.\n// If atom is derived, we read the parent atom's value\n// and add current atom to parent's the dependent set (recursively).\nconst readAtom = (atom) => {\n  const atomState = getAtomState(atom)\n  const get = (a) => {\n    if (a === atom) {\n      return atomState.value\n    }\n    const aState = getAtomState(a)\n    aState.dependents.add(atom) // XXX add only\n    return readAtom(a) // XXX no caching\n  }\n  const value = atom.read(get)\n  atomState.value = value\n  return value\n}\n\n// if atomState is modified, we need to notify all the dependent atoms (recursively)\n// now run callbacks for all the components that are dependent on this atom\nconst notify = (atom) => {\n  const atomState = getAtomState(atom)\n  atomState.dependents.forEach((d) => {\n    if (d !== atom) notify(d)\n  })\n  atomState.listeners.forEach((l) => l())\n}\n\n// writeAtom calls atom.write with the necessary params and triggers notify function\nconst writeAtom = (atom, value) => {\n  const atomState = getAtomState(atom)\n\n  // 'a' is some atom from atomStateMap\n  const get = (a) => {\n    const aState = getAtomState(a)\n    return aState.value\n  }\n\n  // if 'a' is the same as atom, update the value, notify that atom and return\n  // else calls writeAtom for 'a' (recursively)\n  const set = (a, v) => {\n    if (a === atom) {\n      atomState.value = v\n      notify(atom)\n      return\n    }\n    writeAtom(a, v)\n  }\n\n  atom.write(get, set, value)\n}\n\nexport const useAtom = (atom) => {\n  const [value, setValue] = useState()\n  useEffect(() => {\n    const callback = () => setValue(readAtom(atom))\n    const atomState = getAtomState(atom)\n    atomState.listeners.add(callback)\n    callback()\n    return () => atomState.listeners.delete(callback)\n  }, [atom])\n  const setAtom = (nextValue) => {\n    writeAtom(atom, nextValue)\n  }\n  return [value, setAtom]\n}\n```\n\nHere is an example using our derived atom implementation. [Derived counter example](https://codesandbox.io/s/affectionate-chandrasekhar-nuxms?file=/src/App.js)\n\nRef tweet: [Supporting derived atoms](https://twitter.com/dai_shi/status/1485434083778117632)\n"
  },
  {
    "path": "docs/guides/debugging.mdx",
    "content": "---\ntitle: Debugging\nnav: 8.07\nkeywords: debug,labels,devtools,freeze\n---\n\nIn basic apps, `console.log` can be our best friend for debugging atoms, but when applications get bigger and we have more atoms to use, logging would not be a good way of debugging atoms.\nJotai provides two ways of debugging atoms, **React Dev Tools** and **Redux Dev tools**. For reading values and simple debugging, React Dev Tools might suit you, but for more complicated tasks like Time-travelling and setting values, Redux Dev Tools would be a better option.\n\n## Debug labels\n\nIt is worth mentioning that we have a concept called **Debug labels** in Jotai which may help us with debugging.\nBy default each Jotai state has the label like `1:<no debugLabel>` with number being internal `key` assigned to each atom automatically. But you can add labels to atoms to help you distinguish them more easily with `debugLabel`.\n\n```js\nconst countAtom = atom(0)\n// countAtom's debugLabel by default is 'atom1'\nif (process.env.NODE_ENV !== 'production') {\n  countAtom.debugLabel = 'count'\n  // debugLabel is 'count' now\n}\n```\n\nJotai provides both a Babel and a SWC plugin, that adds a debugLabel automatically to every atom, which makes things easier for us. For more info, check out [jotai-babel](https://github.com/jotaijs/jotai-babel) and [@swc-jotai/debug-label](https://github.com/pmndrs/jotai/blob/main/docs/tools/swc.mdx)\n\n## Using React Dev Tools\n\nYou can use [React Dev Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)\nto inspect Jotai state. To achieve that [useDebugValue](https://react.dev/reference/react/useDebugValue)\nis used inside custom hooks. Keep in mind that it only works in dev mode\n(such as `NODE_ENV === 'development'`).\n\n### useAtom\n\n`useAtom` calls `useDebugValue` for atom values, so if you select the component that consumes Jotai atoms in React Dev Tools, you would see \"Atom\" hooks for each atom that is used in the component along with the value it has right now.\n\n### useAtomsDebugValue\n\n`useAtomsDebugValue` catches all atoms in a component tree under Provider (or an entire tree for Provider-less mode), and `useDebugValue` for all atoms values.\nIf you navigate to the component that has `useAtomsDebugValue` in the React Dev Tools, we can see a custom hook \"AtomsDebugValue\" which allows you to see all atom values and their dependents.\n\nOne use case is to put the hook just under the `Provider` component:\n\n```jsx\nconst DebugAtoms = () => {\n  useAtomsDebugValue()\n  return null\n}\n\nconst Root = () => (\n  <Provider>\n    <DebugAtoms />\n    <App />\n  </Provider>\n)\n```\n\n## Using Redux DevTools\n\nYou can also use [Redux DevTools](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd)\nto inspect atoms, with many features like Time-travelling and value dispatching.\n\n### [useAtomDevtools](https://jotai.org/docs/api/devtools#use-atom-devtools)\n\n> `useAtomDevtools` is a React hook that manages ReduxDevTools extension for a particular atom.\n\nIf you have a specific atom in mind that you may want to debug, `useAtomDevtools` can be a good option.\n\n```js\nconst countAtom = atom(0)\n// setting countAtom.debugLabel is recommended if we have more atoms\n\nfunction Counter() {\n  const [count, setCount] = useAtom(countAtom)\n  useAtomDevtools(countAtom)\n}\n```\n\nNow if we try `setCount`, we can see that the Redux Dev Tools logs those changes immediately.\n\n![](https://lh3.googleusercontent.com/pw/AP1GczMgeYQOyCAc69gJIAcRBtKO9BOUEE3SQB5Bl-7IScJfChWGnVb3B0OmlhrjK8caQVnj-HtyN1cpv1l1K9kE4pxwapUwu_2OB-dO_G18ZUC1NbDJFiXYRW9jX8OeDBJeWg1Qx9_IdkfoaoIin90A8gSE=w828-h268-s-no-gm)\n![]()\n\n#### Time travel\n\nSometimes we need to switch to a specific value of our atoms' state, with Time travelling this is possible.\nYou can hover on each action you see in the devtools and see the **Jump** option there, with clicking it you'd be able to switch to that specific value.\n\n#### Pause\n\nIf we don't record changes on atoms, we can stop watching those using the **Pausing** feature.\n\n![](https://lh3.googleusercontent.com/pw/AP1GczP8hTBFtwlx0BJGGbbcXgfhMNG2Vz_uozdVnrTJHwMb1gKx55TP59WgvsMwgIyExwscgYZSpYDmxCJXjk_pKy6wP-K-0p287lkRXdTZEf074xUZr8fnIpkwg-zN14VXZ2STet1sVgTTawm49mc8Oygb=w395-h87-s-no-gm)\n\n#### Dispatch\n\nIt's possible to set values on atoms with the **Dispatch** feature. You can do that by clicking on the **Show Dispatcher** button.\n![](https://lh3.googleusercontent.com/pw/AP1GczMNn6aXTA7K8ZFzUj17I40cm0o7joOG6E76Q6UVnXYJ3TO7ItRI6Jr1EIxogfY9P2xkiQfyYqB7_aU--R_vdSyNXAtTfPuxxLymApRoZov0-6ZHS7mmxxxD4Ku1JnqTRyPyZaQHyQPkq8j4CciQaISV=w832-h149-s-no-gm)\nThis would set the `countAtoms`'s value to `5`.\n\n> We should note that the value will be parsed by JSON.parse, so pass supported values.\n\n### [useAtomsDevtools](../tools/devtools.mdx)\n\n> `useAtomsDevtools` is a catch-all version of `useAtomDevtools` where it shows all atoms in the store instead of showing a specific one.\n\nWe'd recommend this hook if you want to keep track of all of your atoms in one place. It means every action on every atom that is placed in the bottom of this hook (in the React tree) will be caught by the Redux Dev Tools.\n\nEvery feature of `useAtomDevtools` is supported in this hook, but there's an extra feature, which includes giving more information about atoms dependents like:\n\n```json\n{\n  \"values\": {\n    \"atom1:count\": 0,\n    \"atom2:doubleCount\": 0,\n    \"atom3:half\": 0,\n    \"atom4:sum\": 0\n  },\n  \"dependents\": {\n    \"atom1:count\": [\"atom1:count\", \"atom2:doubleCount\", \"atom4:sum\"],\n    \"atom2:doubleCount\": [\"atom3:half\", \"atom4:sum\"],\n    \"atom3:half\": [\"atom4:sum\"],\n    \"atom4:sum\": []\n  }\n}\n```\n\n## Frozen Atoms\n\nTo find bugs where you accidentally tried to mutate objects stored in atoms you\ncould use `freezeAtom` or `freezeAtomCreator`from `jotai/utils` bundle.\nWhich returns atoms value that is deeply freezed with `Object.freeze`.\n\n### freezeAtom\n\n```ts\nfreezeAtom(anAtom): AtomType\n```\n\n`freezeAtom` takes an existing atom and make it \"frozen\".\nIt returns the same atom.\nThe atom value will be deeply frozen by `Object.freeze`.\nIt is useful to find bugs where you unintentionally tried\nto change objects (states) which can lead to unexpected behavior.\nYou may use `freezeAtom` with all atoms to prevent this situation.\n\n#### Parameters\n\n**anAtom** (required): An atom you wish to freeze.\n\n#### Examples\n\n```js\nimport { atom } from 'jotai'\nimport { freezeAtom } from 'jotai/utils'\n\nconst objAtom = freezeAtom(atom({ count: 0 }))\n```\n\n### freezeAtomCreator\n\nIf you need, you can define a factory for `freezeAtom`.\n\n```ts\nimport { freezeAtom } from 'jotai/utils'\n\nexport function freezeAtomCreator<\n  CreateAtom extends (...args: unknown[]) => Atom<unknown>,\n>(createAtom: CreateAtom): CreateAtom {\n  return ((...args: unknown[]) => freezeAtom(createAtom(...args))) as never\n}\n```\n"
  },
  {
    "path": "docs/guides/initialize-atom-on-render.mdx",
    "content": "---\ntitle: Initializing state on render\ndescription: How to initialize atom state on initial render\nnav: 8.13\n---\n\nThere are times when you need to create an reusable component which uses atoms.\n\nThese atoms' initial state are determined by the props passed to the component.\n\nBelow is a basic example illustrating how you can use `Provider` and its prop, `initialValues`, to initialize state.\n\n### Basic Example\n\n> CodeSandbox link: [codesandbox](https://codesandbox.io/s/init-atoms-with-usehydrateatoms-nryk1w).\n\nConsider a basic example where you have a reusable `TextDisplay` component that allows you to display and update plain text.\n\nThis component has two child components, `PrettyText` and `UpdateTextInput`.\n\n- `PrettyText` displays the text in blue.\n- `UpdateTextInput` is an input field which updates the text value.\n\nAs opposed to passing `text` as a prop in the two child components, you decided that the `text` state should be shared between components as an atom.\n\nTo make `TextDisplay` component reusable, we take in a prop `initialTextValue`, which determines the initial state of the `text` atom.\n\nTo tie `initialTextValue` to `textAtom`, we wrap the child components in a component where we create a new store and pass it to a `Provider` component.\n\n```jsx\nconst textAtom = atom('')\n\nconst PrettyText = () => {\n  const [text] = useAtom(textAtom)\n  return (\n    <>\n      <text\n        style={{\n          color: 'blue',\n        }}\n      >\n        {text}\n      </text>\n    </>\n  )\n}\n\nconst UpdateTextInput = () => {\n  const [text, setText] = useAtom(textAtom)\n  const handleInputChange = (e) => {\n    setText(e.target.value)\n  }\n  return (\n    <>\n      <input onChange={handleInputChange} value={text} />\n    </>\n  )\n}\n\nconst HydrateAtoms = ({ initialValues, children }) => {\n  // initialising on state with prop on render here\n  useHydrateAtoms(initialValues)\n  return children\n}\n\nexport const TextDisplay = ({ initialTextValue }) => (\n  <Provider>\n    <HydrateAtoms initialValues={[[textAtom, initialTextValue]]}>\n      <PrettyText />\n      <br />\n      <UpdateTextInput />\n    </HydrateAtoms>\n  </Provider>\n)\n```\n\nNow, we can easily reuse `TextDisplay` component with different initial text values despite them referencing the \"same\" atom.\n\n```jsx\nexport default function App() {\n  return (\n    <div className=\"App\">\n      <TextDisplay initialTextValue=\"initial text value 1\" />\n\n      <TextDisplay initialTextValue=\"initial text value 2\" />\n    </div>\n  )\n}\n```\n\nThis behavior is due to our child components looking for the lowest common `Provider` ancestor to derive its value.\n\nFor more information on `Provider` behavior, please read the docs [here](../core/provider.mdx).\n\nFor more complex use cases, check out [Scope extension](../extensions/scope.mdx).\n\n### Using Typescript\n\n`useHydrateAtoms` has overloaded types and typescript cannot extract types from overloaded function. It is recommended to use a `Map` when passing initial atom values to the `useHydrateAtoms`.\n\nHere is a working example:\n\n```tsx\nimport type { ReactNode } from 'react'\nimport { Provider, atom, useAtomValue } from 'jotai'\nimport type { WritableAtom } from 'jotai'\nimport { useHydrateAtoms } from 'jotai/utils'\n\nconst testAtom = atom('')\n\nexport default function App() {\n  return (\n    <Provider>\n      <AtomsHydrator atomValues={[[testAtom, 'hello']]}>\n        <Component />\n      </AtomsHydrator>\n    </Provider>\n  )\n}\n\n//This component contains all the states and the logic\nfunction Component() {\n  const testAtomValue = useAtomValue(testAtom)\n  return <div>{testAtomValue}</div>\n}\n\nfunction AtomsHydrator({\n  atomValues,\n  children,\n}: {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  atomValues: Iterable<\n    readonly [WritableAtom<unknown, [any], unknown>, unknown]\n  >\n  children: ReactNode\n}) {\n  useHydrateAtoms(new Map(atomValues))\n  return children\n}\n```\n"
  },
  {
    "path": "docs/guides/migrating-to-v2-api.mdx",
    "content": "---\ntitle: v2 API migration\ndescription: New \"Async\" API\nnav: 8.0\n---\n\nRFC: https://github.com/pmndrs/jotai/discussions/1514\n\nJotai v1 is released at June 2022, and there has been various feedbacks.\nReact also proposes first-class support for promises.\nJotai v2 will have a new API.\n\nUnfortunately, there are some breaking changes along with new features.\n\n### What are new features\n\n#### Vanilla library\n\nJotai comes with vanilla (non-React) functions\nand React functions separately.\nThey are provided from alternate entry points like `jotai/vanilla`.\n\n#### Store API\n\nJotai exposes store interface so that you can directly manipulate atom values.\n\n```js\nimport { createStore } from 'jotai' // or from 'jotai/vanilla'\n\nconst store = createStore()\nstore.set(fooAtom, 'foo')\n\nconsole.log(store.get(fooAtom)) // prints \"foo\"\n\nconst unsub = store.sub(fooAtom, () => {\n  console.log('fooAtom value in store is changed')\n})\n// call unsub() to unsubscribe.\n```\n\nYou can also create your own React Context to pass a store.\n\n#### More flexible atom `write` function\n\nThe write function can accept multiple arguments,\nand return a value.\n\n```js\natom(\n  (get) => get(...),\n  (get, set, arg1, arg2, ...) => {\n    ...\n    return someValue\n  }\n)\n```\n\n### What are breaking\n\n#### Async atoms are no longer special\n\nAsync atoms are just normal atoms with promise values.\nAtoms getter functions don't resolve promises.\nOn the other hand, `useAtom` hook continues to resolve promises.\n\nSome utils like `splitAtom` expects sync atoms,\nand won't work with async atoms.\n\n#### Writable atom type is changed (TypeScript only)\n\n```ts\n// Old\nWritableAtom<Value, Arg, Result extends void | Promise<void>>\n\n// New\nWritableAtom<Value, Args extends unknown[], Result>\n```\n\nIn general, we should avoid using `WritableAtom` type directly.\n\n#### Some functions are dropped\n\n- Provider's `initialValues` prop is removed, because `store` is more flexible.\n- Provider's scope props is removed, because you can create own context.\n- `abortableAtom` util is removed, because the feature is included by default\n- `waitForAll` util is removed, because `Promise.all` just works\n\n### Migration guides\n\n#### Async atoms\n\n`get` function for read function of async atoms\ndoesn't resolve promises, so you have to put `await` or `.then()`.\n\nIn short, the change is something like the following.\n(If you are TypeScript users, types will tell where to changes.)\n\n##### Previous API\n\n```js\nconst asyncAtom = atom(async () => 'hello')\nconst derivedAtom = atom((get) => get(asyncAtom).toUppercase())\n```\n\n##### New API\n\n```js\nconst asyncAtom = atom(async () => 'hello')\nconst derivedAtom = atom(async (get) => (await get(asyncAtom)).toUppercase())\n// or\nconst derivedAtom = atom((get) => get(asyncAtom).then((x) => x.toUppercase()))\n```\n\n#### Provider's `initialValues` prop\n\n##### Previous API\n\n```jsx\nconst countAtom = atom(0)\n\n  // in component\n  <Provider initialValues={[[countAtom, 1]]}>\n    ...\n```\n\n##### New API\n\n```jsx\nconst countAtom = atom(0)\n\nconst HydrateAtoms = ({ initialValues, children }) => {\n  useHydrateAtoms(initialValues)\n  return children\n}\n\n  // in component\n  <Provider>\n    <HydrateAtoms initialValues={[[countAtom, 1]]}>\n      ...\n```\n\n#### Provider's `scope` prop\n\n##### Previous API\n\n```jsx\nconst myScope = Symbol()\n\n  // Parent component\n  <Provider scope={myScope}>\n    ...\n  </Provider>\n\n  // Child component\n  useAtom(..., myScope)\n```\n\n##### New API\n\n```jsx\nconst MyContext = createContext()\nconst store = createStore()\n\n  // Parent component\n  <MyContext.Provider value={store}>\n    ...\n  </MyContext.Provider>\n\n  // Child Component\n  const store = useContext(MyContext)\n  useAtom(..., { store })\n```\n\n#### `abortableAtom` util\n\nYou no longer need the previous `abortableAtom` util,\nbecause it's now supported with the normal `atom`.\n\n##### Previous API\n\n```js\nconst asyncAtom = abortableAtom(async (get, { signal }) => {\n ...\n}\n```\n\n##### New API\n\n```js\nconst asyncAtom = atom(async (get, { signal }) => {\n  ...\n}\n```\n\n#### `waitForAll` util\n\nYou no longer need the previous `waitForAll` util,\nbecause we can use native Promise APIs.\n\n##### Previous API\n\n```js\nconst allAtom = waitForAll([fooAtom, barAtom])\n```\n\n##### New API\n\n```js\nconst allAtom = atom((get) => Promise.all([get(fooAtom), get(barAtom)]))\n```\n\nNote that creating an atom in render function can cause [infinite loop](../core/atom.mdx#note-about-creating-an-atom-in-render-function)\n\n#### `splitAtom` util (or some other utils) with async atoms\n\n`splitAtom` util only accepts sync atoms.\nYou need to unwrap async atoms before passing.\n\nThis applies to some other utils like `atomsWithQuery` from `jotai-tanstack-query`.\n\n##### Previous API\n\n```js\nconst splittedAtom = splitAtom(asyncArrayAtom)\n```\n\n##### New API\n\n```js\nconst splittedAtom = splitAtom(unwrap(asyncArrayAtom, () => []))\n```\n\nAs of writing, `unwrap` is unstable and not documented.\nYou can instead use `loadable`, which gives more control on loading status.\nIf you need to use `<Suspense>`, atoms-in-atom pattern would help.\n\nFor more information, refer the following discussions:\n\n- https://github.com/pmndrs/jotai/discussions/1615\n- https://github.com/jotaijs/jotai-tanstack-query/issues/21\n- https://github.com/pmndrs/jotai/discussions/1751\n\n### Some other changes\n\n#### Utils\n\n- `atomWithStorage` util's `delayInit` is removed as being default. Also it will always render `initialValue` on first render, and the stored value, if any, on subsequent renders. The new behavior differs from v1. See https://github.com/pmndrs/jotai/discussions/1737 for more information.\n- `useHydrateAtoms` can only accept writable atoms.\n\n#### Import statements\n\nThe v2 API is also provided from alternate entry points for library authors and non-React users.\n\n- `jotai/vanilla`\n- `jotai/vanilla/utils`\n- `jotai/react`\n- `jotai/react/utils`\n\n```js\n// Available since v1.11.0\nimport { atom } from 'jotai/vanilla'\nimport { useAtom } from 'jotai/react'\n\n// Available since v2.0.0\nimport { atom } from 'jotai' // is same as 'jotai/vanilla'\nimport { useAtom } from 'jotai' // is same as 'jotai/react'\n```\n\nNote: If you are not using ESM, you want to prefer using `jotai/vanilla` etc. instead of `jotai`, for better tree shaking.\n"
  },
  {
    "path": "docs/guides/nextjs.mdx",
    "content": "---\ntitle: Next.js\ndescription: How to use Jotai with Next.js\nnav: 8.03\nkeywords: next,nextjs\n---\n\n### Hydration\n\nJotai has support for hydration of atoms with `useHydrateAtoms`. The documentation for the hook can be seen [here](../utilities/ssr.mdx).\n\n### Sync with router\n\nIt's possible to sync Jotai with the router. You can achieve this with `atomWithHash`:\n\n```js\nconst pageAtom = atomWithHash('page', 1, {\n  replaceState: true,\n  subscribe: (callback) => {\n    Router.events.on('routeChangeComplete', callback)\n    window.addEventListener('hashchange', callback)\n    return () => {\n      Router.events.off('routeChangeComplete', callback)\n      window.removeEventListener('hashchange', callback)\n    }\n  },\n})\n```\n\nThis way you have full control over what [router event](https://nextjs.org/docs/api-reference/next/router#routerevents) you want to subscribe to.\n\n> #### In Next.js 13\n>\n> As of Next.js 13 there have been some changes to the `Router.events.on()` which no longer expose events. There are plans in the [App Router Roadmap](https://beta.nextjs.org/docs/app-directory-roadmap#planned-features) for event intercepting and hash handling. However there is no ETA on when this will be available or what it will look like. For now when trying to the `atomWithHash()` you will not get the atom loading with any data when navigating using the router, only when the page is reloaded or the component is rerendered. It is also recommended that you set the `setHash` option to `replaceState` as Next.js appears to use window.history in the background and this will allow the user to use the browser back button.\n\n### You can't return promises in server side rendering\n\nIt's important to note that you can't return promises with SSR - However, it's possible to guard against it inside the atom definition.\n\nIf possible use `useHydrateAtoms` to hydrate values from the server.\n\n```js\nconst postData = atom((get) => {\n  const id = get(postId)\n  if (isSSR || prefetchedPostData[id]) {\n    return prefetchedPostData[id] || EMPTY_POST_DATA\n  }\n  return fetchData(id) // returns a promise\n})\n```\n\n### Provider\n\nBy default, Jotai uses an implicit global store to keep track of atom values. This is what is referred to as \"provider-less\" mode. This becomes an issue in SSR scenario because this global store is kept alive and is shared between multiple requests, which can lead to bugs and security risks.\n\nTo limit the lifetime of the store to the scope of one request, you need to use a [Provider](../core/provider.mdx) at the root of your app (or a subtree if you're using Jotai only for a part of your application).\n\n```typescript\nimport { Provider } from 'jotai'\n\nfunction App({ Component, pageProps }: AppProps) {\n  return (\n    <Provider>\n      <Component {...pageProps} />\n    </Provider>\n  )\n}\n```\n\nIn this case:\n\n1. `Provider` will hold the state of the atoms used in its subtree instead of the global store.\n2. `Provider`'s lifetime will be the same as the app itself, and since the app is recreated on each SSR request we essentially limit the lifetime of the store to a single request as well.\n\n### SWC plugins\n\nJotai provides SWC plugins for better DX while developing with Next.js. [Find more info in the SWC section.](../tools/swc.mdx)\n\n### Examples\n\n#### Clock\n\n<Stackblitz\n  id=\"stackblitz-starters-nsugnt\"\n  file=\"store%2Findex.ts,components%2FClock.tsx\"\n/>\n\n#### HN Posts\n\nPage Router demo:\n\n<Stackblitz\n  id=\"stackblitz-starters-cnz9lg\"\n  file=\"store%2Findex.ts,pages%2F_app.tsx,pages%2Findex.tsx\"\n/>\n\nApp Router [demo on Stackblitz](https://stackblitz.com/edit/jotai-nextjs-app-router-demo?file=store%2Findex.ts,app%2Flayout.tsx,components%2FPost.tsx,app%2Fpage.tsx)\n\n#### Next.js repo\n\n```bash\nnpx create-next-app --example with-jotai with-jotai-app\n```\n\nHere's a [link](https://github.com/vercel/next.js/tree/canary/examples/with-jotai).\n"
  },
  {
    "path": "docs/guides/performance.mdx",
    "content": "---\ntitle: Performance\ndescription: How to limit extra re-renders\nnav: 8.08\nkeywords: performance\n---\n\n**Note**: This guide has room for improvement. Consider it as FYI for now.\n\nJotai & React gives us quite a few tools to manage the re-renders that happen in the app lifecycle.\nFirst, please read about the difference [between render & commit](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#browsing-commits), because that's very important to understand before going further.\n\n### Cheap renders\n\nAs seen in the [core section](../core/atom.mdx), due to React 18 default behaviour (but overall good practice), you have to make sure your component functions are _idempotent_.\nThey will be called multiple times during the render phase, even at mount. So we need to keep our renders cheap at all cost!\n\n#### Heavy computation\n\nAlways make heavy computation outside of the React lifecycle (in actions for example)\n\nDont's:\n\n```js\n// Heavy computation for each item\nconst selector = (s) => s.filter(heavyComputation)\nconst Profile = () => {\n  const [computed] = useAtom(selectAtom(friendsAtom, selector))\n}\n```\n\nDo's:\n\n```js\nconst friendsAtom = atom([])\nconst fetchFriendsAtom = atom(null, async (get, set, payload) => {\n  // Fetch all friends\n  const res = await fetch('https://jsonplaceholder.typicode.com/users')\n  const data = await res.json()\n\n  // Make heavy computation once only\n  const computed = data.filter(heavyComputation)\n  set(friendsAtom, computed)\n})\n// Usage in components\nconst Profile = () => {\n  const [friends] = useAtom(friendsAtom)\n}\n```\n\n#### Small components\n\nObserved atoms should only re-render small parts of your application that required an update. The less comparison React has to make, the shorter your render time will be.\n\nDont's:\n\n```jsx\nconst Profile = () => {\n  const [name] = useAtom(nameAtom)\n  const [age] = useAtom(ageAtom)\n  return (\n    <>\n      <div>{name}</div>\n      <div>{age}</div>\n    </>\n  )\n}\n```\n\nDo's:\n\n```jsx\nconst NameComponent = () => {\n  const [name] = useAtom(nameAtom)\n  return <div>{name}</div>\n}\nconst AgeComponent = () => {\n  const [age] = useAtom(ageAtom)\n  return <div>{age}</div>\n}\nconst Profile = () => {\n  return (\n    <>\n      <NameComponent />\n      <AgeComponent />\n    </>\n  )\n}\n```\n\n### Render on demand\n\nUsually, the main performance overhead will come from re-rendering parts of your app that did not need to, or way more than they should.\n\nWe have a few tools to deal with \"when\" React should render our components. If you have not seen the usage of `useMemo` and `useCallback`, please check the official React documentation for more info before going further.\nThey are of great use to reduce un-necessary renders where your app is not fluid.\n\nBut Jotai also provides its set of tools to handle the \"when\" our atoms should trigger a re-render.\n\n- Out of the box, Jotai encourages you to split your data into atomic parts, hence each atom is stored separately and will only trigger a re-render when their own value change\n- `selectAtom` allows you to subscribe to specific part of a large object and only re-render on value change\n- `focusAtom` same as selectAtom, but creating a new atom for the part, giving a setter to update that specific part easily\n- `splitAtom` does the work of selectAtom/focusAtom for a dynamic list\n\nWhile this seems simplistic, it is simple to reason about. That was the goal, let's keep it simple to keep it fast.\n\n#### Frequent or rare updates\n\nAsk yourself whether your atom is usually going to be frequently update or more rarely.\nLet's imagine an atom containing an object that changes almost every second, it may not be best suited to \"focus\" on a specific properties of this object using `focusAtom`, because anyway they will all re-render in the same time, so best adding no overhead and not create any more atoms.\n\nOn the other hand, if your object has properties that rarely change, and most importantly, that change independently from the other properties, then you may want to use `focusAtom` or `selectAtom` to prevent un-necessary renders.\n"
  },
  {
    "path": "docs/guides/persistence.mdx",
    "content": "---\ntitle: Persistence\ndescription: How to persist atoms\nnav: 8.14\n---\n\nJotai has an [atomWithStorage function in the utils bundle](../utilities/storage.mdx) for persistence that supports persisting state in `sessionStorage`, `localStorage`, `AsyncStorage`, or the URL hash.\n\n(Note: This guide is a bit outdated and requires some rewrites.)\n\nThere are also several alternate implementations here:\n\n### A simple pattern with localStorage\n\n```js\nconst strAtom = atom(localStorage.getItem('myKey') ?? 'foo')\n\nconst strAtomWithPersistence = atom(\n  (get) => get(strAtom),\n  (get, set, newStr) => {\n    set(strAtom, newStr)\n    localStorage.setItem('myKey', newStr)\n  },\n)\n```\n\n### A helper function with localStorage and JSON parse\n\n```js\nconst atomWithLocalStorage = (key, initialValue) => {\n  const getInitialValue = () => {\n    const item = localStorage.getItem(key)\n    if (item !== null) {\n      return JSON.parse(item)\n    }\n    return initialValue\n  }\n  const baseAtom = atom(getInitialValue())\n  const derivedAtom = atom(\n    (get) => get(baseAtom),\n    (get, set, update) => {\n      const nextValue =\n        typeof update === 'function' ? update(get(baseAtom)) : update\n      set(baseAtom, nextValue)\n      localStorage.setItem(key, JSON.stringify(nextValue))\n    },\n  )\n  return derivedAtom\n}\n```\n\n(Error handling should be added.)\n\n### A helper function with AsyncStorage and JSON parse\n\nThis requires [onMount](../core/atom.mdx#onmount-property).\n\n```js\nconst atomWithAsyncStorage = (key, initialValue) => {\n  const baseAtom = atom(initialValue)\n  baseAtom.onMount = (setValue) => {\n    ;(async () => {\n      const item = await AsyncStorage.getItem(key)\n      setValue(JSON.parse(item))\n    })()\n  }\n  const derivedAtom = atom(\n    (get) => get(baseAtom),\n    (get, set, update) => {\n      const nextValue =\n        typeof update === 'function' ? update(get(baseAtom)) : update\n      set(baseAtom, nextValue)\n      AsyncStorage.setItem(key, JSON.stringify(nextValue))\n    },\n  )\n  return derivedAtom\n}\n```\n\nDon't forget to check out the [Async documentation](../guides/async.mdx) for more details on how to use async atoms.\n\n### Example with sessionStorage\n\nSame as AsyncStorage, just use `atomWithStorage` util and override the default storage with the `sessionStorage`\n\n```js\nimport { atomWithStorage, createJSONStorage } from 'jotai/utils'\n\nconst storage = createJSONStorage(() => sessionStorage)\nconst someAtom = atomWithStorage('some-key', someInitialValue, storage)\n```\n\n### A serialize atom pattern\n\n```tsx\ntype Actions =\n  | { type: 'serialize'; callback: (value: string) => void }\n  | { type: 'deserialize'; value: string }\n\nconst serializeAtom = atom(null, (get, set, action: Actions) => {\n  if (action.type === 'serialize') {\n    const obj = {\n      todos: get(todosAtom).map(get),\n    }\n    action.callback(JSON.stringify(obj))\n  } else if (action.type === 'deserialize') {\n    const obj = JSON.parse(action.value)\n    // needs error handling and type checking\n    set(\n      todosAtom,\n      obj.todos.map((todo: Todo) => atom(todo)),\n    )\n  }\n})\n\nconst Persist = () => {\n  const [, dispatch] = useAtom(serializeAtom)\n  const save = () => {\n    dispatch({\n      type: 'serialize',\n      callback: (value) => {\n        localStorage.setItem('serializedTodos', value)\n      },\n    })\n  }\n  const load = () => {\n    const value = localStorage.getItem('serializedTodos')\n    if (value) {\n      dispatch({ type: 'deserialize', value })\n    }\n  }\n  return (\n    <div>\n      <button onClick={save}>Save to localStorage</button>\n      <button onClick={load}>Load from localStorage</button>\n    </div>\n  )\n}\n```\n\n#### Examples\n\n<Stackblitz id=\"vitejs-vite-zda5uw\" file=\"src%2FApp.tsx\" />\n\n### A pattern with atomFamily\n\n```tsx\ntype Actions =\n  | { type: 'serialize'; callback: (value: string) => void }\n  | { type: 'deserialize'; value: string }\n\nconst serializeAtom = atom(null, (get, set, action: Actions) => {\n  if (action.type === 'serialize') {\n    const todos = get(todosAtom)\n    const todoMap: Record<string, { title: string; completed: boolean }> = {}\n    todos.forEach((id) => {\n      todoMap[id] = get(todoAtomFamily({ id }))\n    })\n    const obj = {\n      todos,\n      todoMap,\n      filter: get(filterAtom),\n    }\n    action.callback(JSON.stringify(obj))\n  } else if (action.type === 'deserialize') {\n    const obj = JSON.parse(action.value)\n    // needs error handling and type checking\n    set(filterAtom, obj.filter)\n    obj.todos.forEach((id: string) => {\n      const todo = obj.todoMap[id]\n      set(todoAtomFamily({ id, ...todo }), todo)\n    })\n    set(todosAtom, obj.todos)\n  }\n})\n\nconst Persist = () => {\n  const [, dispatch] = useAtom(serializeAtom)\n  const save = () => {\n    dispatch({\n      type: 'serialize',\n      callback: (value) => {\n        localStorage.setItem('serializedTodos', value)\n      },\n    })\n  }\n  const load = () => {\n    const value = localStorage.getItem('serializedTodos')\n    if (value) {\n      dispatch({ type: 'deserialize', value })\n    }\n  }\n  return (\n    <div>\n      <button onClick={save}>Save to localStorage</button>\n      <button onClick={load}>Load from localStorage</button>\n    </div>\n  )\n}\n```\n\n#### Examples\n\n<Stackblitz id=\"vitejs-vite-z4kjv3\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/guides/react-native.mdx",
    "content": "---\ntitle: React Native\ndescription: Using Jotai in React Native\nnav: 8.06\nkeywords: native,ios,android\n---\n\nJotai atoms can be used in React Native applications with absolutely no changes.\nOur goal is to always be 100% compatible with React-Native.\n\n### Persistence\n\nWhen it comes to persistence feature, the implementation specific to React Native are detailed in the [atomWithStorage function in the utils bundle](../utilities/storage.mdx).\n\n### Performance\n\nThere is no known specific overhead when using Jotai in your app. Some libraries will add some/lots of additional properties and methods to the stored data for the practical usage, but Jotai behaves differently and you're always manipulating simple stuff that could barely be shortcuted.\n\nJotai atomic architecture will encourage you to split logic and data, providing a top-most experience to control every one of your render ([or commits, to be precise](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#browsing-commits)) and therefore reach the best performances.\n\nAnd always remember that renders have to be fast, split calculation logic to async actions.\n"
  },
  {
    "path": "docs/guides/remix.mdx",
    "content": "---\ntitle: Remix\ndescription: How to use Jotai with Remix\nnav: 8.05\nkeywords: remix\nstatus: draft\n---\n\n### Hydration\n\nJotai has support for hydration of atoms with `useHydrateAtoms`. The documentation for the hook can be seen [here](../utilities/ssr.mdx).\n"
  },
  {
    "path": "docs/guides/resettable.mdx",
    "content": "---\ntitle: Resettable\ndescription: How to use resettable atoms\nnav: 8.99\npublished: false\n---\n\nThe Jotai core doesn't support resettable atoms.\nBut you can create those with helper functions from `jotai/utils`.\n\n### Primitive resettable atom with atomWithReset / useResetAtom\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithReset, useResetAtom } from 'jotai/utils'\n\nconst todoListAtom = atomWithReset([\n  { description: 'Add a todo', checked: false },\n])\n\nconst TodoList = () => {\n  const [todoList, setTodoList] = useAtom(todoListAtom)\n  const resetTodoList = useResetAtom(todoListAtom)\n\n  return (\n    <>\n      <ul>\n        {todoList.map((todo) => (\n          <li>{todo.description}</li>\n        ))}\n      </ul>\n\n      <button\n        onClick={() =>\n          setTodoList((l) => [\n            ...l,\n            {\n              description: `New todo ${new Date().toDateString()}`,\n              checked: false,\n            },\n          ])\n        }\n      >\n        Add todo\n      </button>\n      <button onClick={resetTodoList}>Reset</button>\n    </>\n  )\n}\n```\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-2s1vg6\" file=\"src%2FApp.tsx\" />\n\n### Derived atom with RESET symbol\n\n```jsx\nimport { atom, useAtom, useSetAtom } from 'jotai'\nimport { atomWithReset, useResetAtom, RESET } from 'jotai/utils'\n\nconst dollarsAtom = atomWithReset(0)\nconst centsAtom = atom(\n  (get) => get(dollarsAtom) * 100,\n  (get, set, newValue: number | typeof RESET) =>\n    set(dollarsAtom, newValue === RESET ? newValue : newValue / 100)\n)\n\nconst ResetExample = () => {\n  const [dollars] = useAtom(dollarsAtom)\n  const setCents = useSetAtom(centsAtom)\n  const resetCents = useResetAtom(centsAtom)\n\n  return (\n    <>\n      <h3>Current balance ${dollars}</h3>\n      <button onClick={() => setCents(100)}>Set $1</button>\n      <button onClick={() => setCents(200)}>Set $2</button>\n      <button onClick={resetCents}>Reset</button>\n    </>\n  )\n}\n```\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-wjgquh\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/guides/testing.mdx",
    "content": "---\ntitle: Testing\ndescription: How to test your code using Jotai\nnav: 8.09\nkeywords: test,testing\n---\n\nWe echo the [guiding principles of Testing library](https://testing-library.com/docs/guiding-principles/):\n\n- \"The more your tests resemble the way your software is used, the more confidence they can give you.\"\n\nWe encourage you to write tests, like the user would interact with your atoms and components,\ntherefore treating Jotai as an implementation detail.\n\nHere's an example using [React testing library](https://github.com/testing-library/react-testing-library):\n\n`Counter.tsx`:\n\n```jsx\nimport { atom, useAtom } from 'jotai'\n\nexport const countAtom = atom(0)\n\nexport function Counter() {\n  const [count, setCount] = useAtom(countAtom)\n  return (\n    <h1>\n      <p>{count}</p>\n      <button onClick={() => setCount((c) => c + 1)}>one up</button>\n    </h1>\n  )\n}\n```\n\n`Counter.test.ts`:\n\n```jsx\nimport React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { Counter } from './Counter'\n\ntest('should increment counter', () => {\n  // Arrange\n  render(<Counter />)\n\n  const counter = screen.getByText('0')\n  const incrementButton = screen.getByText('one up')\n  // Act\n  await userEvent.click(incrementButton)\n  // Assert\n  expect(counter.textContent).toEqual('1')\n})\n```\n\n### Injected Values\n\nYou may want to inject arbitrary values to your atom before starting some tests.\nMaybe the counter should be limited to 100. Let's see how to test that it doesn't increase after reaching 100.\nIn order to do that, simply use a [Provider](../core/provider.mdx), and export your atom to be filled-in.\n\n```tsx\nimport React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { useHydrateAtoms } from 'jotai/utils'\nimport { countAtom, Counter } from './Counter'\nimport { Provider } from 'jotai'\n\nconst HydrateAtoms = ({ initialValues, children }) => {\n  useHydrateAtoms(initialValues)\n  return children\n}\n\nconst TestProvider = ({ initialValues, children }) => (\n  <Provider>\n    <HydrateAtoms initialValues={initialValues}>{children}</HydrateAtoms>\n  </Provider>\n)\n\nconst CounterProvider = () => {\n  return (\n    <TestProvider initialValues={[[countAtom, 100]]}>\n      <Counter />\n    </TestProvider>\n  )\n}\n\ntest('should not increment on max (100)', () => {\n  render(<CounterProvider />)\n\n  const counter = screen.getByText('100')\n  const incrementButton = screen.getByText('one up')\n  await userEvent.click(incrementButton)\n  expect(counter.textContent).toEqual('100')\n})\n```\n\n### Custom hooks\n\nIf you have complex atoms, sometimes you want to test them in isolation.\n\nFor that, you can use [React Hooks Testing Library](https://github.com/testing-library/react-hooks-testing-library).\nHere's an example below:\n\n`countAtom.ts`:\n\n```ts\nimport { useAtom } from 'jotai'\nimport { atomWithReducer } from 'jotai/utils'\n\nconst reducer = (state: number, action?: 'INCREASE' | 'DECREASE') => {\n  switch (action) {\n    case 'INCREASE':\n      return state + 1\n    case 'DECREASE':\n      return state - 1\n    case undefined:\n      return state\n  }\n}\nexport const countAtom = atomWithReducer(0, reducer)\n```\n\n`countAtom.test.ts`:\n\n```ts\nimport { renderHook, act } from '@testing-library/react-hooks'\nimport { useAtom } from 'jotai'\nimport { countAtom } from './countAtom'\n\ntest('should increment counter', () => {\n  const { result } = renderHook(() => useAtom(countAtom))\n\n  act(() => {\n    result.current[1]('INCREASE')\n  })\n\n  expect(result.current[0]).toBe(1)\n})\n```\n\n### Example with React-Native\n\nOf course, you can test React-Native components too the same way, with or without `Provider`.\n\n```tsx\nimport React from 'react'\nimport { render, fireEvent } from '@testing-library/react-native'\nimport { Counter } from './counter'\n\ntest('should increment counter', () => {\n  // Arrange\n  const { getByText } = render(<Counter />)\n  const counter = getByText('0')\n  const incrementButton = getByText('one up')\n  // Act\n  fireEvent.press(incrementButton)\n  // Assert\n  expect(counter.props.children.toString()).toEqual('1')\n})\n```\n"
  },
  {
    "path": "docs/guides/typescript.mdx",
    "content": "---\ntitle: TypeScript\ndescription: How to use Jotai with TypeScript\nnav: 8.02\nkeywords: typescript,types\n---\n\n### Version requirement\n\nJotai uses TypeScript 3.8+ syntax. Upgrade your TypeScript version if you're on 3.7.5 or lower.\n\nJotai relies heavily on type inferences and requires `strictNullChecks` to be enabled. Consider adding `\"strict\": true` in your tsconfig.json.\n[#550](https://github.com/pmndrs/jotai/issues/550)\n[#802](https://github.com/pmndrs/jotai/issues/802)\n[#838](https://github.com/pmndrs/jotai/issues/838)\n\n### Notes\n\n#### Primitive atoms are basically type inferred\n\n```ts\nconst numAtom = atom(0) // primitive number atom\nconst strAtom = atom('') // primitive string atom\n```\n\n### Primitive atoms can be explicitly typed\n\n```ts\nconst numAtom = atom<number>(0)\nconst numAtom = atom<number | null>(0)\nconst arrAtom = atom<string[]>([])\n```\n\n#### Derived atoms can mostly have their types inferred\n\nIn general, this is the recommended approach since typing derived\natoms can get a little confusing, particularly for those who are\ndoing it for the first time.\n\n```ts\n\n# Read only derived atoms\nconst readOnlyAtom = atom((get) => get(numAtom))\nconst asyncReadOnlyAtom = atom(async (get) => await get(someAsyncAtom))\n\n# Write only atoms\nconst writeOnlyAtom = atom(null, (_get, set, str: string) => set(fooAtom, str))\nconst multipleArgumentsAtom = atom(\n  null,\n  (_get, set, valueOne: number, valueTwo: number) =>\n    set(fooAtom, Math.max(valueOne, valueTwo))\n);\n\n# Read/Write atoms\nconst readWriteAtom = atom(\n  (get) => get(strAtom),\n  (_get, set, num: number) => set(strAtom, String(num))\n)\nconst asyncReadWriteAtom = atom(\n  async (get) => await get(asyncStrAtom),\n  (_get, set, num: number) => set(strAtom, String(num))\n)\n```\n\n#### Derived atoms can also be explicitly typed\n\nIf you encounter a situation where you need or want to explicitly\ntype your derived atoms, you can do that as well.\n\n```ts\nconst asyncStrAtom = atom<Promise<string>>(async () => 'foo')\n\n/**\n * For write only atoms you'll need to supply three type parameters.\n * The first type parameter describes the value returned from the atom. In the following example this is `null`.\n * The second type parameter describes the arguments (plural) you will pass to the \"write\" function. Even if you only\n * plan to have one argument, this type must be an array as show in the example.\n * The third type parameter describes the return value of the \"write\" function. Normally, there is no return value,\n * which is why we use `void` in the example below.\n */\nconst writeOnlyAtom = atom<null, [string, number], void>(\n  null,\n  (_get, set, stringValue, numberValue) => set(fooAtom, stringValue),\n)\n\n/**\n * Read/Write atoms also take the same three type parameters.\n * Just for the sake of completeness, in this example, we show that the first type parameter\n * can also describe an async atom.\n */\nconst readWriteAtom = atom<Promise<string>, [number], void>(\n  async (get) => await get(asyncStrAtom),\n  (_get, set, num) => set(strAtom, String(num)),\n)\n```\n\n#### useAtom is typed based on atom types\n\n```ts\nconst [num, setNum] = useAtom(primitiveNumAtom)\nconst [num] = useAtom(readOnlyNumAtom)\nconst [, setNum] = useAtom(writeOnlyNumAtom)\n```\n\n#### Access to the value type of an atom\n\n```ts\nimport { ExtractAtomValue, useAtomValue } from 'jotai'\nimport { userAtom } from 'state'\nimport { useQuery } from '@tanstack/react-query'\n\nexport default function WriteReview(hid) {\n  const user = useAtomValue(userAtom)\n  const res = useGetReviewQuery(user)\n}\n\nfunction useGetReviewQuery(user: ExtractAtomValue<typeof userAtom>) {\n  return fetch('/api/user/' + user.id + '/review')\n}\n```\n"
  },
  {
    "path": "docs/guides/using-store-outside-react.mdx",
    "content": "---\ntitle: Using store outside React\ndescription: Using store outside React\nnav: 8.98\nkeywords: state, outside, react\npublished: false\n---\n\nJotai's state resides in React, but sometimes it would be nice\nto interact with the world outside React.\n\n## createStore\n\n[`createStore`](../core/store.mdx#createstore) provides a store interface that can be used to store your atoms. Using the store, you can access and mutate the state of your stored atoms from outside React.\n\n```jsx\nimport { atom, useAtomValue, createStore, Provider } from 'jotai'\n\nconst timeAtom = atom(0)\nconst store = createStore()\n\nstore.set(timeAtom, (prev) => prev + 1) // Update atom's value\nstore.get(timeAtom) // Read atom's value\n\nfunction Component() {\n  const time = useAtomValue(timeAtom) // Inside React\n  return (\n    <div className=\"App\">\n      <h1>{time}</h1>\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <Provider store={store}>\n      <Component />\n    </Provider>\n  )\n}\n```\n\n### Examples\n\n<Stackblitz id=\"vitejs-vite-m1h61f\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/guides/vite.mdx",
    "content": "---\ntitle: Vite\ndescription: How to use Jotai with Vite\nnav: 8.99\nkeywords: vite\npublished: false\n---\n\nYou can use the plugins from the `jotai-babel` package to enhance your developer experience when using Vite and Jotai.\n\nIn your `vite.config.ts`:\n\n```js\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport jotaiDebugLabel from 'jotai-babel/plugin-debug-label'\nimport jotaiReactRefresh from 'jotai-babel/plugin-react-refresh'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [\n    react({ babel: { plugins: [jotaiDebugLabel, jotaiReactRefresh] } }),\n  ],\n  // ... The rest of your configuration\n})\n```\n\nThere's a template below to try it yourself.\n\n### Examples\n\n#### Vite Template\n\n<Stackblitz id=\"vitejs-vite-xhhxve\" file=\"vite.config.ts\" />\n"
  },
  {
    "path": "docs/guides/waku.mdx",
    "content": "---\ntitle: Waku\ndescription: How to use Jotai with Waku\nnav: 8.04\nkeywords: waku\nstatus: draft\n---\n\n### Hydration\n\nJotai has support for hydration of atoms with `useHydrateAtoms`. The documentation for the hook can be seen [here](../utils/ssr.mdx).\n"
  },
  {
    "path": "docs/index.mdx",
    "content": "---\ntitle: Documentation\ndescription: Table of contents\nnav: 0\n---\n\nWelcome to the Jotai v2 documentation! Jotai's atomic approach to global React state management scales from a simple `useState` replacement to an enterprise application with complex requirements.\n\n## Features\n\n- Minimal core API (2kb)\n- Many utilities and extensions\n- TypeScript oriented\n- Works with Next.js, Waku, Remix, and React Native\n\n## Core\n\nJotai has a very minimal API, exposing only a few exports from the main `jotai` bundle. They are split into four categories below.\n\n<TOC section=\"core\" />\n\n## Utilities\n\nJotai also includes a `jotai/utils` bundle with a variety of extra utility functions. One example is `atomWithStorage`, which includes localStorage persistence and cross browser tab synchronization.\n\n<TOC section=\"utilities\" />\n\n## Extensions\n\nJotai has many officially maintained extensions including `atomWithQuery` for React Query and `atomWithMachine` for XState, among many others.\n\n<TOC section=\"extensions\" />\n\n## Third-party\n\nBeyond the official extensions, there are many third-party community packages as well.\n\n<TOC section=\"third-party\" />\n\n## Tools\n\nUse SWC and Babel compiler plugins for React Fast Refresh support and debugging labels. This creates the best developer experience when using a React framework such as Next.js or Waku.\n\n<TOC section=\"tools\" />\n\n## Basics\n\nLearn the basic concepts of the library, discover how it compares with others, and see usage examples.\n\n<TOC section=\"basics\" />\n\n## Guides\n\nGuides can help with use common cases such as TypeScript, React frameworks, and basic patterns.\n\n<TOC section=\"guides\" />\n\n## Recipes\n\nRecipes can help with more advanced patterns.\n\n<TOC section=\"recipes\" />\n"
  },
  {
    "path": "docs/recipes/atom-with-broadcast.mdx",
    "content": "---\ntitle: atomWithBroadcast\nnav: 9.09\nkeywords: creators,broadcast\n---\n\n> `atomWithBroadcast` creates an atom. The atom will be shared between\n> browser tabs and frames, similar to `atomWithStorage` but with the\n> initialization limitation.\n\nThis can be useful when you want states to interact with each other without the use of localStorage.\nBy using the BroadcastChannel API, you can enable basic communication between browsing contexts such as windows, tabs, frames, components, or iframes, and workers on the same origin.\nAccording to the MDN documentation, receiving a message during initialization is not supported in the BroadcastChannel, but if you want to support that functionality, you may need to add extra option to atomWithBroadcast, such as local storage.\n\n```tsx\nimport { atom, SetStateAction } from 'jotai'\n\nexport function atomWithBroadcast<Value>(key: string, initialValue: Value) {\n  const baseAtom = atom(initialValue)\n  const listeners = new Set<(event: MessageEvent<any>) => void>()\n  const channel = new BroadcastChannel(key)\n\n  channel.onmessage = (event) => {\n    listeners.forEach((l) => l(event))\n  }\n\n  const broadcastAtom = atom(\n    (get) => get(baseAtom),\n    (get, set, update: { isEvent: boolean; value: SetStateAction<Value> }) => {\n      set(baseAtom, update.value)\n\n      if (!update.isEvent) {\n        channel.postMessage(get(baseAtom))\n      }\n    },\n  )\n\n  broadcastAtom.onMount = (setAtom) => {\n    const listener = (event: MessageEvent<any>) => {\n      setAtom({ isEvent: true, value: event.data })\n    }\n\n    listeners.add(listener)\n\n    return () => {\n      listeners.delete(listener)\n    }\n  }\n\n  const returnedAtom = atom(\n    (get) => get(broadcastAtom),\n    (_get, set, update: SetStateAction<Value>) => {\n      set(broadcastAtom, { isEvent: false, value: update })\n    },\n  )\n\n  return returnedAtom\n}\n\nconst broadAtom = atomWithBroadcast('count', 0)\n\nconst ListOfThings = () => {\n  const [count, setCount] = useAtom(broadAtom)\n\n  return (\n    <div>\n      {count}\n      <button onClick={() => setCount(count + 1)}>+1</button>\n    </div>\n  )\n}\n```\n\n<Stackblitz id=\"vitejs-vite-imqbzg\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/recipes/atom-with-compare.mdx",
    "content": "---\ntitle: atomWithCompare\nnav: 9.05\nkeywords: creators,compare\n---\n\n> `atomWithCompare` creates atom that triggers updates when custom compare function `areEqual(prev, next)` is false.\n\nThis can help you avoid unwanted re-renders by ignoring state changes that don't matter to your application.\n\nNote: Jotai uses `Object.is` internally to compare values when changes occur. If `areEqual(a, b)` returns false, but `Object.is(a, b)` returns true, Jotai will not trigger an update.\n\n```ts\nimport { atomWithReducer } from 'jotai/utils'\n\nexport function atomWithCompare<Value>(\n  initialValue: Value,\n  areEqual: (prev: Value, next: Value) => boolean,\n) {\n  return atomWithReducer(initialValue, (prev: Value, next: Value) => {\n    if (areEqual(prev, next)) {\n      return prev\n    }\n\n    return next\n  })\n}\n```\n\nHere's how you'd use it to make an atom that ignores updates that are shallow-equal:\n\n```ts\nimport { atomWithCompare } from 'XXX'\nimport { shallowEquals } from 'YYY'\nimport { CSSProperties } from 'react'\n\nconst styleAtom = atomWithCompare<CSSProperties>(\n  { backgroundColor: 'blue' },\n  shallowEquals,\n)\n```\n\nIn a component:\n\n```jsx\nconst StylePreview = () => {\n  const [styles, setStyles] = useAtom(styleAtom)\n\n  return (\n    <div>\n      <div styles={styles}>Style preview</div>\n\n      {/* Clicking this button twice will only trigger one render */}\n      <button onClick={() => setStyles({ ...styles, backgroundColor: 'red' })}>\n        Set background to red\n      </button>\n\n      {/* Clicking this button twice will only trigger one render */}\n      <button onClick={() => setStyles({ ...styles, fontSize: 32 })}>\n        Enlarge font\n      </button>\n    </div>\n  )\n}\n```\n"
  },
  {
    "path": "docs/recipes/atom-with-debounce.mdx",
    "content": "---\ntitle: atomWithDebounce\nnav: 9.10\nkeywords: creators,debounce\n---\n\n> `atomWithDebounce` helps with creating an atom where state set should be debounced.\n\nThis util is useful for text search inputs, where you would like to call **functions in derived atoms only once** after waiting for a duration, instead of firing an action on every keystroke.\n\n```tsx\nimport { atom, SetStateAction } from 'jotai'\n\nexport default function atomWithDebounce<T>(\n  initialValue: T,\n  delayMilliseconds = 500,\n  shouldDebounceOnReset = false,\n) {\n  const prevTimeoutAtom = atom<ReturnType<typeof setTimeout> | undefined>(\n    undefined,\n  )\n\n  // DO NOT EXPORT currentValueAtom as using this atom to set state can cause\n  // inconsistent state between currentValueAtom and debouncedValueAtom\n  const _currentValueAtom = atom(initialValue)\n  const isDebouncingAtom = atom(false)\n\n  const debouncedValueAtom = atom(\n    initialValue,\n    (get, set, update: SetStateAction<T>) => {\n      clearTimeout(get(prevTimeoutAtom))\n\n      const prevValue = get(_currentValueAtom)\n      const nextValue =\n        typeof update === 'function'\n          ? (update as (prev: T) => T)(prevValue)\n          : update\n\n      const onDebounceStart = () => {\n        set(_currentValueAtom, nextValue)\n        set(isDebouncingAtom, true)\n      }\n\n      const onDebounceEnd = () => {\n        set(debouncedValueAtom, nextValue)\n        set(isDebouncingAtom, false)\n      }\n\n      onDebounceStart()\n\n      if (!shouldDebounceOnReset && nextValue === initialValue) {\n        onDebounceEnd()\n        return\n      }\n\n      const nextTimeoutId = setTimeout(() => {\n        onDebounceEnd()\n      }, delayMilliseconds)\n\n      // set previous timeout atom in case it needs to get cleared\n      set(prevTimeoutAtom, nextTimeoutId)\n    },\n  )\n\n  // exported atom setter to clear timeout if needed\n  const clearTimeoutAtom = atom(null, (get, set, _arg) => {\n    clearTimeout(get(prevTimeoutAtom))\n    set(isDebouncingAtom, false)\n  })\n\n  return {\n    currentValueAtom: atom((get) => get(_currentValueAtom)),\n    isDebouncingAtom,\n    clearTimeoutAtom,\n    debouncedValueAtom,\n  }\n}\n```\n\n### Caveat\n\nPlease note that this atom has different objectives from concurrent features in React 18 such as `useTransition` and `useDeferredValue` whose main aim is to prevent blocking of interaction with the page for expensive updates.\n\nFor more info, please read this github discussion https://github.com/reactwg/react-18/discussions/41 under the section titled **\"How is it different from setTimeout?\"**\n\n### Example Usage\n\nThe sandbox link below shows how we would use a derived atom to fetch state based on the value of `debouncedValueAtom`.\n\nWhen typing a pokemon's name in `<SearchInput>`, we do not send a get request on every letter, but only after `delayMilliseconds` has passed since the last text input.\n\nThis reduces the number of backend requests to the server.\n\n<Stackblitz id=\"vitejs-vite-chvvtu\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/recipes/atom-with-listeners.mdx",
    "content": "---\ntitle: atomWithListeners\nnav: 9.08\nkeywords: creators,listeners\n---\n\n> `atomWithListeners` creates an atom and a hook. The hook can be called to\n> add a new listener. The hook takes as an argument a callback, and that\n> callback is called every time the atom's value is set. The hook also\n> returns a function to remove the listener.\n\nThis can be useful when you want to create a component that can listen to when\nan atom's state changes without having to re-render that component with each of\nthose state changes.\n\n```ts\nimport { useEffect } from 'react'\nimport {\n  atom,\n  useAtom,\n  useSetAtom,\n  Getter,\n  Setter,\n  SetStateAction,\n} from 'jotai'\n\ntype Callback<Value> = (\n  get: Getter,\n  set: Setter,\n  newVal: Value,\n  prevVal: Value,\n) => void\n\nexport function atomWithListeners<Value>(initialValue: Value) {\n  const baseAtom = atom(initialValue)\n  const listenersAtom = atom<Callback<Value>[]>([])\n  const anAtom = atom(\n    (get) => get(baseAtom),\n    (get, set, arg: SetStateAction<Value>) => {\n      const prevVal = get(baseAtom)\n      set(baseAtom, arg)\n      const newVal = get(baseAtom)\n      get(listenersAtom).forEach((callback) => {\n        callback(get, set, newVal, prevVal)\n      })\n    },\n  )\n  const useListener = (callback: Callback<Value>) => {\n    const setListeners = useSetAtom(listenersAtom)\n    useEffect(() => {\n      setListeners((prev) => [...prev, callback])\n      return () =>\n        setListeners((prev) => {\n          const index = prev.indexOf(callback)\n          return [...prev.slice(0, index), ...prev.slice(index + 1)]\n        })\n    }, [setListeners, callback])\n  }\n  return [anAtom, useListener] as const\n}\n```\n\nIn a component:\n\n```jsx\nconst [countAtom, useCountListener] = atomWithListeners(0)\n\nfunction EvenCounter() {\n  const [evenCount, setEvenCount] = useAtom(countAtom)\n\n  useCountListener(\n    useCallback(\n      (get, set, newVal, prevVal) => {\n        // Every time `countAtom`'s value is set, we check if its new value\n        // is even, and if it is, we increment `evenCount`.\n        if (newVal % 2 === 0) {\n          setEvenCount((c) => c + 1)\n        }\n      },\n      [setEvenCount],\n    ),\n  )\n\n  return <>Count was set to an even number {evenCount} times.</>\n}\n```\n"
  },
  {
    "path": "docs/recipes/atom-with-refresh-and-default.mdx",
    "content": "---\ntitle: atomWithRefreshAndDefault\nnav: 9.07\nkeywords: creators,refresh,default\n---\n\n> This is for another implementation of [atomWithDefault](../utilities/resettable.mdx#atomwithdefault)\n\n### Look back to atomWithDefault behavior\n\nAs you can see in the example code in atomWithDefault section, the two atoms' relation is disconnected after updating created one, `count2Atom = atomWithDefault((get) => get(count1Atom) * 2)`.\nLet's confirm what's occurred,\n\n- 1. Click \"increment count1\", then count1 is 2 and count2 is 4\n- 2. Click \"increment count2\", then count1 is 2 and count2 is 5 (Disconnected!!)\n\nThose atoms have no relation after updating count2Atom. So,\n\n- Click \"increment count1\", count1 is incremented only\n- Even if you reset count2Atom, these dependency relation never come back\n\n### Motivation\n\nIn some cases,\n\n- After disconnecting and resetting, they should come back to their relation\n- Derived atoms should be reset based on updated the original atom\n- We'd like to reset all derived atoms but just want to operate as simply as possible\n\nHow do we make those cases?\nHere is a declarative way to create a function to provide a refreshable atom instead of atomWithDefault.\n\n```js\nconst refreshCountAtom = atom(0)\n\nconst baseDataAtom = atom(1) // original data, e.g. base count1Atom\nconst dataAtom = atom(\n  (get) => {\n    get(refreshCountAtom) // it's introduced at atomWithRefresh\n    return get(baseDataAtom)\n  },\n  (get, set, update) => {\n    set(baseDataAtom, update)\n  },\n)\n\nconst atomWithRefreshAndDefault = (refreshAtom, getDefault) => {\n  const overwrittenAtom = atom(null)\n  return atom(\n    (get) => {\n      const lastState = get(overwrittenAtom)\n      if (lastState && lastState.refresh === get(refreshAtom)) {\n        return lastState.value\n      }\n      return getDefault(get)\n    },\n    (get, set, update) => {\n      set(overwrittenAtom, { refresh: get(refreshAtom), value: update })\n    },\n  )\n}\n\n// This is an alternative of `atomWithDefault((get) => get(count1Atom) * 2)`\nconst refreshableAtom = atomWithRefreshAndDefault(\n  refreshCountAtom,\n  (get) => get(dataAtom) * 2,\n)\n\n// You can reset by updating just one atom\nconst resetRootAtom = atom(null, (get, set) => {\n  set(refreshCountAtom, get(refreshCountAtom) + 1)\n})\n```\n\n<Stackblitz id=\"vitejs-vite-1m7ce3\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/recipes/atom-with-refresh.mdx",
    "content": "---\ntitle: atomWithRefresh\nnav: 9.06\nkeywords: creators,refresh\n---\n\n`atomWithRefresh` has been provided by `jotai/utils` since v2.7.0.\n[Jump to the doc](../utilities/resettable.mdx#atomwithrefresh)\n"
  },
  {
    "path": "docs/recipes/atom-with-toggle-and-storage.mdx",
    "content": "---\ntitle: atomWithToggleAndStorage\nnav: 9.05\nkeywords: creators,storage\n---\n\n> `atomWithToggleAndStorage` is like `atomWithToggle` but also persist the state anytime it changes in given storage using [`atomWithStorage`](../utilities/storage.mdx).\n\nHere is the source:\n\n```ts\nimport { WritableAtom, atom } from 'jotai'\nimport { atomWithStorage } from 'jotai/utils'\n\nexport function atomWithToggleAndStorage(\n  key: string,\n  initialValue?: boolean,\n  storage?: any,\n): WritableAtom<boolean, [boolean?], void> {\n  const anAtom = atomWithStorage(key, initialValue, storage)\n  const derivedAtom = atom(\n    (get) => get(anAtom),\n    (get, set, nextValue?: boolean) => {\n      const update = nextValue ?? !get(anAtom)\n      void set(anAtom, update)\n    },\n  )\n\n  return derivedAtom as WritableAtom<boolean, [boolean?], void>\n}\n```\n\nAnd how it's used:\n\n```js\nimport { atomWithToggleAndStorage } from 'XXX'\n\n// will have an initial value set to false & get stored in localStorage under the key \"isActive\"\nconst isActiveAtom = atomWithToggleAndStorage('isActive')\n```\n\nThe usage in a component is also the same as `atomWithToggle`.\n"
  },
  {
    "path": "docs/recipes/atom-with-toggle.mdx",
    "content": "---\ntitle: atomWithToggle\nnav: 9.04\nkeywords: creators,toggle\n---\n\n> `atomWithToggle` creates a new atom with a boolean as initial state & a setter function to toggle it.\n\nThis avoids the boilerplate of having to set up another atom just to update the state of the first.\n\n```ts\nimport { WritableAtom, atom } from 'jotai'\n\nexport function atomWithToggle(\n  initialValue?: boolean,\n): WritableAtom<boolean, [boolean?], void> {\n  const anAtom = atom(initialValue, (get, set, nextValue?: boolean) => {\n    const update = nextValue ?? !get(anAtom)\n    set(anAtom, update)\n  })\n\n  return anAtom as WritableAtom<boolean, [boolean?], void>\n}\n```\n\nAn optional initial state can be provided as the first argument.\n\nThe setter function can have an optional argument to force a particular state, such as if you want to make a setActive function out of it.\n\nHere is how it's used.\n\n```js\nimport { atomWithToggle } from 'XXX'\n\n// will have an initial value set to true\nconst isActiveAtom = atomWithToggle(true)\n```\n\nAnd in a component:\n\n```jsx\nconst Toggle = () => {\n  const [isActive, toggle] = useAtom(isActiveAtom)\n\n  return (\n    <>\n      <button onClick={() => toggle()}>\n        isActive: {isActive ? 'yes' : 'no'}\n      </button>\n      <button onClick={() => toggle(true)}>force true</button>\n      <button onClick={() => toggle(false)}>force false</button>\n    </>\n  )\n}\n```\n"
  },
  {
    "path": "docs/recipes/custom-useatom-hooks.mdx",
    "content": "---\ntitle: Custom useAtom hooks\nnav: 9.02\nkeywords: custom,hook\n---\n\nThis page shows the ways of creating different utility functions. Utility functions save your time on coding, and you can preserve your base atom for other usage.\n\n### utils\n\n#### useSelectAtom\n\n```js\nimport { useAtomValue } from 'jotai'\nimport { selectAtom } from 'jotai/utils'\n\nexport function useSelectAtom(anAtom, selector) {\n  const selectorAtom = selectAtom(\n    anAtom,\n    selector,\n    // Alternatively, you can customize `equalityFn` to determine when it will rerender\n    // Check selectAtom's signature for details.\n  )\n  return useAtomValue(selectorAtom)\n}\n\n// how to use it\nfunction useN(n) {\n  const selector = useCallback((v) => v[n], [n])\n  return useSelectAtom(arrayAtom, selector)\n}\n```\n\nPlease note that in this case `keyFn` must be stable, either define outside render or wrap with `useCallback`.\n\n#### useFreezeAtom\n\n```js\nimport { useAtom } from 'jotai'\nimport { freezeAtom } from 'jotai/utils'\n\nexport function useFreezeAtom(anAtom) {\n  return useAtom(freezeAtom(anAtom))\n}\n```\n\n#### useSplitAtom\n\n```js\nimport { useAtom } from 'jotai'\nimport { splitAtom } from 'jotai/utils'\n\nexport function useSplitAtom(anAtom) {\n  return useAtom(splitAtom(anAtom))\n}\n```\n\n### extensions\n\n#### useFocusAtom\n\n```js\nimport { useAtom } from 'jotai'\nimport { focusAtom } from 'jotai-optics'\n\n/* if an atom is created here, please use `useMemo(() => atom(initValue), [initValue])` instead. */\nexport function useFocusAtom(anAtom, keyFn) {\n    return useAtom(focusAtom(anAtom, keyFn))\n}\n\n// how to use it\nuseFocusAtom(anAtom) {\n    useMemo(() => atom(initValue), [initValue]),\n    useCallback((optic) => optic.prop('key'), [])\n}\n```\n\n#### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-ge1mah\" file=\"src%2FApp.tsx\" />\n\nPlease note that in this case `keyFn` must be stable, either define outside render or wrap with `useCallback`.\n"
  },
  {
    "path": "docs/recipes/large-objects.mdx",
    "content": "---\ntitle: Large objects\nnav: 9.01\nkeywords: large,object\n---\n\n> The examples and descriptions below are based on this [codesandbox](https://codesandbox.io/s/zealous-sun-f2qnl?file=/src/App.tsx), so it will give you a better understanding if you check it out along with these examples.\n\nSometimes we have nested data we need to store in atoms, and we may need to change that data at different levels, or we need to use part of that data without listening to all changes.\n\nConsider this example:\n\n```js\nconst initialData = {\n  people: [\n    {\n      name: 'Luke Skywalker',\n      information: { height: 172 },\n      siblings: ['John Skywalker', 'Doe Skywalker'],\n    },\n    {\n      name: 'C-3PO',\n      information: { height: 167 },\n      siblings: ['John Doe', 'Doe John'],\n    },\n  ],\n  films: [\n    {\n      title: 'A New Hope',\n      planets: ['Tatooine', 'Alderaan'],\n    },\n    {\n      title: 'The Empire Strikes Back',\n      planets: ['Hoth'],\n    },\n  ],\n  info: {\n    tags: ['People', 'Films', 'Planets', 'Titles'],\n  },\n}\n```\n\n## focusAtom\n\n> `focusAtom` creates a new atom, based on the focus that you pass to it. [jotai-optics](../extensions/optics.mdx#focusatom)\n\nWe use this utility to focus an atom and create an atom from a specific part of the data. For example we may need to consume the people property of the above data, Here's how we do it:\n\n```js\nimport { atom } from 'jotai'\nimport { focusAtom } from 'jotai-optics'\n\nconst dataAtom = atom(initialData)\n\nconst peopleAtom = focusAtom(dataAtom, (optic) => optic.prop('people'))\n```\n\n`focusAtom` returns `WritableAtom` which means it's possible to change the `peopleAtom` data.\n\nIf we change the `films` property of the above data example, the `peopleAtom` won't cause a re-render, so that's one of the benefits of using `focusAtom`.\n\n## splitAtom\n\n> The `splitAtom` utility is useful when you want to get an atom for each element in a list. [jotai/utils](../utilities/split.mdx)\n\nWe use this utility for atoms that return arrays as their values. For example, the `peopleAtom` we made above returns the people property array, so we can return an atom for each item of that array. If the array atom is writable, `splitAtom` returned atoms are going to be writable, if the array atom is read-only, the returned atoms will be read-only too.\n\n```js\nimport { splitAtom } from 'jotai/utils'\n\nconst peopleAtomsAtom = splitAtom(peopleAtom)\n```\n\nAnd this is how we use it in components.\n\n```jsx\nconst People = () => {\n  const [peopleAtoms] = useAtom(peopleAtomsAtom)\n  return (\n    <div>\n      {peopleAtoms.map((personAtom) => (\n        <Person personAtom={personAtom} key={`${personAtom}`} />\n      ))}\n    </div>\n  )\n}\n```\n\n## selectAtom\n\n> This function creates a derived atom whose value is a function of the original atom's value. [jotai/utils](../utilities/select.mdx)\n\nThis utility is like `focusAtom`, but it always returns a read-only atom.\n\nAssume we want to consume the info data, and its data is always unchangeable. We can make a read-only atom from it and select that created atom.\n\n```js\n// first we create a derived atom based on initialData.info\nconst infoAtom = atom((get) => get(dataAtom).info)\n```\n\nThen we use it in our component:\n\n```jsx\nimport { atom, useAtom } from 'jotai'\nimport { selectAtom, splitAtom } from 'jotai/utils'\n\nconst tagsSelector = (s) => s.tags\nconst Tags = () => {\n  const tagsAtom = selectAtom(infoAtom, tagsSelector)\n  const tagsAtomsAtom = splitAtom(tagsAtom)\n  const [tagAtoms] = useAtom(tagsAtomsAtom)\n  return (\n    <div>\n      {tagAtoms.map((tagAtom) => (\n        <Tag key={`${tagAtom}`} tagAtom={tagAtom} />\n      ))}\n    </div>\n  )\n}\n```\n"
  },
  {
    "path": "docs/recipes/use-atom-effect.mdx",
    "content": "---\ntitle: useAtomEffect\nnav: 9.03\nkeywords: effect, atom effect, side effect, side-effect, sideeffect, hook, useAtomEffect\n---\n\n> `useAtomEffect` runs side effects in response to changes in atoms or props using [atomEffect](../extensions/effect.mdx).\n\nThe effectFn reruns whenever the atoms it depends on change or the effectFn itself changes. Be sure to memoize the effectFn if it's a function defined in the component.\n\n⚠️ Note: always prefer to use a [stable version of useMemo and useCallback](https://github.com/alexreardon/use-memo-one) to avoid extra atomEffect recomputations. You may rely on useMemo as a performance optimization, but not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components.\n\n```ts\nimport { useMemoOne as useStableMemo } from 'use-memo-one'\nimport { useAtomValue } from 'jotai/react'\nimport { atomEffect } from 'jotai-effect'\n\ntype EffectFn = Parameters<typeof atomEffect>[0]\n\nexport function useAtomEffect(effectFn: EffectFn) {\n  useAtomValue(useStableMemo(() => atomEffect(effectFn), [effectFn]))\n}\n```\n\n### Example Usage\n\n```tsx\nimport { useCallbackOne as useStableCallback } from 'use-memo-one'\nimport { atom, useAtom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\nimport { useAtomEffect } from './useAtomEffect'\n\nconst channelSubscriptionAtomFamily = atomFamily<Channel>(\n  (channelId: string) => {\n    return atom(new Channel(channelId))\n  },\n)\nconst messagesAtom = atom<Message[]>([])\n\nfunction Messages({ channelId }: { channelId: string }) {\n  const [messages] = useAtom(messagesAtom)\n  useAtomEffect(\n    useStableCallback(\n      (get, set) => {\n        const channel = get(channelSubscriptionAtomFamily(channelId))\n        const unsubscribe = channel.subscribe((message) => {\n          set(messagesAtom, (prev) => [...prev, message])\n        })\n        return unsubscribe\n      },\n      [channelId],\n    ),\n  )\n  return (\n    <>\n      <h1>You have {messages.length} messages</h1>\n      <hr />\n      {messages.map((message) => (\n        <div key={message.id}>{message.text}</div>\n      ))}\n    </>\n  )\n}\n```\n"
  },
  {
    "path": "docs/recipes/use-reducer-atom.mdx",
    "content": "---\ntitle: useReducerAtom\nnav: 9.11\nkeywords: reducer, hook, useReducerAtom\n---\n\n`useReducerAtom` is a custom hook to apply a reducer to a primitive atom.\n\nIt's useful to change the update behavior temporarily.\nAlso, consider [atomWithReducer](../utilities/reducer.mdx)\nfor the atom-level solution.\n\n```ts\nimport { useCallback } from 'react'\nimport { useAtom } from 'jotai'\nimport type { PrimitiveAtom } from 'jotai'\n\nexport function useReducerAtom<Value, Action>(\n  anAtom: PrimitiveAtom<Value>,\n  reducer: (v: Value, a: Action) => Value,\n) {\n  const [state, setState] = useAtom(anAtom)\n  const dispatch = useCallback(\n    (action: Action) => setState((prev) => reducer(prev, action)),\n    [setState, reducer],\n  )\n  return [state, dispatch] as const\n}\n```\n\n### Example Usage\n\n```jsx\nimport { atom } from 'jotai'\n\nconst countReducer = (prev, action) => {\n  if (action.type === 'inc') return prev + 1\n  if (action.type === 'dec') return prev - 1\n  throw new Error('unknown action type')\n}\n\nconst countAtom = atom(0)\n\nconst Counter = () => {\n  const [count, dispatch] = useReducerAtom(countAtom, countReducer)\n  return (\n    <div>\n      {count}\n      <button onClick={() => dispatch({ type: 'inc' })}>+1</button>\n      <button onClick={() => dispatch({ type: 'dec' })}>-1</button>\n    </div>\n  )\n}\n```\n\n<Stackblitz id=\"vitejs-vite-gsctj6\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/third-party/bunja.mdx",
    "content": "---\ntitle: Bunja\ndescription: State Lifetime Manager\nnav: 5.03\nkeywords: scope,di,raii,lifetime\n---\n\n[Bunja](https://github.com/disjukr/bunja) is lightweight State Lifetime Manager.\n\nIt provides an RAII wrapper for jōtai atoms.\n\n---\n\nSee also:\n\n- [Bunja README](https://github.com/disjukr/bunja/blob/main/README.md)\n- [Presentations](https://github.com/disjukr/bunja/tree/main/presentations)\n\n## install\n\n```\nnpm install bunja\n```\n\n### Defining a Bunja\n\nYou can define a bunja using the `bunja` function.\n\nWhen you access the defined bunja with the `useBunja` hook, a bunja instance is created.\n\nIf all components in the render tree that refer to the bunja disappear, the bunja instance is automatically destroyed.\n\nIf you want to trigger effects when the lifetime of a bunja starts and ends, you can use the `bunja.effect` function.\n\n```ts\nimport { bunja } from 'bunja'\nimport { useBunja } from 'bunja/react'\n\nconst countBunja = bunja(() => {\n  const countAtom = atom(0)\n\n  bunja.effect(() => {\n    console.log('mounted')\n    return () => console.log('unmounted')\n  })\n\n  return { countAtom }\n})\n\nfunction MyComponent() {\n  const { countAtom } = useBunja(countBunja)\n  const [count, setCount] = useAtom(countAtom)\n  // Your component logic here\n}\n```\n\n### Defining a Bunja that relies on other Bunja\n\nIf you want to manage a state with a broad lifetime and another state with a narrower lifetime, you can create a (narrower) bunja that depends on a (broader) bunja.\n\nFor example, you can think of a bunja that holds the page state and another bunja that holds the modal state.\n\nThe page state lives longer than the modal state, and the modal state should exist from the moment the modal opens until it closes.\n\nIn such a case, you can write the following code.\n\n```tsx\nconst pageBunja = bunja(() => {\n  const pageStateAtom = atom({})\n  return { pageStateAtom }\n})\n\nconst childBunja = bunja(() => {\n  const { pageStateAtom } = bunja.use(pageBunja)\n  const childStateAtom = atom((get) => ({\n    ...get(pageStateAtom),\n    child: 'state',\n  }))\n  return { childStateAtom }\n})\n\nconst modalBunja = bunja(() => {\n  const { pageStateAtom } = bunja.use(pageBunja)\n  const modalStateAtom = atom((get) => ({\n    ...get(pageStateAtom),\n    modal: 'state',\n  }))\n\n  bunja.effect(() => {\n    console.log('modal opened')\n    return () => console.log('modal closed')\n  })\n\n  return { modalStateAtom }\n})\n\nfunction Page() {\n  const [modalOpen, setModalOpen] = useState(false)\n  return (\n    <>\n      <Child />\n      {modalOpen && <Modal />}\n    </>\n  )\n}\n\nfunction Child() {\n  const { childStateAtom } = useBunja(childBunja)\n  const childState = useAtomValue(childStateAtom)\n  // ...\n}\n\nfunction Modal() {\n  const { modalStateAtom } = useBunja(modalBunja)\n  const modalState = useAtomValue(modalStateAtom)\n  // ...\n}\n```\n\nNotice that `pageBunja` is not directly `useBunja`-ed.\n\nWhen you `useBunja` either `childBunja` or `modalBunja`, since they depend on `pageBunja`, it has the same effect as if `pageBunja` were also `useBunja`-ed.\n\nWhen the modal is unmounted, there are no longer any places using `useBunja(modalBunja)`, so the instance of `modalBunja` is automatically destroyed.\n\n### Dependency injection using Scope\n\nYou can use a bunja for local state management.\n\nWhen you specify a scope as a dependency of the bunja, separate bunja instances are created based on the values injected into the scope.\n\n```ts\nimport { bunja, createScope } from 'bunja'\n\nconst UrlScope = createScope()\n\nconst fetchBunja = bunja(() => {\n  const url = bunja.use(UrlScope)\n\n  const queryAtom = atomWithQuery((get) => ({\n    queryKey: [url],\n    queryFn: async () => (await fetch(url)).json(),\n  }))\n\n  return { queryAtom }\n})\n```\n\n#### Injecting dependencies via React context\n\nIf you bind a scope to a React context, bunjas that depend on the scope can retrieve values from the corresponding React context.\n\nIn the example below, there are two React instances (`<ChildComponent />`) that reference the same `fetchBunja`, but since each looks at a different context value, two separate bunja instances are also created.\n\n```tsx\nimport { createContext } from 'react'\nimport { bunja, createScope } from 'bunja'\nimport { bindScope } from 'bunja/react'\n\nconst UrlContext = createContext('https://example.com/')\nconst UrlScope = createScope()\nbindScope(UrlScope, UrlContext)\n\nconst fetchBunja = bunja(() => {\n  const url = bunja.use(UrlScope)\n\n  const queryAtom = atomWithQuery((get) => ({\n    queryKey: [url],\n    queryFn: async () => (await fetch(url)).json(),\n  }))\n\n  return { queryAtom }\n})\n\nfunction ParentComponent() {\n  return (\n    <>\n      <UrlContext value=\"https://example.com/foo\">\n        <ChildComponent />\n      </UrlContext>\n      <UrlContext value=\"https://example.com/bar\">\n        <ChildComponent />\n      </UrlContext>\n    </>\n  )\n}\n\nfunction ChildComponent() {\n  const { queryAtom } = useBunja(fetchBunja)\n  const { data, isPending, isError } = useAtomValue(queryAtom)\n  // Your component logic here\n}\n```\n\nYou can use the `createScopeFromContext` function to handle both the creation of the scope and the binding to the context in one step.\n\n```ts\nimport { createContext } from 'react'\nimport { createScopeFromContext } from 'bunja/react'\n\nconst UrlContext = createContext('https://example.com/')\nconst UrlScope = createScopeFromContext(UrlContext)\n```\n\n#### Injecting dependencies directly into the scope\n\nYou might want to use a bunja directly within a React component where the values to be injected into the scope are created.\n\nIn such cases, you can use the second parameter of `useBunja` hook to inject values into the scope without wrapping the context separately.\n\n```tsx\nfunction MyComponent() {\n  const { queryAtom } = useBunja(fetchBunja, [\n    UrlScope.bind('https://example.com/'),\n  ])\n  const { data, isPending, isError } = useAtomValue(queryAtom)\n  // Your component logic here\n}\n```\n\n##### Doing the same thing inside a bunja\n\nYou can use `bunja.fork` to inject scope values from within a bunja initialization function.\n\n```ts\nconst myBunja = bunja(() => {\n  const fooData = bunja.fork(fetchBunja, [\n    UrlScope.bind('https://example.com/foo'),\n  ])\n  const barData = bunja.fork(fetchBunja, [\n    UrlScope.bind('https://example.com/bar'),\n  ])\n\n  return { fooData, barData }\n})\n```\n"
  },
  {
    "path": "docs/third-party/derive.mdx",
    "content": "---\ntitle: Derive\ndescription: This doc describes Derive extension.\nnav: 5.01\nkeywords: derive,derived,async,zalgo,suspense,promise,react\n---\n\n#### When is this useful?\n\n- When local updates to the cache cause micro-suspensions\n- When unnecessary recomputation causes performance issues\n\nJōtai offers powerful primitives for working with asynchronous data outside of the web framework (e.g. React), and allows the UI and business logic to properly integrate with the data layer. Many data-fetching integrations offer a peek into the client-side cache via atoms. When the cache is not yet populated, the atom has to resolve to a Promise of the value. However, if the value already exists in cache, and we do an optimistic update, then the value can be made available downstream immediately.\n\nBuilding data graphs with these dual-natured (sometimes async, sometimes sync) atoms as a base can lead to unnecessary rerenders, stale values and micro-suspensions (in case of React) if not handled with care.\n\n**jotai-derive** provides a primitive for building asynchronous data graphs that act on values as soon as they are available (either awaiting for them, or acting on them synchronously).\n\n### Install\n\nYou have to install `jotai-derive` to use this feature.\n\n```\nnpm install jotai-derive\n```\n\n## derive\n\nLets say we have a _dual-natured_ atom, meaning that sometimes we are yet to\nknow the value (e.g. fetching the data), but other times we update the atom\nlocally and can know its value immediately (e.g. optimistic updates).\n\n```ts\n// `jotai-derive` is applicable to most data-fetching solutions, not just `jotai-apollo`\nimport { atomWithQuery } from 'jotai-apollo';\n\n// An example of a dual-natured atom\nconst userAtom: Atom<User | Promise<User>> =\n  atomWithQuery(...);\n\n```\n\nBelow is how a derived atom is usually created. The drawback is that always awaiting (even though the value can be known)\nintroduces unnecessary deferring and recomputation.\n\n```ts\n// Without `jotai-derive`\n\nimport { atom } from 'jotai'\n\n// Type is Atom<Promise<string>>, even though\n// get(userAtom) does not always return a promise,\n// meaning we could compute `uppercaseNameAtom`\n// synchronously.\nconst uppercaseNameAtom = atom(async (get) => {\n  const user = await get(userAtom)\n  return user.name.toUppercase()\n})\n```\n\nHere is how `jotai-derive` is used to create a tighter async data-processing pipeline.\n\n```ts\n// With `jotai-derive`\n\nimport { derive } from 'jotai-derive'\n\n// Atom<string | Promise<string>>\nconst uppercaseNameAtom = derive(\n  [userAtom], // will be awaited only when necessary\n  (user) => user.name.toUppercase(),\n)\n```\n\n### Multiple async dependencies\n\nTo derive a value from multiple atoms, the array accepts more than one atom. The corresponding values are then passed\ninto the producer function in the same order.\n\n```ts\nimport { derive } from 'jotai-derive'\n\n// Atom<string | Promise<string>>\nconst welcomeMessageAtom = derive(\n  [userAtom, serverNameAtom],\n  (user, serverName) => `Welcome ${user.name} to ${serverName}!`,\n)\n```\n\n## soon\n\nFor more advances usage, for example **conditional dependencies**, the `soon` and `soonAll` functions\ncan be used instead (`derive` is a utility wrapper around them).\n\n### Conditional dependency\n\n```ts\n// pipes allow for cleaner code when using `soon` directly.\nimport { pipe } from 'remeda';\nimport { soon } from 'jotai-derive';\n\n// Atom<RestrictedItem | Promise<RestrictedItem>>\nconst queryAtom = ...;\n\n// Atom<boolean | Promise<boolean>>\nconst isAdminAtom = ...;\n\n// Atom<null | RestrictedItem | Promise<null | RestrictedItem>>\nconst restrictedItemAtom = atom((get) =>\n  pipe(\n    get(isAdminAtom),\n    soon((isAdmin) => (isAdmin ? get(queryAtom) : null))\n  )\n);\n```\n\n### Conditional dependency (multiple conditions)\n\n```ts\n// pipes allow for cleaner code when using `soon` directly.\nimport { pipe } from 'remeda';\nimport { soon, soonAll } from 'jotai-derive';\n\n// Atom<RestrictedItem | Promise<RestrictedItem>>\nconst queryAtom = ...;\n\n// Atom<boolean | Promise<boolean>>\nconst isAdminAtom = ...;\n\n// Atom<boolean | Promise<boolean>>\nconst enabledAtom = ...;\n\n// Atom<null | RestrictedItem | Promise<null | RestrictedItem>>\nconst restrictedItemAtom = atom((get) =>\n  pipe(\n    soonAll(get(isAdminAtom), get(enabledAtom)),\n    soon(([isAdmin, enabled]) => (isAdmin && enabled ? get(queryAtom) : null))\n  )\n);\n\n```\n\n## Demo\n\n<CodeSandbox id=\"jotai-derive-example-7422pk\" />\n"
  },
  {
    "path": "docs/third-party/history.mdx",
    "content": "---\ntitle: History\ndescription: A Jōtai utility package for state history\nnav: 4.04\nkeywords: history, undo, redo, track changes\n---\n\n[jotai-history](https://github.com/jotaijs/jotai-history) is a utility package for tracking state history in Jotai.\n\n## Installation\n\n```\nnpm install jotai-history\n```\n\n## `withHistory`\n\n```js\nimport { withHistory } from 'jotai-history'\n\nconst targetAtom = atom(0)\nconst limit = 2\nconst historyAtom = withHistory(targetAtom, limit)\n\nfunction Component() {\n  const [current, previous] = useAtomValue(historyAtom)\n  ...\n}\n```\n\n### Description\n\n`withHistory` creates an atom that tracks the history of states for a given `targetAtom`. The most recent `limit` states are retained.\n\n### Action Symbols\n\n- **RESET**  \n  Clears the entire history, removing all previous states (including the undo/redo stack).\n\n  ```js\n  import { RESET } from 'jotai-history'\n\n  ...\n\n  function Component() {\n    const setHistoryAtom = useSetAtom(historyAtom)\n    ...\n    setHistoryAtom(RESET)\n  }\n  ```\n\n- **UNDO** and **REDO**  \n  Moves the `targetAtom` backward or forward in its history.\n\n  ```js\n  import { REDO, UNDO } from 'jotai-history'\n\n  ...\n\n  function Component() {\n    const setHistoryAtom = useSetAtom(historyAtom)\n    ...\n    setHistoryAtom(UNDO)\n    setHistoryAtom(REDO)\n  }\n  ```\n\n### Indicators\n\n- **canUndo** and **canRedo**  \n  Booleans indicating whether undo or redo actions are currently possible. These can be used to disable buttons or conditionally trigger actions.\n\n  ```jsx\n  ...\n\n  function Component() {\n    const history = useAtomValue(historyAtom)\n\n    return (\n      <>\n        <button disabled={!history.canUndo}>Undo</button>\n        <button disabled={!history.canRedo}>Redo</button>\n      </>\n    )\n  }\n  ```\n\n<CodeSandbox id=\"g6qj3q\" />\n\n## Memory Management\n\n> Because `withHistory` maintains a list of previous states, be mindful of memory usage by setting a reasonable `limit`. Applications that update state frequently can grow significantly in memory usage.\n"
  },
  {
    "path": "docs/tools/babel.mdx",
    "content": "---\ntitle: Babel\ndescription: This doc describes the `jotai-babel` package.\nnav: 6.02\nkeywords: babel,gatsby,fast,refresh\n---\n\n> **Deprecated**: `jotai/babel` bundle is deprecated and will be removed in v3. Please use the [`jotai-babel`](https://github.com/jotaijs/jotai-babel) package instead.\n\n## jotai-babel/plugin-react-refresh\n\nThis plugin adds support for React Refresh for Jotai atoms. This makes sure that state isn't reset, when developing using React Refresh.\n\n### Usage\n\nWith a `babel` configuration file:\n\n```json\n{\n  \"plugins\": [\"jotai-babel/plugin-react-refresh\"]\n}\n```\n\nThe plugin recognizes a predefined list of atom function names (e.g. 'atom', 'atomFamily', 'atomWithDefault'). If you're using custom atom function names, you can explicitly supply them to ensure they are recognized.\n\nHere's how you can configure it in your Babel setup:\n\n```json\n{\n  \"plugins\": [\n    [\"jotai-babel/plugin-react-refresh\", { \"customAtomNames\": [\"customAtom\"] }]\n  ]\n}\n```\n\nExamples can be found below.\n\n## jotai-babel/plugin-debug-label\n\nJotai is based on object references and not keys (like Recoil). This means there's no identifier for atoms. To identify atoms, it's possible to add a `debugLabel` to an atom, which can be found in React devtools.\n\nHowever, this can quickly become cumbersome to add a `debugLabel` to every atom.\n\nThis `babel` plugin adds a `debugLabel` to every atom, based on its identifier.\n\nThe plugin transforms this code:\n\n```js\nexport const countAtom = atom(0)\n```\n\nInto:\n\n```js\nexport const countAtom = atom(0)\ncountAtom.debugLabel = 'countAtom'\n```\n\nDefault exports are also handled, based on the file naming:\n\n```js\n// countAtom.ts\nexport default atom(0)\n```\n\nWhich transform into:\n\n```js\n// countAtom.ts\nconst countAtom = atom(0)\ncountAtom.debugLabel = 'countAtom'\nexport default countAtom\n```\n\n### Usage\n\nIt is recommended to disable this plugin for production builds to avoid unnecessary overhead. You can conditionally include it in your Babel configuration based on the environment.\n\nWith a `babel` configuration file:\n\n```json\n{\n  \"plugins\": [\"jotai-babel/plugin-debug-label\"]\n}\n```\n\nThe plugin recognizes a predefined list of atom function names (e.g. 'atom', 'atomFamily', 'atomWithDefault'). If you're using custom atom function names, you can explicitly supply them to ensure they are recognized.\n\nHere's how you can configure it in your Babel setup:\n\n```json\n{\n  \"plugins\": [\n    [\"jotai-babel/plugin-debug-label\", { \"customAtomNames\": [\"customAtom\"] }]\n  ]\n}\n```\n\nExamples can be found below.\n\n## jotai-babel/preset\n\nJotai ships with a `babel preset` containing the plugins created for Jotai.\n\n### Usage\n\nWith a `babel` configuration file:\n\n```json\n{\n  \"presets\": [\"jotai-babel/preset\"]\n}\n```\n\nYou can also supply your atom names to the preset:\n\n```json\n{\n  \"presets\": [[\"jotai-babel/preset\", { \"customAtomNames\": [\"customAtom\"] }]]\n}\n```\n\n### Examples\n\n#### Next.js\n\n<CodeSandbox id=\"o2di2\" />\n\n#### Parcel\n\n<CodeSandbox id=\"bgf3b\" />\n"
  },
  {
    "path": "docs/tools/devtools.mdx",
    "content": "---\ntitle: Devtools\ndescription: This doc describes Devtools for Jotai.\nnav: 6.03\nkeywords: devtools,debug,snapshot\n---\n\n### Install\n\nInstall `jotai-devtools` to your project to get started.\n\n```sh\nnpm install jotai-devtools\n```\n\n### Notes\n\n- `<DevTools/>` is optimized to be tree-shakable for production builds, and **only works in a non-production environment**\n- Hooks are dev-only, and are designed to work in a non-production environment\n- Feedback welcome, please file issues or ask questions on the [Jotai DevTools GitHub Repo](https://github.com/jotaijs/jotai-devtools/discussions)\n\n### Quick links\n\n- [UI Devtools](#ui-devtools)\n- Hooks\n  - [useAtomsDebugValue](#useatomsdebugvalue)\n  - [useAtomDevtools](#useatomdevtools)\n  - [useAtomsDevtools](#useatomsdevtools)\n  - [useAtomsSnapshot](#useatomssnapshot)\n  - [useGotoAtomsSnapshot](#usegotoatomssnapshot)\n- Migration guides\n  - [Migrate from `@emotion/react`](https://github.com/jotaijs/jotai-devtools?tab=readme-ov-file#migrate-%C6%92rom-emotionreact-to-native-css)\n\n### UI DevTools\n\nEnhance your development experience with the UI based Jotai DevTool.\n\n#### Babel plugin setup - (_Optional but highly recommended_)\n\nUse [`jotai-babel`](https://github.com/jotaijs/jotai-babel) plugins for optimal debugging experience. Find the complete guide on the [babel](../tools/babel) page and/or [swc](../tools/swc) page.\n\n```\nnpm install -D jotai-babel\n```\n\n```ts\n{\n  \"presets\": [\n    // The preset includes two plugins:\n    // - jotai-babel/plugin-react-refresh to enable hot reload for atoms\n    // - jotai-babel/plugin-debug-label to automatically adds debug labels to atoms\n    'jotai-babel/preset'\n  ]\n}\n```\n\nExample for Vite + React project:\n\n```ts\n// vite.config.ts\n\nexport default defineConfig({\n  plugins: [\n    react({\n      babel: {\n        presets: ['jotai-babel/preset'],\n      },\n    }),\n  ],\n})\n```\n\nIf you are using Vite 8, first install these additional packages:\n\n```\nnpm install -D @rolldown/plugin-babel @babel/core\n```\n\nThen use the following example instead:\n\n```\n// vite.config.ts\nimport babel from '@rolldown/plugin-babel';\n\nexport default defineConfig({\n  plugins: [\n    react(),\n    babel({\n      presets: ['jotai-babel/preset'],\n    })\n  ],\n})\n```\n\n### Next JS setup\n\n_You may skip this section if you're not using [Next.js](https://nextjs.org)._\n\nEnable `transpilePackages` for the UI CSS and components to be transpiled correctly.\n\n```ts\n// next.config.ts\nconst nextConfig = {\n  // Learn more here - https://nextjs.org/docs/advanced-features/compiler#module-transpilation\n  // Required for UI css to be transpiled correctly 👇\n  transpilePackages: ['jotai-devtools'],\n}\nmodule.exports = nextConfig\n```\n\n### Available props\n\n```ts\ntype DevToolsProps = {\n  // defaults to false\n  isInitialOpen?: boolean\n  // pass a custom store\n  store?: Store\n  // Defaults to light\n  theme?: 'dark' | 'light'\n  // Set the position of the trigger button\n  // Defaults to `bottom-left`\n  position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'\n  // Custom nonce to allowlist jotai-devtools specific inline styles via CSP\n  nonce?: string\n  options?: {\n    // Private atoms are used internally in atom creators like `atomWithStorage`\n    // or `atomWithLocation`, etc. to manage the internal state.\n    // Defaults to `false`\n    shouldShowPrivateAtoms?: boolean\n    // Expands the JSON tree view on initial render on Atom Viewer tab, Timeline tab, etc.\n    // Defaults to `false`\n    shouldExpandJsonTreeViewInitially?: boolean\n    // The interval (in milliseconds) between each step of the time travel playback.\n    // Defaults to `750ms`\n    timeTravelPlaybackInterval?: number\n    // The maximum number of snapshots to keep in the history.\n    // The higher the number the more memory it will consume.\n    // Defaults to `Infinity`. Recommended: `~30`\n    snapshotHistoryLimit?: number\n  }\n}\n```\n\n### Usage\n\n#### Provider-less\n\n```tsx\nimport { DevTools } from 'jotai-devtools'\nimport 'jotai-devtools/styles.css'\n\nconst App = () => {\n  return (\n    <>\n      <DevTools />\n      {/* your app */}\n    </>\n  )\n}\n```\n\n#### With Provider\n\n```tsx\nimport { createStore } from 'jotai'\nimport { DevTools } from 'jotai-devtools'\nimport 'jotai-devtools/styles.css'\n\nconst customStore = createStore()\n\nconst App = () => {\n  return (\n    <Provider store={customStore}>\n      <DevTools store={customStore} />\n      {/* your app */}\n    </Provider>\n  )\n}\n```\n\n### Preview\n\n<Stackblitz id=\"vitejs-vite-hzldhk\" file=\"src%2FApp.tsx\" />\n\n## useAtomsDebugValue\n\n`useAtomsDebugValue` is a React hook that will show all atom values in React Devtools.\n\n```ts\nfunction useAtomsDebugValue(options?: {\n  store?: Store\n  enabled?: boolean\n}): void\n```\n\nInternally, it uses `useDebugValue` which is only effective in DEV mode.\nIt will catch all atoms that are accessible from the place the hook is located.\n\n### Example\n\n```jsx\nimport { useAtomsDebugValue } from 'jotai-devtools/utils'\n\nconst textAtom = atom('hello')\ntextAtom.debugLabel = 'textAtom'\n\nconst lenAtom = atom((get) => get(textAtom).length)\nlenAtom.debugLabel = 'lenAtom'\n\nconst TextBox = () => {\n  const [text, setText] = useAtom(textAtom)\n  const [len] = useAtom(lenAtom)\n  return (\n    <span>\n      <input value={text} onChange={(e) => setText(e.target.value)} />({len})\n    </span>\n  )\n}\n\nconst DebugAtoms = () => {\n  useAtomsDebugValue()\n  return null\n}\n\nconst App = () => (\n  <Provider>\n    <DebugAtoms />\n    <TextBox />\n  </Provider>\n)\n```\n\n<Stackblitz id=\"vitejs-vite-dmts5e\" file=\"src%2FApp.tsx\" />\n\n## useAtomDevtools\n\n`useAtomDevtools` is a React hook that manages ReduxDevTools extension for a particular atom.\n\n```ts\nfunction useAtomDevtools<Value>(\n  anAtom: WritableAtom<Value, Value>,\n  options?: {\n    store?: Store\n    name?: string\n    enabled?: boolean\n  },\n): void\n```\n\nThe `useAtomDevtools` hook accepts a generic type parameter (mirroring the type stored in the atom). Additionally, the hook accepts two invocation parameters, `anAtom` and `name`.\n`anAtom` is the atom that will be attached to the devtools instance. `name` is an optional parameter that defines the debug label for the devtools instance. If `name` is undefined, `atom.debugLabel` will be used instead.\n\n### Example\n\n```ts\nimport { useAtomDevtools } from 'jotai-devtools/utils'\n\n// The interface for the type stored in the atom.\nexport interface Task {\n  label: string\n  complete: boolean\n}\n\n// The atom to debug.\nexport const tasksAtom = atom<Task[]>([])\n\n// If the useAtomDevtools name parameter is undefined, this value will be used instead.\ntasksAtom.debugLabel = 'Tasks'\n\nexport const useTasksDevtools = () => {\n  // The hook can be called simply by passing an atom for debugging.\n  useAtomDevtools(tasksAtom)\n\n  // Specify a custom type parameter\n  useAtomDevtools<Task[]>(tasksAtom)\n\n  // You can attach two devtools instances to the same atom and differentiate them with custom names.\n  useAtomDevtools(tasksAtom, 'Tasks (Instance 1)')\n  useAtomDevtools(tasksAtom, 'Tasks (Instance 2)')\n}\n```\n\n## useAtomsDevtools\n\n⚠️ Note: This hook is experimental (feedbacks are welcome) and only works in a `process.env.NODE_ENV !== 'production'` environment.\n\n`useAtomsDevtools` is a catch-all version of `useAtomDevtools` where it shows all atoms in the store instead of showing a specific one.\n\n```ts\nfunction useAtomsDevtools(\n  name: string,\n  options?: {\n    store?: Store\n    enabled?: boolean\n  },\n): void\n```\n\nIt takes a `name` parameter that is needed for naming the Redux devtools instance and a `store` parameter.\n\nAs a limitation for this API, we need to put `useAtomsDevtools` in a component where the consumed atoms should be in a lower place of the React tree than that component (`AtomsDevtools` in the below example).\n`AtomsDevtools` component can be considered as a best practice for our apps.\n\n### Example\n\n```jsx\nconst countAtom = atom(0);\nconst doubleCountAtom = atom((get) => get(countAtom) * 2);\n\nfunction Counter() {\n  const  [count, setCount] =  useAtom(countAtom);\n  const  [doubleCount] =  useAtom(doubleCountAtom);\n\n  ...\n}\n\nconst AtomsDevtools = ({ children }) => {\n  useAtomsDevtools('demo')\n  return children\n}\n\nexport default function App()  {\n  return (\n    <AtomsDevtools>\n      <Counter />\n    </AtomsDevtools>\n  )\n }\n```\n\n<Stackblitz id=\"vitejs-vite-lpr5rs\" file=\"src%2FApp.tsx\" />\n\n## useAtomsSnapshot\n\n⚠️ Note: This hook only works in a `process.env.NODE_ENV !== 'production'` environment. And it returns a static empty value in production.\n\n`useAtomsSnapshot` takes a snapshot of the currently mounted atoms and their state.\n\n```ts\nfunction useAtomsSnapshot(options?: { store?: Store }): AtomsSnapshot\n```\n\nIt accepts a `store` parameter and will return an `AtomsSnapshot`, which is basically a `Map<AnyAtom, unknown>`. You can use the `Map` API to iterate over atoms and their state.\nThis hook is primarily meant for debugging and devtools use cases.\n\nBe careful using this hook because it will cause the component to re-render for all state changes.\n\n### Example\n\n```jsx\nimport { Provider } from 'jotai'\nimport { useAtomsSnapshot } from 'jotai-devtools/utils'\n\nconst RegisteredAtoms = () => {\n  const atoms = useAtomsSnapshot()\n\n  return (\n    <div>\n      <p>Atom count: {atoms.size}</p>\n      <div>\n        {Array.from(atoms).map(([atom, atomValue]) => (\n          <p key={`${atom}`}>{`${atom.debugLabel}: ${atomValue}`}</p>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nconst App = () => (\n  <Provider>\n    <RegisteredAtoms />\n  </Provider>\n)\n```\n\n## useGotoAtomsSnapshot\n\n⚠️ Note: This hook only works in a `process.env.NODE_ENV !== 'production'` environment. And it works like an empty function in the production environment.\n\n`useGotoAtomsSnapshot` will update the current Jotai state to match the passed snapshot.\n\n```ts\nfunction useGotoAtomsSnapshot(options?: {\n  store?: Store\n}): (values: Iterable<readonly [AnyAtom, unknown]>) => void\n```\n\nThis hook returns a callback, which takes a `snapshot` from the `useAtomsSnapshot` hook and will update the Jotai state. It accepts a `store` parameter.\nThis hook is primarily meant for debugging and devtools use cases.\n\n### Example\n\n```jsx\nimport { Provider } from 'jotai'\nimport { useAtomsSnapshot, useGotoAtomsSnapshot } from 'jotai-devtools/utils'\n\nconst petAtom = atom('cat')\nconst colorAtom = atom('blue')\n\nconst UpdateSnapshot = () => {\n  const snapshot = useAtomsSnapshot()\n  const goToSnapshot = useGotoAtomsSnapshot()\n  return (\n    <button\n      onClick={() => {\n        const newSnapshot = new Map(snapshot)\n        newSnapshot.set(petAtom, 'dog')\n        newSnapshot.set(colorAtom, 'green')\n        goToSnapshot(newSnapshot)\n      }}\n    >\n      Go to snapshot\n    </button>\n  )\n}\n```\n"
  },
  {
    "path": "docs/tools/swc.mdx",
    "content": "---\ntitle: SWC\ndescription: This doc describes SWC plugins for Jotai.\nnav: 6.01\nkeywords: swc,next,nextjs,fast,refresh\n---\n\n⚠️ Note: These plugins are experimental. Feedback is welcome in the [Github repo](https://github.com/pmndrs/swc-jotai). Please file issues in the separate repo instead of `jotai` repo.\n\n## @swc-jotai/react-refresh\n\nThis plugin adds support for React Refresh for Jotai atoms. This makes sure that state isn't reset, when developing using React Refresh.\n\n### Usage\n\nInstall it with:\n\n```sh\nnpm install --save-dev @swc-jotai/react-refresh\n```\n\nYou can add the plugin to `.swcrc`:\n\n```json\n{\n  \"jsc\": {\n    \"experimental\": {\n      \"plugins\": [[\"@swc-jotai/react-refresh\", {}]]\n    }\n  }\n}\n```\n\nYou can use the plugin with the [experimental SWC plugins feature](https://nextjs.org/docs/advanced-features/compiler#swc-plugins-experimental) in Next.js.\n\n```js\nmodule.exports = {\n  experimental: {\n    swcPlugins: [['@swc-jotai/react-refresh', {}]],\n  },\n}\n```\n\nExamples can be found below.\n\n## @swc-jotai/debug-label\n\nJotai is based on object references and not keys (like Recoil). This means there's no identifier for atoms. To identify atoms, it's possible to add a `debugLabel` to an atom, which can be found in React devtools.\n\nHowever, this can quickly become cumbersome to add a `debugLabel` to every atom.\n\nThis `SWC` plugin adds a `debugLabel` to every atom, based on its identifier.\n\nThe plugin transforms this code:\n\n```js\nexport const countAtom = atom(0)\n```\n\nInto:\n\n```js\nexport const countAtom = atom(0)\ncountAtom.debugLabel = 'countAtom'\n```\n\nDefault exports are also handled, based on the file naming:\n\n```js\n// countAtom.ts\nexport default atom(0)\n```\n\nWhich transform into:\n\n```js\n// countAtom.ts\nconst countAtom = atom(0)\ncountAtom.debugLabel = 'countAtom'\nexport default countAtom\n```\n\n### Usage\n\nInstall it with:\n\n```sh\nnpm install --save-dev @swc-jotai/debug-label\n```\n\nYou can add the plugin to `.swcrc`:\n\n```json\n{\n  \"jsc\": {\n    \"experimental\": {\n      \"plugins\": [[\"@swc-jotai/debug-label\", {}]]\n    }\n  }\n}\n```\n\nOr you can use the plugin with the [experimental SWC plugins feature](https://nextjs.org/docs/advanced-features/compiler#swc-plugins-experimental) in Next.js.\n\n```js\nmodule.exports = {\n  experimental: {\n    swcPlugins: [['@swc-jotai/debug-label', {}]],\n  },\n}\n```\n\nExamples can be found below.\n\n### Custom atom names\n\nYou can enable the plugins for your custom atoms. You can supply them to the plugins like below:\n\n```js\nmodule.exports = {\n  experimental: {\n    swcPlugins: [\n      ['@swc-jotai/debug-label', { atomNames: ['customAtom'] }],\n      ['@swc-jotai/react-refresh', { atomNames: ['customAtom'] }],\n    ],\n  },\n}\n```\n\n### Examples\n\n### Next.js\n\n<Stackblitz id=\"stackblitz-starters-ha5txx\" file=\"app%2Fpage.tsx\" />\n"
  },
  {
    "path": "docs/utilities/async.mdx",
    "content": "---\ntitle: Async\nnav: 3.03\nkeywords: load,loadable,observable\n---\n\nAll atoms support async behavior such as async read or async write. However there are APIs for more control described here.\n\n## loadable\n\nIf you don't want async atoms to suspend or throw to an error boundary (for example, for finer-grained control of loading and error logic), you can use the `loadable` util.\n\nIt would work the same way for any atom. Simply wrap your atoms with the `loadable` util. It returns a value with one of three states: `loading`, `hasData` and `hasError`.\n\n```ts\n{\n\tstate: 'loading' | 'hasData' | 'hasError',\n\tdata?: any,\n\terror?: any,\n}\n```\n\n```jsx\nimport { loadable } from \"jotai/utils\"\n\nconst asyncAtom = atom(async (get) => ...)\nconst loadableAtom = loadable(asyncAtom)\n// Does not need to be wrapped by a <Suspense> element\nconst Component = () => {\n  const [value] = useAtom(loadableAtom)\n  if (value.state === 'hasError') return <Text>{value.error}</Text>\n  if (value.state === 'loading') {\n    return <Text>Loading...</Text>\n  }\n  console.log(value.data) // Results of the Promise\n  return <Text>Value: {value.data}</Text>\n}\n```\n\n## atomWithObservable\n\nRef: https://github.com/pmndrs/jotai/pull/341\n\n### Usage\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithObservable } from 'jotai/utils'\nimport { interval } from 'rxjs'\nimport { map } from 'rxjs/operators'\n\nconst counterSubject = interval(1000).pipe(map((i) => `#${i}`))\nconst counterAtom = atomWithObservable(() => counterSubject)\n\nconst Counter = () => {\n  const [counter] = useAtom(counterAtom)\n  return <div>count: {counter}</div>\n}\n```\n\nThe `atomWithObservable` function creates an atom from a rxjs (or similar) `subject` or `observable`.\nIts value will be last value emitted from the stream.\n\nTo use this atom, you need to wrap your component with `<Suspense>`. Check out [guides/async](../guides/async.mdx).\n\n### Initial value\n\n`atomWithObservable` takes second optional parameter `{ initialValue }` that allows to specify initial value for the atom. If `initialValue` is provided then `atomWithObservable` will not suspend and will show initial value before receiving first value from observable. `initialValue` can be either a value or a function that returns a value\n\n```js\nconst counterAtom = atomWithObservable(() => counterSubject, {\n  initialValue: 10,\n})\n\nconst counterAtom2 = atomWithObservable(() => counterSubject, {\n  initialValue: () => Math.random(),\n})\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-3bkqrb\" file=\"src%2FApp.tsx\" />\n\n## unwrap\n\nThe `unwrap` util will convert an async atom to a sync atom like `loadable`.\nUnlike `loadable`, the fallback value can be configured.\nUnlike `loadable`, the error won't be handled and just thrown.\n\nThe use case of `unwrap` is to ease deriving atoms.\nThis is especially useful for v2 API,\nbecause `get` in the read function doesn't resolve promises.\n\n### Signature\n\n```ts\nfunction unwrap<Value, Args extends unknown[], Result>(\n  anAtom: WritableAtom<Value, Args, Result>,\n): WritableAtom<Awaited<Value> | undefined, Args, Result>\n\nfunction unwrap<Value, Args extends unknown[], Result, PendingValue>(\n  anAtom: WritableAtom<Value, Args, Result>,\n  fallback: (prev?: Awaited<Value>) => PendingValue,\n): WritableAtom<Awaited<Value> | PendingValue, Args, Result>\n\nfunction unwrap<Value>(anAtom: Atom<Value>): Atom<Awaited<Value> | undefined>\n\nfunction unwrap<Value, PendingValue>(\n  anAtom: Atom<Value>,\n  fallback: (prev?: Awaited<Value>) => PendingValue,\n): Atom<Awaited<Value> | PendingValue>\n```\n\n### Usage\n\n```tsx\nimport { atom } from 'jotai'\nimport { unwrap } from 'jotai/utils'\n\nconst countAtom = atom(0)\nconst delayedCountAtom = atom(async (get) => {\n  await new Promise((r) => setTimeout(r, 500))\n  return get(countAtom)\n})\n\nconst unwrapped1Atom = unwrap(delayedCountAtom)\n// The value is `undefined` while pending\n\nconst unwrapped2Atom = unwrap(delayedCountAtom, (prev) => prev ?? 0)\n// The value is `0` initially, and subsequent updates keep the previous value.\n```\n"
  },
  {
    "path": "docs/utilities/callback.mdx",
    "content": "---\ntitle: Callback\nnav: 3.99\nkeywords: callback\npublished: false\n---\n\n## useAtomCallback\n\nRef: https://github.com/pmndrs/jotai/issues/60\n\n### Usage\n\n```ts\nuseAtomCallback<Result, Args extends unknown[]>(\n  callback: (get: Getter, set: Setter, ...arg: Args) => Result,\n  options?: Options\n): (...args: Args) => Result\n```\n\nThis hook is for interacting with atoms imperatively.\nIt takes a callback function that works like atom write function,\nand returns a function that returns an atom value.\n\nThe callback to pass in the hook must be stable (should be wrapped with useCallback).\n\n### Examples\n\n```jsx\nimport { useEffect, useState, useCallback } from 'react'\nimport { Provider, atom, useAtom } from 'jotai'\nimport { useAtomCallback } from 'jotai/utils'\n\nconst countAtom = atom(0)\n\nconst Counter = () => {\n  const [count, setCount] = useAtom(countAtom)\n  return (\n    <>\n      {count} <button onClick={() => setCount((c) => c + 1)}>+1</button>\n    </>\n  )\n}\n\nconst Monitor = () => {\n  const [count, setCount] = useState(0)\n  const readCount = useAtomCallback(\n    useCallback((get) => {\n      const currCount = get(countAtom)\n      setCount(currCount)\n      return currCount\n    }, []),\n  )\n  useEffect(() => {\n    const timer = setInterval(async () => {\n      console.log(readCount())\n    }, 1000)\n    return () => {\n      clearInterval(timer)\n    }\n  }, [readCount])\n  return <div>current count: {count}</div>\n}\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-ekxjhs\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/utilities/family.mdx",
    "content": "---\ntitle: Family\nnav: 3.5\nkeywords: family\n---\n\n## atomFamily\n\n:::caution Deprecated\n`atomFamily` is deprecated and will be removed in Jotai v3.\n\nPlease migrate to the [`jotai-family`](https://github.com/jotaijs/jotai-family) package, which provides the same API with additional features like `atomTree`.\n\n**Migration:**\n\n```bash\nnpm install jotai-family\n```\n\n```ts\n// Before\nimport { atomFamily } from 'jotai/utils'\n\n// After\nimport { atomFamily } from 'jotai-family'\n```\n\nThe API is identical, so no code changes are needed beyond the import statement.\n:::\n\nRef: https://github.com/pmndrs/jotai/issues/23\n\n### Usage\n\n```js\natomFamily(initializeAtom, areEqual): (param) => Atom\n```\n\nThis will create a function that takes `param` and returns an atom.\nIf the atom has already been created, it will be returned from the cache.\n`initializeAtom` is a function that can return any kind of atom (`atom()`, `atomWithDefault()`, ...).\nNote that the `areEqual` argument is optional and compares\nif two params are equal (defaults to `Object.is`).\n\nTo reproduce behavior similar to [Recoil's atomFamily/selectorFamily](https://recoiljs.org/docs/api-reference/utils/atomFamily),\nspecify a deepEqual function to `areEqual`. For example:\n\n```js\nimport { atom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\nimport deepEqual from 'fast-deep-equal'\n\nconst fooFamily = atomFamily((param) => atom(param), deepEqual)\n```\n\n### TypeScript\n\nThe atom family types will be inferred from initializeAtom. Here's a typical usage with a primitive atom.\n\n```ts\nimport type { PrimitiveAtom } from 'jotai'\n\n/**\n * here the atom(id) returns a PrimitiveAtom<number>\n * and PrimitiveAtom<number> is a WritableAtom<number, SetStateAction<number>>\n */\nconst myFamily = atomFamily((id: number) => atom(id))\n```\n\nYou can explicitly declare the type of parameter and atom type using TypeScript generics.\n\n```ts\natomFamily<Param, AtomType extends Atom<unknown>>(\n  initializeAtom: (param: Param) => AtomType,\n  areEqual?: (a: Param, b: Param) => boolean\n): AtomFamily<Param, AtomType>\n```\n\nExample with explicit types:\n\n```ts\nimport { atom } from 'jotai'\nimport type { PrimitiveAtom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\n\nconst myFamily = atomFamily<number, PrimitiveAtom<number>>((id: number) =>\n  atom(id),\n)\n```\n\n### API Methods\n\nThe `atomFamily` function returns an object with the following methods:\n\n#### `myFamily(param)`\n\nReturns an atom for the given param. If the atom has already been created, it will be returned from the cache.\n\n#### `myFamily.getParams()`\n\nReturns an iterable of all params currently in the cache.\n\n```js\nconst todoFamily = atomFamily((name) => atom(name))\n\ntodoFamily('foo')\ntodoFamily('bar')\n\nfor (const param of todoFamily.getParams()) {\n  console.log(param) // 'foo', 'bar'\n}\n```\n\n#### `myFamily.remove(param)`\n\nRemoves a specific param from the cache.\n\n```js\ntodoFamily.remove('foo')\n```\n\n#### `myFamily.setShouldRemove(shouldRemove)`\n\nRegisters a `shouldRemove` function which runs immediately **and** when you are about to get an atom from the cache.\n\n- `shouldRemove` is a function that takes two arguments: `createdAt` (in milliseconds) and `param`, and returns a boolean value.\n- Setting `null` will remove the previously registered function.\n\n```js\n// Remove atoms older than 1 hour\ntodoFamily.setShouldRemove((createdAt, param) => {\n  return Date.now() - createdAt > 60 * 60 * 1000\n})\n```\n\n#### `myFamily.unstable_listen(callback)`\n\n**⚠️ Unstable API**: This API is for advanced use cases and can change without notice.\n\nFires when an atom is created or removed. Returns a cleanup function.\n\n```js\nconst cleanup = todoFamily.unstable_listen((event) => {\n  console.log(event.type) // 'CREATE' or 'REMOVE'\n  console.log(event.param) // the param\n  console.log(event.atom) // the atom\n})\n\n// Later, stop listening\ncleanup()\n```\n\n### Caveat: Memory Leaks\n\nInternally, atomFamily is just a Map whose key is a param and whose value is an atom config.\nUnless you explicitly remove unused params, this leads to memory leaks.\nThis is crucial if you use infinite number of params.\n\nUse `myFamily.remove(param)` or `myFamily.setShouldRemove(shouldRemove)` to manage memory.\n\n### Examples\n\n```js\nimport { atom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\n\nconst todoFamily = atomFamily((name) => atom(name))\n\ntodoFamily('foo')\n// this will create a new atom('foo'), or return the one if already created\n```\n\n```js\nimport { atom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\n\nconst todoFamily = atomFamily((name) =>\n  atom(\n    (get) => get(todosAtom)[name],\n    (get, set, arg) => {\n      const prev = get(todosAtom)\n      set(todosAtom, { ...prev, [name]: { ...prev[name], ...arg } })\n    },\n  ),\n)\n```\n\n```js\nimport { atom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\n\nconst todoFamily = atomFamily(\n  ({ id, name }) => atom({ name }),\n  (a, b) => a.id === b.id,\n)\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-rx6npn\" file=\"src%2FApp.tsx\" />\n\n---\n\n## Migration to jotai-family\n\nFor new projects or when updating existing code, we recommend using the [`jotai-family`](https://github.com/jotaijs/jotai-family) package instead. It provides:\n\n- **Same API**: Drop-in replacement for `atomFamily`\n- **Additional features**: Includes `atomTree` for hierarchical atom management\n- **Better maintenance**: Dedicated package with focused development\n- **Future-proof**: Will continue to be supported in Jotai v3 and beyond\n\nSee the [jotai-family documentation](https://github.com/jotaijs/jotai-family#readme) for more details.\n"
  },
  {
    "path": "docs/utilities/lazy.mdx",
    "content": "---\ntitle: Lazy\nnav: 3.03\nkeywords: lazy,initialize,init,loading\n---\n\nWhen defining primitive atoms, their initial value has to be bound at definition time.\nIf creating that initial value is computationally expensive, or the value is not accessible during definition,\nit would be best to postpone the atom's initialization until its [first use in the store](#using-multiple-stores).\n\n```jsx\nconst imageDataAtom = atom(initializeExpensiveImage()) // 1) has to be computed here\n\nfunction Home() {\n  ...\n}\n\nfunction ImageEditor() {\n  // 2) used only in this route\n  const [imageData, setImageData] = useAtom(imageDataAtom);\n  ...\n}\n\nfunction App() {\n  return (\n    <Router>\n      <Route path=\"/\" component={Home} />\n      <Route path=\"/edit\" component={ImageEditor} />\n    </Router>\n  )\n}\n```\n\n## atomWithLazy\n\nRef: https://github.com/pmndrs/jotai/pull/2465\n\nWe can use `atomWithLazy` to create a primitive atom whose initial value will be computed at [first use in the store](#using-multiple-stores).\nAfter initialization, it will behave like a regular primitive atom (can be written to).\n\n### Usage\n\n```jsx\nimport { atomWithLazy } from 'jotai/utils'\n\n// passing the initialization function\nconst imageDataAtom = atomWithLazy(initializeExpensiveImage)\n\nfunction Home() {\n  ...\n}\n\nfunction ImageEditor() {\n  // only gets initialized if user goes to `/edit`.\n  const [imageData, setImageData] = useAtom(imageDataAtom);\n  ...\n}\n\nfunction App() {\n  return (\n    <Router>\n      <Route path=\"/\" component={Home} />\n      <Route path=\"/edit\" component={ImageEditor} />\n    </Router>\n  )\n}\n```\n\n### Using multiple stores\n\nSince each store is its separate universe, the initial value will be recreated exactly once per store (unless using something like `jotai-scope`, which fractures a store into smaller universes).\n\n```ts\ntype RGB = [number, number, number];\n\nfunction randomRGB(): RGB {\n  ...\n}\n\nconst lift = (value: number) => ([r, g, b]: RGB) => {\n  return [r + value, g + value, b + value]\n}\n\nconst colorAtom = lazyAtom(randomRGB)\n\nlet store = createStore()\n\nconsole.log(store.get(colorAtom)) // [0, 36, 128]\nstore.set(colorAtom, lift(8))\nconsole.log(store.get(colorAtom)) // [8, 44, 136]\n\n// recreating store, sometimes done when logging out or resetting the app in some way\nstore = createStore()\n\nconsole.log(store.get(colorAtom)) // [255, 12, 46] -- a new random color\n```\n"
  },
  {
    "path": "docs/utilities/reducer.mdx",
    "content": "---\ntitle: Reducer\nnav: 3.99\nkeywords: reducer,action,dispatch\npublished: false\n---\n\n## atomWithReducer\n\nRef: https://github.com/pmndrs/jotai/issues/38\n\n```js\nimport { atomWithReducer } from 'jotai/utils'\n\nconst countReducer = (prev, action) => {\n  if (action.type === 'inc') return prev + 1\n  if (action.type === 'dec') return prev - 1\n  throw new Error('unknown action type')\n}\n\nconst countReducerAtom = atomWithReducer(0, countReducer)\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-bbpmjn\" file=\"src%2FApp.tsx\" />\n\n## useReducerAtom\n\nSee [useReducerAtom](../recipes/use-reducer-atom.mdx) recipe.\n"
  },
  {
    "path": "docs/utilities/resettable.mdx",
    "content": "---\ntitle: Resettable\nnav: 3.04\nkeywords: reset,default\n---\n\n## atomWithReset\n\nRef: https://github.com/pmndrs/jotai/issues/41\n\n```ts\nfunction atomWithReset<Value>(\n  initialValue: Value,\n): WritableAtom<Value, SetStateAction<Value> | typeof RESET>\n```\n\nCreates an atom that could be reset to its `initialValue` with\n[`useResetAtom`](use-reset-atom.mdx) hook. It works exactly the same\nway as primitive atom would, but you are also able to set it to a special value\n[`RESET`](reset.mdx). See examples in [Resettable atoms](../utilities/resettable.mdx).\n\n### Example\n\n```js\nimport { atomWithReset } from 'jotai/utils'\n\nconst dollarsAtom = atomWithReset(0)\nconst todoListAtom = atomWithReset([\n  { description: 'Add a todo', checked: false },\n])\n```\n\n## RESET\n\nRef: https://github.com/pmndrs/jotai/issues/217\n\n```ts\nconst RESET: unique symbol\n```\n\nSpecial value that is accepted by [Resettable atoms](../utilities/resettable.mdx)\ncreated with [`atomWithReset`](../utilities/resettable.mdx), [`atomWithDefault`](../utilities/resettable.mdx)\nor writable atom created with `atom` if it accepts `RESET` symbol.\n\n### Example\n\n```jsx\nimport { atom, useSetAtom } from 'jotai'\nimport { atomWithReset, useResetAtom, RESET } from 'jotai/utils'\n\nconst dollarsAtom = atomWithReset(0)\nconst centsAtom = atom(\n  (get) => get(dollarsAtom) * 100,\n  (get, set, newValue: number | typeof RESET) =>\n    set(dollarsAtom, newValue === RESET ? newValue : newValue / 100)\n)\n\nconst ResetExample = () => {\n  const setDollars = useSetAtom(dollarsAtom)\n  const resetCents = useResetAtom(centsAtom)\n\n  return (\n    <>\n      <button onClick={() => setDollars(RESET)}>Reset dollars</button>\n      <button onClick={resetCents}>Reset cents</button>\n    </>\n  )\n}\n```\n\n## useResetAtom\n\n```ts\nfunction useResetAtom<Value>(\n  anAtom: WritableAtom<Value, typeof RESET>,\n): () => void | Promise<void>\n```\n\nResets a [Resettable atom](../utilities/resettable.mdx) to its initial value.\n\n### Example\n\n```jsx\nimport { useResetAtom } from 'jotai/utils'\nimport { todoListAtom } from './store'\n\nconst TodoResetButton = () => {\n  const resetTodoList = useResetAtom(todoListAtom)\n  return <button onClick={resetTodoList}>Reset</button>\n}\n```\n\n## atomWithDefault\n\nRef: https://github.com/pmndrs/jotai/issues/352\n\n### Usage\n\nThis is a function to create a resettable primitive atom.\nIts default value can be specified with a read function instead of a static initial value.\n\n```js\nimport { atomWithDefault } from 'jotai/utils'\n\nconst count1Atom = atom(1)\nconst count2Atom = atomWithDefault((get) => get(count1Atom) * 2)\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-gffb5v\" file=\"src%2FApp.tsx\" />\n\n### Resetting default values\n\nYou can reset the value of an `atomWithDefault` atom to its original default value.\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithDefault, useResetAtom, RESET } from 'jotai/utils'\n\nconst count1Atom = atom(1)\nconst count2Atom = atomWithDefault((get) => get(count1Atom) * 2)\n\nconst Counter = () => {\n  const [count1, setCount1] = useAtom(count1Atom)\n  const [count2, setCount2] = useAtom(count2Atom)\n  const resetCount2 = useResetAtom(count2Atom)\n\n  return (\n    <>\n      <div>\n        count1: {count1}, count2: {count2}\n      </div>\n      <button onClick={() => setCount1((c) => c + 1)}>increment count1</button>\n      <button onClick={() => setCount2((c) => c + 1)}>increment count2</button>\n      <button onClick={() => resetCount2()}>Reset with useResetAtom</button>\n      <button onClick={() => setCount2(RESET)}>Reset with RESET const</button>\n    </>\n  )\n}\n```\n\nThis can be useful when an `atomWithDefault` atom value is overwritten\nusing the `set` function, in which case the provided `getter` function\nis no longer used and any change in dependencies atoms will not trigger an update.\n\nResetting the value allows us to restore its original default value,\ndiscarding changes made previously via the `set` function.\n\n## atomWithRefresh\n\n```ts\nfunction atomWithRefresh<Value>(\n  read: Read<Value, [], void>,\n): WritableAtom<Value, [], void>\n```\n\nCreates an atom that we can refresh,\nwhich is to force reevaluating the read function.\n\nThis is helpful when you need to refresh asynchronous data.\nIt can also be used to implement \"pull to refresh\" functionality.\n\n```ts\nfunction atomWithRefresh<Value, Args extends unknown[], Result>(\n  read: Read<Value, Args, Result>,\n  write: Write<Value, Args, Result>,\n): WritableAtom<Value, Args | [], Result | void>\n```\n\nPassing zero arguments to `set` will refresh.\nPassing one or more arguments to `set` will call \"write\" function.\n\n### Example\n\nHere's how you'd use it to implement an refresh-able source of data:\n\n```js\nimport { atomWithRefresh } from 'jotai/utils'\n\nconst postsAtom = atomWithRefresh((get) =>\n  fetch('https://jsonplaceholder.typicode.com/posts').then((r) => r.json()),\n)\n```\n\nIn a component:\n\n```jsx\nconst PostsList = () => {\n  const [posts, refreshPosts] = useAtom(postsAtom)\n\n  return (\n    <div>\n      <ul>\n        {posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n\n      {/* Clicking this button will re-fetch the posts */}\n      <button type=\"button\" onClick={() => refreshPosts()}>\n        Refresh posts\n      </button>\n    </div>\n  )\n}\n```\n"
  },
  {
    "path": "docs/utilities/select.mdx",
    "content": "---\ntitle: Select\nnav: 3.99\nkeywords: select\npublished: false\n---\n\n## selectAtom\n\n⚠️ Unlike its name, `selectAtom` is provided as an escape hatch. Using it means building not 100% pure atom model. Prefer using derived atoms and use `selectAtom` only when `equalityFn` or `prevSlice` is unavoidable.\n\n### Signatures\n\n```ts\nfunction selectAtom<Value, Slice>(\n  anAtom: Atom<Value>,\n  selector: (v: Value, prevSlice?: Slice) => Slice,\n  equalityFn: (a: Slice, b: Slice) => boolean = Object.is,\n): Atom<Slice>\n```\n\nThis function creates a derived atom whose value is a function of the original atom's value,\ndetermined by `selector.`\nThe selector function runs whenever the original atom changes; it updates the derived atom\nonly if `equalityFn` reports that the derived value has changed.\nBy default, `equalityFn` is reference equality, but you can supply your favorite deep-equals\nfunction to stabilize the derived value where necessary.\n\n### Examples\n\n```js\nconst defaultPerson = {\n  name: {\n    first: 'Jane',\n    last: 'Doe',\n  },\n  birth: {\n    year: 2000,\n    month: 'Jan',\n    day: 1,\n    time: {\n      hour: 1,\n      minute: 1,\n    },\n  },\n}\n\n// Original atom.\nconst personAtom = atom(defaultPerson)\n\n// Tracks person.name. Updated when person.name object changes, even\n// if neither name.first nor name.last actually change.\nconst nameAtom = selectAtom(personAtom, (person) => person.name)\n\n// Tracks person.birth. Updated when year, month, day, hour, or minute changes.\n// Use of deepEquals means that this atom doesn't update if birth field is\n// replaced with a new object containing the same data. E.g., if person is re-read\n// from a database.\nconst birthAtom = selectAtom(personAtom, (person) => person.birth, deepEquals)\n```\n\n### Hold stable references\n\nAs always, to prevent an infinite loop when using `useAtom` in render cycle, you must provide `useAtom` a stable reference of your atoms.\nFor `selectAtom`, we need **both** the base atom and the selector to be stable.\n\n```js\nconst [value] = useAtom(selectAtom(atom(0), (val) => val)) // So this will cause an infinite loop\n```\n\nYou have multiple options in order to satisfy these constraints:\n\n```js\nconst baseAtom = atom(0) // Stable\nconst baseSelector = (v) => v // Stable\nconst Component = () => {\n  // Solution 1: Memoize the whole resulting atom with \"useMemo\"\n  const [value] = useAtom(useMemo(() => selectAtom(baseAtom, (v) => v), []))\n\n  // Solution 2: Memoize the inline callback with \"useCallback\"\n  const [value] = useAtom(\n    selectAtom(\n      baseAtom,\n      useCallback((v) => v, []),\n    ),\n  )\n\n  // Solution 3: All constraints are already satisfied\n  const [value] = useAtom(selectAtom(baseAtom, baseSelector))\n}\n```\n\n### Stackblitz\n\n<Stackblitz id=\"vitejs-vite-tgejq7\" file=\"src%2FApp.tsx\" />\n"
  },
  {
    "path": "docs/utilities/split.mdx",
    "content": "---\ntitle: Split\nnav: 3.99\nkeywords: select\npublished: false\n---\n\n## splitAtom\n\nThe `splitAtom` utility is designed for scenarios where you need to obtain an atom for each element in a list. It operates on read/write atoms containing a list, returning an atom that holds a list of atoms, each corresponding to an item in the original list.\n\n### Signature\n\nA simplified type signature for `splitAtom` would be:\n\n```ts\ntype SplitAtom = <Item, Key>(\n  arrayAtom: PrimitiveAtom<Array<Item>>,\n  keyExtractor?: (item: Item) => Key\n): Atom<Array<PrimitiveAtom<Item>>>\n```\n\n### Key Features\n\n1. The returned atom contains a dispatch function in the `write` direction (since v1.6.4), providing a simple way to modify the original atom with actions like `remove`, `insert`, and `move`.\n\n2. An optional `keyExtractor` function can be provided as a second argument to enhance stability and performance.\n\n### Key Extractor\n\nThe `splitAtom` utility supports a second argument which is a key extractor function:\n\n```ts\nexport function splitAtom<Item, Key>(\n  arrAtom: WritableAtom<Item[], [Item[]], void> | Atom<Item[]>,\n  keyExtractor?: (item: Item) => Key,\n)\n```\n\nImportant considerations for the `keyExtractor`:\n\n- If `splitAtom` is used within a React render loop, the `keyExtractor` must be a stable function that maintains object equality (shallow equality). This requirement doesn't apply outside of render loops.\n- Providing a `keyExtractor` enhances the stability of the atom output (which makes memoization hit cache more often). It prevents unnecessary subatom recreation due to index shifts in the source array.\n- `keyExtractor` is an optional optimization that should only be used if the extracted key is guaranteed unique for each item in the array.\n\n### Example Usage\n\nHere's an example demonstrating the use of `splitAtom`:\n\n<Stackblitz id=\"vitejs-vite-oavdw2\" file=\"src%2FApp.tsx\" />\n\n```tsx\nimport { Provider, atom, useAtom, PrimitiveAtom } from 'jotai'\nimport { splitAtom } from 'jotai/utils'\nimport './styles.css'\n\nconst initialState = [\n  {\n    task: 'help the town',\n    done: false,\n  },\n  {\n    task: 'feed the dragon',\n    done: false,\n  },\n]\n\nconst todosAtom = atom(initialState)\nconst todoAtomsAtom = splitAtom(todosAtom)\n\ntype TodoType = (typeof initialState)[number]\n\nconst TodoItem = ({\n  todoAtom,\n  remove,\n}: {\n  todoAtom: PrimitiveAtom<TodoType>\n  remove: () => void\n}) => {\n  const [todo, setTodo] = useAtom(todoAtom)\n  return (\n    <div>\n      <input\n        value={todo.task}\n        onChange={(e) => {\n          setTodo((oldValue) => ({ ...oldValue, task: e.target.value }))\n        }}\n      />\n      <input\n        type=\"checkbox\"\n        checked={todo.done}\n        onChange={() => {\n          setTodo((oldValue) => ({ ...oldValue, done: !oldValue.done }))\n        }}\n      />\n      <button onClick={remove}>remove</button>\n    </div>\n  )\n}\n\nconst TodoList = () => {\n  const [todoAtoms, dispatch] = useAtom(todoAtomsAtom)\n  return (\n    <ul>\n      {todoAtoms.map((todoAtom) => (\n        <TodoItem\n          todoAtom={todoAtom}\n          remove={() => dispatch({ type: 'remove', atom: todoAtom })}\n        />\n      ))}\n    </ul>\n  )\n}\n\nconst App = () => (\n  <Provider>\n    <TodoList />\n  </Provider>\n)\n\nexport default App\n```\n\nThis example demonstrates how to use `splitAtom` to manage a list of todo items, allowing individual manipulation of each item while maintaining the overall list atom.\n"
  },
  {
    "path": "docs/utilities/ssr.mdx",
    "content": "---\ntitle: SSR\nnav: 3.02\nkeywords: ssr,server,hydrate,hydration,next,nextjs,gatsby,remix,waku,framework\n---\n\n## useHydrateAtoms\n\nRef: https://github.com/pmndrs/jotai/issues/340\n\n### Usage\n\n```js\nimport { atom, useAtom } from 'jotai'\nimport { useHydrateAtoms } from 'jotai/utils'\n\nconst countAtom = atom(0)\nconst CounterPage = ({ countFromServer }) => {\n  useHydrateAtoms([[countAtom, countFromServer]])\n  const [count] = useAtom(countAtom)\n  // count would be the value of `countFromServer`, not 0.\n}\n```\n\nThe primary use case for `useHydrateAtoms` are SSR apps like Next.js, where an initial value is e.g. fetched on the server, which can be passed to a component by props.\n\n⚠️ Note: Although the term \"hydrate\" might suggest server-side usage, this hook is designed for client-side code and should be used with the [`'use client'` directive](https://react.dev/reference/rsc/use-client).\n\n```ts\n// Definition\nfunction useHydrateAtoms(\n  values: Iterable<readonly [Atom<unknown>, unknown]>,\n  options?: { store?: Store },\n): void\n```\n\nThe hook takes an iterable of tuples containing `[atom, value]` as an argument and optional options.\n\n```js\n// Usage with an array, specifying a store\nuseHydrateAtoms(\n  [\n    [countAtom, 42],\n    [frameworkAtom, 'Next.js'],\n  ],\n  { store: myStore },\n)\n// Or with a map\nuseHydrateAtoms(new Map([[count, 42]]))\n```\n\nAtoms can only be hydrated once per store. Therefore, if the initial value used is changed during rerenders, it won't update the atom value.\nIf there is a unique need to re-hydrate a previously hydrated atom, pass the optional dangerouslyForceHydrate as true\nand note that it may behave wrongly in concurrent rendering.\n\n```js\nuseHydrateAtoms(\n  [\n    [countAtom, 42],\n    [frameworkAtom, 'Next.js'],\n  ],\n  {\n    dangerouslyForceHydrate: true,\n  },\n)\n```\n\nIf there's a need to hydrate in multiple stores, use multiple `useHydrateAtoms` hooks to achieve that.\n\n```js\nuseHydrateAtoms([\n  [countAtom, 42],\n  [frameworkAtom, 'Next.js'],\n])\nuseHydrateAtoms(\n  [\n    [countAtom, 17],\n    [frameworkAtom, 'Gatsby'],\n  ],\n  { store: myStore },\n)\n```\n\nIf you are using TypeScript with target `ES5`, you might need `as const` cast on the array to preserve the tuple type.\n\n```ts\nuseHydrateAtoms([\n  [countAtom, 42],\n  [frameworkAtom, 'Next.js'],\n] as const)\n```\n\nOr you may need to use a Map when passing the atom value to useHydrateAtoms. You can find a working example in the [Initializing State on Render docs](https://jotai.org/docs/guides/initialize-atom-on-render#using-typescript).\n\n### Demo\n\n<Stackblitz id=\"stackblitz-starters-b7cvxi\" file=\"pages%2Findex.tsx\" />\n\nThere's more examples in the [Next.js section](../guides/nextjs.mdx).\n"
  },
  {
    "path": "docs/utilities/storage.mdx",
    "content": "---\ntitle: Storage\nnav: 3.01\nkeywords: storage,localstorage,sessionstorage,asyncstorage,persist,persistence\n---\n\n## atomWithStorage\n\nRef: https://github.com/pmndrs/jotai/pull/394\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithStorage } from 'jotai/utils'\n\nconst darkModeAtom = atomWithStorage('darkMode', false)\n\nconst Page = () => {\n  const [darkMode, setDarkMode] = useAtom(darkModeAtom)\n\n  return (\n    <>\n      <h1>Welcome to {darkMode ? 'dark' : 'light'} mode!</h1>\n      <button onClick={() => setDarkMode(!darkMode)}>toggle theme</button>\n    </>\n  )\n}\n```\n\nThe `atomWithStorage` function creates an atom with a value persisted in `localStorage` or `sessionStorage` for React or `AsyncStorage` for React Native.\n\n### Parameters\n\n**key** (required): a unique string used as the key when syncing state with localStorage, sessionStorage, or AsyncStorage\n\n**initialValue** (required): the initial value of the atom\n\n**storage** (optional): an object with the following methods:\n\n- **getItem(key, initialValue)** (required): Reads an item from storage, or falls back to the `intialValue`\n- **setItem(key, value)** (required): Saves an item to storage\n- **removeItem(key)** (required): Deletes the item from storage\n- **subscribe(key, callback, initialValue)** (optional): A method which subscribes to external storage updates.\n\n**options** (optional): an object with the following properties:\n\n- **getOnInit** (optional, by default **false**): A boolean value indicating whether to get item from storage on initialization. Note that in an SPA with `getOnInit` either not set or `false` you will always get the initial value instead of the stored value on initialization. If the stored value is preferred set `getOnInit` to `true`.\n\nIf not specified, the default storage implementation uses `localStorage` for storage/retrieval, `JSON.stringify()`/`JSON.parse()` for serialization/deserialization, and subscribes to `storage` events for cross-tab synchronization.\n\n<Stackblitz id=\"vitejs-vite-xvlgfxrk\" file=\"src%2FApp.tsx\" />\n\n### `createJSONStorage` util\n\nTo create a custom storage implementation with `JSON.stringify()`/`JSON.parse()` for the `storage` option, `createJSONStorage` util is provided.\n\nUsage:\n\n```js\nconst storage = createJSONStorage(\n  // getStringStorage\n  () => localStorage, // or sessionStorage, asyncStorage or alike\n  // options (optional)\n  {\n    reviver, // optional reviver option for JSON.parse\n    replacer, // optional replacer option for JSON.stringify\n  },\n)\n```\n\nNote: `JSON.parse` is not type safe. If it can't accept any types, some kind of validation would be necessary for production apps.\n\n### Server-side rendering\n\nAny JSX markup that depends on the value of a stored atom (e.g., a `className` or `style` prop) will use the `initialValue` when rendered on the server (since `localStorage` and `sessionStorage` are not available on the server).\n\nThis means that there will be a mismatch between what is originally served to the user's browser as HTML and what is expected by React during the rehydration process if the user has a `storedValue` that differs from the `initialValue`.\n\nThe suggested workaround for this issue is to only render the content dependent on the `storedValue` client-side by wrapping it in a [custom `<ClientOnly>` wrapper](https://www.joshwcomeau.com/react/the-perils-of-rehydration/#abstractions), which only renders after rehydration. Alternative solutions are technically possible, but would require a brief \"flicker\" as the `initialValue` is swapped to the `storedValue`, which can result in an unpleasant user experience, so this solution is advised.\n\n### Deleting an item from storage\n\nFor the case you want to delete an item from storage,\nthe atom created with `atomWithStorage` accepts the `RESET` symbol on write.\n\nSee the following example for the usage:\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithStorage, RESET } from 'jotai/utils'\n\nconst textAtom = atomWithStorage('text', 'hello')\n\nconst TextBox = () => {\n  const [text, setText] = useAtom(textAtom)\n\n  return (\n    <>\n      <input value={text} onChange={(e) => setText(e.target.value)} />\n      <button onClick={() => setText(RESET)}>Reset (to 'hello')</button>\n    </>\n  )\n}\n```\n\nIf needed, you can also do conditional resets based on previous value.\n\nThis can be particularly useful if you wish to clear keys in localStorage if previous values meet a condition.\n\nBelow exemplifies this usage that clears the `visible` key whenever the previous value is `true`.\n\n```jsx\nimport { useAtom } from 'jotai'\nimport { atomWithStorage, RESET } from 'jotai/utils'\n\nconst isVisibleAtom = atomWithStorage('visible', false)\n\nconst TextBox = () => {\n  const [isVisible, setIsVisible] = useAtom(isVisibleAtom)\n\n  return (\n    <>\n      { isVisible && <h1>Header is visible!</h1> }\n      <button onClick={() => setIsVisible((prev) => prev ? RESET : true))}>Toggle visible</button>\n    </>\n  )\n}\n```\n\n### React-Native implementation\n\nYou can use any library that implements `getItem`, `setItem` & `removeItem`.\nLet's say you would use the standard AsyncStorage provided by the community.\n\n```js\nimport { atomWithStorage, createJSONStorage } from 'jotai/utils'\nimport AsyncStorage from '@react-native-async-storage/async-storage'\n\nconst storage = createJSONStorage(() => AsyncStorage)\nconst content = {} // anything JSON serializable\nconst storedAtom = atomWithStorage('stored-key', content, storage)\n```\n\n**Note** set `getOnInit` to `true` if you want the atom to return the stored value immediately on initialization instead of the default value.\n\n**getOnInit true vs. false example**\n\n<details>\n\n```jsx\n// Assume localStorage already contains: { \"symbol\": \"BTC_USDC\" }\n\n// Without getOnInit (default behavior)\nconst symbolAtom = atomWithStorage('symbol', 'SOL_USDC')\n\nfunction App() {\n  const symbol = useAtomValue(symbolAtom)\n\n  useEffect(() => {\n    console.log('symbol', symbol)\n  }, [symbol])\n\n  return <div>{symbol}</div>\n}\n\n// Console output WITHOUT getOnInit:\n// LOG \"symbol\" SOL_USDC  (initial render)\n// LOG \"symbol\" BTC_USDC  (after storage loads)\n\n// With getOnInit set to true\nconst symbolAtom = atomWithStorage('symbol', 'SOL_USDC', undefined, {\n  getOnInit: true,\n})\n\nfunction App() {\n  const symbol = useAtomValue(symbolAtom)\n\n  useEffect(() => {\n    console.log('symbol', symbol)\n  }, [symbol])\n\n  return <div>{symbol}</div>\n}\n\n// Console output WITH getOnInit:\n// LOG \"symbol\" BTC_USDC  (gets stored value immediately)\n```\n\n</details>\n\n#### Notes with AsyncStorage (since v2.2.0)\n\nWith AsyncStorage (as with any asynchronous storage), the atom value becomes async.\nWhen updating the atom by referencing the current value, then you'll need to `await` it.\n\n```js\nconst countAtom = atomWithStorage('count-key', 0, anyAsyncStorage)\nconst Component = () => {\n  const [count, setCount] = useAtom(countAtom)\n  const increment = () => {\n    setCount(async (promiseOrValue) => (await promiseOrValue) + 1)\n  }\n  // ...\n}\n```\n\n### Validating stored values\n\nTo add runtime validation to your storage atoms, you will need to create a custom implementation of storage.\n\nBelow is an example that utilizes Zod to validate values stored in `localStorage` with cross-tab synchronization.\n\n```js\nimport { atomWithStorage } from 'jotai/utils'\nimport { z } from 'zod'\n\nconst myNumberSchema = z.number().int().nonnegative()\n\nconst storedNumberAtom = atomWithStorage('my-number', 0, {\n  getItem(key, initialValue) {\n    const storedValue = localStorage.getItem(key)\n    try {\n      return myNumberSchema.parse(JSON.parse(storedValue ?? ''))\n    } catch {\n      return initialValue\n    }\n  },\n  setItem(key, value) {\n    localStorage.setItem(key, JSON.stringify(value))\n  },\n  removeItem(key) {\n    localStorage.removeItem(key)\n  },\n  subscribe(key, callback, initialValue) {\n    if (\n      typeof window === 'undefined' ||\n      typeof window.addEventListener === 'undefined'\n    ) {\n      return\n    }\n    const handler = (e) => {\n      if (e.storageArea === localStorage && e.key === key) {\n        let newValue\n        try {\n          newValue = myNumberSchema.parse(JSON.parse(e.newValue ?? ''))\n        } catch {\n          newValue = initialValue\n        }\n        callback(newValue)\n      }\n    }\n    window.addEventListener('storage', handler)\n    return () => window.removeEventListener('storage', handler)\n  },\n})\n```\n\nWe also have a new util `unstable_withStorageValidator` to simplify some cases.\nThe above case would become:\n\n```js\nimport {\n  atomWithStorage,\n  createJSONStorage,\n  unstable_withStorageValidator as withStorageValidator,\n} from 'jotai/utils'\nimport { z } from 'zod'\n\nconst myNumberSchema = z.number().int().nonnegative()\nconst isMyNumber = (v) => myNumberSchema.safeParse(v).success\n\nconst storedNumberAtom = atomWithStorage(\n  'my-number',\n  0,\n  withStorageValidator(isMyNumber)(createJSONStorage()),\n)\n```\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    rules: {\n      'testing-library/no-unnecessary-act': 'off',\n    },\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: ['*.config.*'],\n    languageOptions: {\n      parserOptions: {\n        project: null,\n      },\n    },\n  },\n)\n"
  },
  {
    "path": "examples/hacker_news/README.md",
    "content": "# Hacker News [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/hacker_news)\n\n## Description\n\nDemonstrate a news article with jotai, hit next to see more articles.\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/hacker_news && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/hacker_news\n"
  },
  {
    "path": "examples/hacker_news/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    <meta name=\"theme-color\" content=\"#000000\" />\n    <title>Jotai Examples | Hacker News</title>\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/hacker_news/package.json",
    "content": "{\n  \"name\": \"hacker_news\",\n  \"version\": \"2.0.0\",\n  \"description\": \"Demonstrate a news articles with jotai, hit next to see more articles.\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@react-spring/web\": \"^9.2.3\",\n    \"html-react-parser\": \"^1.2.6\",\n    \"jotai\": \"^2.10.4\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/hacker_news/public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>Jotai Examples | Hacker News</title>\n  </head>\n\n  <body>\n    <noscript> You need to enable JavaScript to run this app. </noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "examples/hacker_news/src/App.tsx",
    "content": "import { Suspense } from 'react'\nimport { a, useSpring } from '@react-spring/web'\nimport Parser from 'html-react-parser'\nimport { Provider, atom, useAtom, useSetAtom } from 'jotai'\n\ntype PostData = {\n  by: string\n  descendants?: number\n  id: number\n  kids?: number[]\n  parent: number\n  score?: number\n  text?: string\n  time: number\n  title?: string\n  type: 'comment' | 'story'\n  url?: string\n}\n\nconst postId = atom(9001)\nconst postData = atom(async (get) => {\n  const id = get(postId)\n  const response = await fetch(\n    `https://hacker-news.firebaseio.com/v0/item/${id}.json`,\n  )\n  const data: PostData = await response.json()\n  return data\n})\n\nfunction Id() {\n  const [id] = useAtom(postId)\n  const props = useSpring({ from: { id }, id, reset: true })\n  return <a.h1>{props.id.to(Math.round)}</a.h1>\n}\n\nfunction Next() {\n  // Use `useSetAtom` to avoid re-render\n  // const [, setPostId] = useAtom(postId)\n  const setPostId = useSetAtom(postId)\n  return (\n    <button onClick={() => setPostId((id) => id + 1)}>\n      <div>→</div>\n    </button>\n  )\n}\n\nfunction PostTitle() {\n  const [{ by, text, time, title, url }] = useAtom(postData)\n  return (\n    <>\n      <h2>{by}</h2>\n      <h6>{new Date(time * 1000).toLocaleDateString('en-US')}</h6>\n      {title && <h4>{title}</h4>}\n      {url && <a href={url}>{url}</a>}\n      {text && <div>{Parser(text)}</div>}\n    </>\n  )\n}\n\nexport default function App() {\n  return (\n    <Provider>\n      <Id />\n      <div>\n        <Suspense fallback={<h2>Loading...</h2>}>\n          <PostTitle />\n        </Suspense>\n      </div>\n      <Next />\n    </Provider>\n  )\n}\n"
  },
  {
    "path": "examples/hacker_news/src/index.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport './styles.css'\nimport App from './App'\n\nconst rootElement = document.getElementById('root')\ncreateRoot(rootElement!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/hacker_news/src/styles.css",
    "content": "@import url('https://rsms.me/inter/inter.css');\n\n* {\n  box-sizing: border-box;\n  outline: none !important;\n}\n\nhtml,\nbody,\n#root {\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  background: white;\n  color: black;\n  font-family: 'Inter', sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n#root {\n  display: grid;\n  grid-template-columns: auto 1fr auto;\n}\n\nh1 {\n  writing-mode: tb-rl;\n  font-variant-numeric: tabular-nums;\n  font-weight: 700;\n  font-size: 10em;\n  letter-spacing: -10px;\n  text-align: left;\n  margin: 0;\n  padding: 50px 0px 0px 20px;\n}\n\nh2 {\n  margin-bottom: 0.2em;\n}\n\nh4 {\n  font-weight: 500;\n}\n\nh6 {\n  margin-top: 0;\n}\n\n#root > div {\n  padding: 50px 20px;\n  overflow: hidden;\n  word-wrap: break-word;\n  position: relative;\n}\n\n#root > div > div {\n  position: absolute;\n}\n\np {\n  color: #474747;\n}\n\nbutton {\n  text-decoration: none;\n  background: transparent;\n  border: none;\n  cursor: pointer;\n  font-family: 'Inter', sans-serif;\n  font-weight: 200;\n  font-size: 6em;\n  padding: 0px 30px 20px 0px;\n  display: flex;\n  align-items: flex-end;\n  color: inherit;\n}\n\nbutton:focus {\n  outline: 0;\n}\n\na {\n  color: inherit;\n}\n"
  },
  {
    "path": "examples/hacker_news/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/hacker_news/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\": [\"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/hacker_news/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/hello/README.md",
    "content": "# Hello [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/hello)\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/hello && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/hello\n"
  },
  {
    "path": "examples/hello/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    <link\n      href=\"https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css\"\n      rel=\"stylesheet\"\n    />\n    <meta name=\"description\" content=\"jotai examples\" />\n    <title>Jotai Examples | Hello</title>\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/hello/package.json",
    "content": "{\n  \"name\": \"hello\",\n  \"version\": \"2.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"jotai\": \"^2.10.4\",\n    \"prismjs\": \"^1.23.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-prism\": \"4.3.2\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/hello/public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <link\n      href=\"https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css\"\n      rel=\"stylesheet\"\n    />\n\n    <meta name=\"description\" content=\"jotai examples\" />\n    <title>Jotai Examples | Hello</title>\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/hello/src/App.tsx",
    "content": "import { atom, useAtom } from 'jotai'\n// @ts-ignore\nimport PrismCode from 'react-prism'\nimport 'prismjs'\nimport 'prismjs/components/prism-jsx.min'\n\nconst textAtom = atom<string>('hello')\nconst uppercaseAtom = atom((get) => get(textAtom).toUpperCase())\n\nconst Input = () => {\n  const [text, setText] = useAtom(textAtom)\n  return (\n    <input\n      className=\"bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded py-2 px-4 block w-full appearance-none leading-normal\"\n      value={text}\n      onChange={(e) => setText(e.target.value)}\n    />\n  )\n}\n\nconst Uppercase = () => {\n  const [uppercase] = useAtom(uppercaseAtom)\n  return <>{uppercase}</>\n}\n\nconst code = `import { atom, useAtom } from 'jotai'\n\n// Create your atoms and derivatives\nconst textAtom = atom('hello')\nconst uppercaseAtom = atom((get) => get(textAtom).toUpperCase())\n\n// Use them anywhere in your app\nconst Input = () => {\n  const [text, setText] = useAtom(textAtom)\n  return <input value={text} onChange={(e) => setText(e.target.value)} />\n}\n\nconst Uppercase = () => {\n  const [uppercase] = useAtom(uppercaseAtom)\n  return <div>Uppercase: {uppercase}</div>\n}\n\n// Now you have the components\nconst MyApp = () => (\n  <div>\n    <Input />\n    <Uppercase />\n  </div>\n)\n`\n\nconst App = () => (\n  <div>\n    <p>A simple example:</p>\n    <div>\n      <div className=\"py-8 text-sm\">\n        <div className=\"relative\">\n          <Input />\n          <div className=\"absolute top-0 right-0 h-full flex items-center mr-4 font-bold\">\n            <Uppercase />\n          </div>\n        </div>\n      </div>\n      <div>\n        <PrismCode component=\"pre\" className=\"language-jsx\" children={code} />\n      </div>\n    </div>\n  </div>\n)\n\nexport default App\n"
  },
  {
    "path": "examples/hello/src/index.tsx",
    "content": "import { createRoot } from 'react-dom/client'\nimport App from './App'\nimport './prism.css'\nimport './style.css'\n\nconst root = document.getElementById('root')\n\ncreateRoot(root!).render(\n  <div className=\"max-w-4xl mx-auto px-8 pb-8\">\n    <header className=\"mt-12 mb-12\">\n      <h1 className=\"mainlink text-6xl font-bold\">\n        <a href=\"https://github.com/pmndrs/jotai\">Jōtai</a>\n      </h1>\n      <h2\n        className=\"\n        text-2xl font-regular text-gray-400\n        lg:flex justify-between items-center\n        \"\n      >\n        <div>Primitive and flexible state management for React.</div>\n        <div className=\"text-xl font-regular text-gray-400\">状態 </div>\n      </h2>\n    </header>\n    <App />\n  </div>,\n)\n"
  },
  {
    "path": "examples/hello/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/hello/src/style.css",
    "content": "@import url('https://rsms.me/inter/inter.css');\nhtml {\n  font-family: 'Inter', sans-serif;\n}\n\n@supports (font-variation-settings: normal) {\n  html {\n    font-family: 'Inter var', sans-serif;\n  }\n}\n\n* {\n  box-sizing: border-box;\n}\n\n::selection {\n  background: #212121;\n  color: white;\n}\n\nhtml,\nbody {\n  overflow-x: hidden;\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\n@media screen and (min-width: 800px) {\n  pre {\n    width: 100% !important;\n    margin: 0 !important;\n  }\n}\n\npre > span:nth-child(11),\npre > span:nth-child(17),\npre > span:nth-child(20),\npre > span:nth-child(23),\npre > span:nth-child(44),\npre > span:nth-child(61),\npre > span:nth-child(78),\npre > span:nth-child(79) > span > span.class-name,\npre > span:nth-child(85) > span > span.class-name {\n  color: #ff7bab !important;\n}\n"
  },
  {
    "path": "examples/hello/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/hello/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\": [\"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/hello/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/mega-form/README.md",
    "content": "# Mega form [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/mega-form)\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/mega-form && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/mega-form\n"
  },
  {
    "path": "examples/mega-form/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    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n    <title>Jotai Examples | Mega Form</title>\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/mega-form/package.json",
    "content": "{\n  \"name\": \"mega-form\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"fp-ts\": \"^2.9.5\",\n    \"io-ts\": \"^2.2.15\",\n    \"jotai\": \"^2.10.4\",\n    \"jotai-optics\": \"^0.4.0\",\n    \"optics-ts\": \"^2.0.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/mega-form/public/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    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n    <title>Jotai Examples | Mega Form</title>\n  </head>\n\n  <body>\n    <div id=\"root\"></div>\n    <script src=\"./src/index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/mega-form/src/App.tsx",
    "content": "import { useCallback, useMemo } from 'react'\nimport { Provider, atom, useAtom } from 'jotai'\nimport type { PrimitiveAtom } from 'jotai'\nimport { focusAtom } from 'jotai-optics'\nimport { useAtomCallback } from 'jotai/utils'\nimport initialValue from './initialValue'\nimport useAtomSlice from './useAtomSlice'\n\nconst Field = ({\n  fieldAtom,\n  removeField,\n}: {\n  fieldAtom: PrimitiveAtom<{ name: string; value: string }>\n  removeField: () => void\n}) => {\n  const nameAtom = useMemo(\n    () => focusAtom(fieldAtom, (o) => o.prop('name')),\n    [fieldAtom],\n  )\n  const valueAtom = useMemo(\n    () => focusAtom(fieldAtom, (o) => o.prop('value')),\n    [fieldAtom],\n  )\n  const [name, setName] = useAtom(nameAtom)\n  const [value, setValue] = useAtom(valueAtom)\n  return (\n    <div>\n      <input\n        type=\"text\"\n        value={name}\n        onChange={(e) => setName(e.target.value)}\n      />\n      <input\n        type=\"text\"\n        value={value}\n        onChange={(e) => setValue(e.target.value)}\n      />\n      <button onClick={removeField}>X</button>\n    </div>\n  )\n}\n\nconst Form = ({\n  formAtom,\n  nameAtom,\n  remove,\n}: {\n  formAtom: PrimitiveAtom<Record<string, string>>\n  nameAtom: PrimitiveAtom<string>\n  remove: () => void\n}) => {\n  const objectsAtom = useMemo(\n    () =>\n      focusAtom(formAtom, (o) =>\n        o.iso(\n          (bigObject) =>\n            Object.entries(bigObject).map(([name, value]) => ({\n              name,\n              value,\n            })),\n          (arrayOfObjects) =>\n            Object.fromEntries(\n              arrayOfObjects.map(({ name, value }) => [name, value]),\n            ),\n        ),\n      ),\n    [formAtom],\n  )\n\n  const fieldAtoms = useAtomSlice(objectsAtom)\n  const [name, setName] = useAtom(nameAtom)\n  const addField = useAtomCallback(\n    useCallback(\n      (get, set) => {\n        const id = Math.floor(Math.random() * 1000)\n        set(objectsAtom, (oldValue) => [\n          ...oldValue,\n          { name: `new field ${id}`, value: '' },\n        ])\n      },\n      [objectsAtom],\n    ),\n  )\n\n  return (\n    <div>\n      <div>\n        <input value={name} onChange={(e) => setName(e.target.value)} />\n        <button onClick={remove}>Remove form</button>\n      </div>\n\n      <ul>\n        {fieldAtoms.map(([fieldAtom, remove]) => (\n          <li key={fieldAtom.toString()}>\n            <Field fieldAtom={fieldAtom} removeField={remove} />\n          </li>\n        ))}\n      </ul>\n      <button onClick={addField}>Add field</button>\n    </div>\n  )\n}\n\nconst FormList = ({\n  formListAtom,\n}: {\n  formListAtom: PrimitiveAtom<Record<string, Record<string, string>>>\n}) => {\n  const entriesAtom = useMemo(\n    () =>\n      focusAtom(formListAtom, (o) =>\n        o.iso(\n          (obj) => Object.entries(obj),\n          (array) => Object.fromEntries(array),\n        ),\n      ),\n    [formListAtom],\n  )\n  const formAtoms = useAtomSlice(entriesAtom)\n\n  const addForm = useAtomCallback(\n    useCallback(\n      (get, set) => {\n        const id = Math.floor(Math.random() * 1000)\n        set(entriesAtom, (oldValue) => [...oldValue, [`new form ${id}`, {}]])\n      },\n      [entriesAtom],\n    ),\n  )\n\n  const formValues = useMemo(() => {\n    return formAtoms.map(([formEntryAtom, remove]) => ({\n      nameAtom: focusAtom(formEntryAtom, (o) => o.nth(0)),\n      formAtom: focusAtom(formEntryAtom, (o) => o.nth(1)),\n      remove,\n    }))\n  }, [formAtoms])\n  return (\n    <ul>\n      {formValues.map(({ nameAtom, formAtom, remove }) => (\n        <li key={nameAtom.toString()}>\n          <Form nameAtom={nameAtom} formAtom={formAtom} remove={remove} />\n        </li>\n      ))}\n\n      <button onClick={addForm}>Add new form</button>\n    </ul>\n  )\n}\n\nconst formListAtom = atom(initialValue)\n\nexport default function App() {\n  return (\n    <Provider>\n      <h1>Mega form</h1>\n      <FormList formListAtom={formListAtom} />\n    </Provider>\n  )\n}\n"
  },
  {
    "path": "examples/mega-form/src/index.tsx",
    "content": "import { createRoot } from 'react-dom/client'\nimport App from './App'\nimport './style.css'\n\nconst rootElement = document.getElementById('root')\ncreateRoot(rootElement!).render(<App />)\n"
  },
  {
    "path": "examples/mega-form/src/initialValue.ts",
    "content": "const initialValue: Record<string, Record<string, string>> = {\n  form1: { task: 'Eat some food', checked: 'yeah' },\n  form2: { task: 'Eat some food', checked: 'yeah' },\n  form3: { task: 'Eat some food', checked: 'yeah' },\n  form4: { task: 'Eat some food', checked: 'yeah' },\n  form5: { task: 'Eat some food', checked: 'yeah' },\n  form6: { task: 'Eat some food', checked: 'yeah' },\n  form7: { task: 'Eat some food', checked: 'yeah' },\n  form8: { task: 'Eat some food', checked: 'yeah' },\n  form12: { task: 'Eat some food', checked: 'yeah' },\n  form22: { task: 'Eat some food', checked: 'yeah' },\n  form32: { task: 'Eat some food', checked: 'yeah' },\n  form42: { task: 'Eat some food', checked: 'yeah' },\n  form52: { task: 'Eat some food', checked: 'yeah' },\n  form62: { task: 'Eat some food', checked: 'yeah' },\n  form72: { task: 'Eat some food', checked: 'yeah' },\n  form82: { task: 'Eat some food', checked: 'yeah' },\n  form14: { task: 'Eat some food', checked: 'yeah' },\n  form24: { task: 'Eat some food', checked: 'yeah' },\n  form34: { task: 'Eat some food', checked: 'yeah' },\n  form44: { task: 'Eat some food', checked: 'yeah' },\n  form54: { task: 'Eat some food', checked: 'yeah' },\n  form64: { task: 'Eat some food', checked: 'yeah' },\n  form74: { task: 'Eat some food', checked: 'yeah' },\n  form84: { task: 'Eat some food', checked: 'yeah' },\n  form15: { task: 'Eat some food', checked: 'yeah' },\n  form25: { task: 'Eat some food', checked: 'yeah' },\n  form35: { task: 'Eat some food', checked: 'yeah' },\n  form45: { task: 'Eat some food', checked: 'yeah' },\n  form55: { task: 'Eat some food', checked: 'yeah' },\n  form65: { task: 'Eat some food', checked: 'yeah' },\n  form75: { task: 'Eat some food', checked: 'yeah' },\n  form85: { task: 'Eat some food', checked: 'yeah' },\n}\n\nexport default initialValue\n"
  },
  {
    "path": "examples/mega-form/src/style.css",
    "content": "html,\nbody {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\nbody {\n  color: #333;\n  margin: 0;\n  padding: 8px;\n  box-sizing: border-box;\n  font-family:\n    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu,\n    Cantarell, 'Helvetica Neue', sans-serif;\n}\n\na {\n  color: rgb(0, 100, 200);\n  text-decoration: none;\n}\n\na:hover {\n  text-decoration: underline;\n}\n\na:visited {\n  color: rgb(0, 80, 160);\n}\n\nlabel {\n  display: block;\n}\n\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  -webkit-padding: 0.4em 0;\n  padding: 0.4em;\n  margin: 0 0 0.5em 0;\n  box-sizing: border-box;\n  border: 1px solid #ccc;\n  border-radius: 2px;\n}\n\ninput:disabled {\n  color: #ccc;\n}\n\nbutton {\n  color: #333;\n  background-color: #f4f4f4;\n  outline: none;\n}\n\nbutton:disabled {\n  color: #999;\n}\n\nbutton:not(:disabled):active {\n  background-color: #ddd;\n}\n\nbutton:focus {\n  border-color: #666;\n}\n"
  },
  {
    "path": "examples/mega-form/src/useAtomSlice.ts",
    "content": "import { useMemo } from 'react'\nimport { useAtom } from 'jotai'\nimport type { PrimitiveAtom } from 'jotai'\nimport { splitAtom } from 'jotai/utils'\n\nconst useAtomSlice = <Item>(arrAtom: PrimitiveAtom<Item[]>) => {\n  const [atoms, dispatch] = useAtom(\n    useMemo(() => splitAtom(arrAtom), [arrAtom]),\n  )\n  return useMemo(\n    () =>\n      atoms.map(\n        (itemAtom) =>\n          [\n            itemAtom,\n            () => dispatch({ type: 'remove', atom: itemAtom }),\n          ] as const,\n      ),\n    [atoms, dispatch],\n  )\n}\n\nexport default useAtomSlice\n"
  },
  {
    "path": "examples/mega-form/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/mega-form/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\": [\"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/mega-form/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\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/jotai/tree/main/examples/starter)\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && 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/jotai/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=\"jotai examples\" />\n    <title>Jotai 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    \"jotai\": \"^2.10.4\",\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\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\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: hsl(0, 0%, 4%);\n}\n"
  },
  {
    "path": "examples/starter/src/index.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { atom, useAtom } from 'jotai'\n\nimport mascot from './assets/jotai-mascot.png'\n\nimport './index.css'\n\nconst countAtom = atom(0)\n\nconst Counter = () => {\n  const [count, setCount] = useAtom(countAtom)\n  const inc = () => setCount((c) => c + 1)\n\n  return (\n    <>\n      <span className=\"text-3xl\">{count}</span>\n      <button\n        className=\"bg-white text-black font-bold py-2 px-4 rounded\"\n        onClick={inc}\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://jotai.org/\" target=\"_blank\" rel=\"noreferrer\">\n        <img\n          src={mascot}\n          alt=\"Jotai mascot\"\n          className=\"w-32\"\n          style={{\n            filter: 'drop-shadow(0 0 2em #bfdbfe)',\n          }}\n        />\n      </a>\n\n      <h1 className=\"text-5xl font-bold\">Jotai 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'\nimport { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/text_length/README.md",
    "content": "# Text Length [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/text_length)\n\n## Description\n\nCount the length and show the uppercase of any text.\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/text_length && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/text_length\n"
  },
  {
    "path": "examples/text_length/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>Jotai Examples | Text Length</title>\n    <style>\n      body {\n        margin: 0;\n      }\n      .container {\n        display: grid;\n        grid-template-rows: 0px 0px 1fr;\n        grid-template-columns: 0px 1fr 0px 0px;\n      }\n      /* Rest of existing styles... */\n    </style>\n  </head>\n  <body>\n    <div class=\"container\">\n      <div class=\"item-left\">\n        <div><div>REACT SPRING</div></div>\n      </div>\n      <div class=\"item-top\">\n        <a href=\"https://github.com/react-spring/jotai\" target=\"_blank\"\n          >GitHub</a\n        >\n      </div>\n      <div class=\"item-logo\"><img src=\"/cover.svg\" alt=\"Logo\" /></div>\n      <div class=\"item-demo\" id=\"root\"></div>\n      <div class=\"item-code\"><img src=\"/snippet.png\" alt=\"Code\" /></div>\n      <div class=\"item-right\"><img src=\"/castle.jpg\" alt=\"Castle\" /></div>\n    </div>\n    <script type=\"module\" src=\"/src/index.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/text_length/package.json",
    "content": "{\n  \"name\": \"text_length\",\n  \"version\": \"2.0.0\",\n  \"description\": \"Count the length and show the uppercase of any text\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"jotai\": \"^2.10.4\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/text_length/public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>Jotai Examples | Text Length</title>\n    <style>\n      body {\n        margin: 0;\n      }\n      .container {\n        display: grid;\n        grid-template-rows: 0px 0px 1fr;\n        grid-template-columns: 0px 1fr 0px 0px;\n      }\n      @media screen and (min-width: 800px) {\n        .container {\n          width: 100vw;\n          height: 100vh;\n          display: grid;\n          grid-template-rows: 1fr 320px 240px;\n          grid-template-columns: 1fr 170px 350px 2fr;\n        }\n      }\n      .item-left {\n        grid-row: 2 / span 2;\n        grid-column: 1;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        overflow: hidden;\n      }\n      .item-left > div {\n        transform: rotate(-90deg);\n      }\n      .item-left > div > div {\n        width: 12rem;\n        text-align: center;\n      }\n      .item-top {\n        grid-row: 1;\n        grid-column: 1 / span 4;\n        display: flex;\n        align-items: center;\n        justify-content: flex-end;\n        overflow: hidden;\n      }\n      .item-top a {\n        margin: 8px;\n        text-decoration: none;\n      }\n      .item-logo {\n        grid-row: 2;\n        grid-column: 2 / span 2;\n        overflow: hidden;\n      }\n      .item-logo img {\n        transform: translateY(-30px);\n        height: 470px;\n      }\n      .item-demo {\n        grid-row: 3;\n        grid-column: 2;\n        margin: 8px;\n      }\n      .item-code {\n        grid-row: 3;\n        grid-column: 3;\n        position: relative;\n      }\n      .item-code img {\n        position: absolute;\n        left: 0;\n        bottom: 0;\n        height: 240px;\n        transition-duration: 800ms;\n      }\n      .item-code img:hover {\n        height: 360px;\n        transition-duration: 800ms;\n      }\n      .item-right {\n        grid-row: 2 / span 2;\n        grid-column: 4;\n        overflow: hidden;\n      }\n      .item-right img {\n        object-fit: cover;\n        width: 100%;\n        height: 100%;\n        margin-left: 16px;\n      }\n    </style>\n  </head>\n\n  <body>\n    <noscript> You need to enable JavaScript to run this app. </noscript>\n    <div class=\"container\">\n      <div class=\"item-left\">\n        <div><div>REACT SPRING</div></div>\n      </div>\n      <div class=\"item-top\">\n        <a href=\"https://github.com/react-spring/jotai\" target=\"_blank\"\n          >GitHub</a\n        >\n      </div>\n      <div class=\"item-logo\"><img src=\"cover.svg\" alt=\"Logo\" /></div>\n      <div class=\"item-demo\" id=\"root\"></div>\n      <div class=\"item-code\"><img src=\"snippet.png\" alt=\"Code\" /></div>\n      <div class=\"item-right\"><img src=\"castle.jpg\" alt=\"Castle\" /></div>\n    </div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "examples/text_length/src/App.tsx",
    "content": "import { Provider, atom, useAtom } from 'jotai'\n\nconst textAtom = atom('hello')\nconst textLenAtom = atom((get) => get(textAtom).length)\nconst uppercaseAtom = atom((get) => get(textAtom).toUpperCase())\n\nconst Input = () => {\n  const [text, setText] = useAtom(textAtom)\n  return <input value={text} onChange={(e) => setText(e.target.value)} />\n}\n\nconst CharCount = () => {\n  const [len] = useAtom(textLenAtom)\n  return <div>Length: {len}</div>\n}\n\nconst Uppercase = () => {\n  const [uppercase] = useAtom(uppercaseAtom)\n  return <div>Uppercase: {uppercase}</div>\n}\n\nconst App = () => (\n  <Provider>\n    <Input />\n    <CharCount />\n    <Uppercase />\n  </Provider>\n)\n\nexport default App\n"
  },
  {
    "path": "examples/text_length/src/index.tsx",
    "content": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport App from './App'\n\nconst rootElement = document.getElementById('root')\ncreateRoot(rootElement!).render(\n  <StrictMode>\n    <App />\n  </StrictMode>,\n)\n"
  },
  {
    "path": "examples/text_length/src/react-app-env.d.ts",
    "content": "/// <reference types=\"react-scripts\" />\n"
  },
  {
    "path": "examples/text_length/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\": [\"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/text_length/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/todos/README.md",
    "content": "# Todos [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/todos)\n\n## Description\n\nRecord your todo list by typing them into this app, check them if you have completed the task, and switch between `Completed` and `Incompleted` to see the status of your task.\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/todos && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/todos\n"
  },
  {
    "path": "examples/todos/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    <meta name=\"theme-color\" content=\"#000000\" />\n    <title>Jotai Examples | Todos</title>\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/todos/package.json",
    "content": "{\n  \"name\": \"todos\",\n  \"version\": \"2.0.0\",\n  \"description\": \"Record your todo list by typing them into this app\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.5.2\",\n    \"@react-spring/web\": \"^9.2.3\",\n    \"antd\": \"^4.16.2\",\n    \"jotai\": \"^2.10.4\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/todos/public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>Jotai Examples | Todos</title>\n  </head>\n\n  <body>\n    <noscript> You need to enable JavaScript to run this app. </noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "examples/todos/src/App.tsx",
    "content": "import type { FormEvent } from 'react'\nimport { CloseOutlined } from '@ant-design/icons'\nimport { a, useTransition } from '@react-spring/web'\nimport { Radio } from 'antd'\nimport { Provider, atom, useAtom, useSetAtom } from 'jotai'\nimport type { PrimitiveAtom } from 'jotai'\n\ntype Todo = {\n  title: string\n  completed: boolean\n}\n\nconst filterAtom = atom('all')\nconst todosAtom = atom<PrimitiveAtom<Todo>[]>([])\nconst filteredAtom = atom<PrimitiveAtom<Todo>[]>((get) => {\n  const filter = get(filterAtom)\n  const todos = get(todosAtom)\n  if (filter === 'all') return todos\n  else if (filter === 'completed')\n    return todos.filter((atom) => get(atom).completed)\n  else return todos.filter((atom) => !get(atom).completed)\n})\n\ntype RemoveFn = (item: PrimitiveAtom<Todo>) => void\ntype TodoItemProps = {\n  atom: PrimitiveAtom<Todo>\n  remove: RemoveFn\n}\nconst TodoItem = ({ atom, remove }: TodoItemProps) => {\n  const [item, setItem] = useAtom(atom)\n  const toggleCompleted = () =>\n    setItem((props) => ({ ...props, completed: !props.completed }))\n  return (\n    <>\n      <input\n        type=\"checkbox\"\n        checked={item.completed}\n        onChange={toggleCompleted}\n      />\n      <span style={{ textDecoration: item.completed ? 'line-through' : '' }}>\n        {item.title}\n      </span>\n      <CloseOutlined onClick={() => remove(atom)} />\n    </>\n  )\n}\n\nconst Filter = () => {\n  const [filter, set] = useAtom(filterAtom)\n  return (\n    <Radio.Group onChange={(e) => set(e.target.value)} value={filter}>\n      <Radio value=\"all\">All</Radio>\n      <Radio value=\"completed\">Completed</Radio>\n      <Radio value=\"incompleted\">Incompleted</Radio>\n    </Radio.Group>\n  )\n}\n\ntype FilteredType = {\n  remove: RemoveFn\n}\nconst Filtered = (props: FilteredType) => {\n  const [todos] = useAtom(filteredAtom)\n  const transitions = useTransition(todos, {\n    keys: (todo) => todo.toString(),\n    from: { opacity: 0, height: 0 },\n    enter: { opacity: 1, height: 40 },\n    leave: { opacity: 0, height: 0 },\n  })\n  return transitions((style, atom) => (\n    <a.div className=\"item\" style={style}>\n      <TodoItem atom={atom} {...props} />\n    </a.div>\n  ))\n}\n\nconst TodoList = () => {\n  // Use `useSetAtom` to avoid re-render\n  // const [, setTodos] = useAtom(todosAtom)\n  const setTodos = useSetAtom(todosAtom)\n  const remove: RemoveFn = (todo) =>\n    setTodos((prev) => prev.filter((item) => item !== todo))\n  const add = (e: FormEvent<HTMLFormElement>) => {\n    e.preventDefault()\n    const title = e.currentTarget.inputTitle.value\n    e.currentTarget.inputTitle.value = ''\n    setTodos((prev) => [...prev, atom<Todo>({ title, completed: false })])\n  }\n  return (\n    <form onSubmit={add}>\n      <Filter />\n      <input name=\"inputTitle\" placeholder=\"Type ...\" />\n      <Filtered remove={remove} />\n    </form>\n  )\n}\n\nexport default function App() {\n  return (\n    <Provider>\n      <h1>Jōtai</h1>\n      <TodoList />\n    </Provider>\n  )\n}\n"
  },
  {
    "path": "examples/todos/src/index.tsx",
    "content": "import { createRoot } from 'react-dom/client'\nimport 'antd/dist/antd.css'\nimport './styles.css'\nimport App from './App'\n\nconst rootElement = document.getElementById('root')\ncreateRoot(rootElement!).render(<App />)\n"
  },
  {
    "path": "examples/todos/src/styles.css",
    "content": "@import url('https://rsms.me/inter/inter.css');\n\n* {\n  box-sizing: border-box;\n}\n\nhtml,\nbody {\n  width: 100%;\n  height: 100%;\n}\n\nbody {\n  margin-top: 5em;\n  display: flex;\n  align-items: flex-start;\n  justify-content: center;\n  background: #fdfdfd;\n  font-family: 'Inter', sans-serif !important;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  filter: saturate(0);\n}\n\n#root {\n  width: 50ch;\n  display: flex;\n  flex-direction: column;\n  gap: 1em;\n}\n\ninput:not([type='checkbox']) {\n  width: 100%;\n  border: none;\n  box-shadow: 0px 15px 30px rgba(0, 0, 0, 0.05);\n  padding: 10px 20px;\n  margin-top: 2em;\n  margin-bottom: 4em;\n  background: white;\n}\n\ninput:focus {\n  outline: none;\n}\n\n.anticon-close {\n  width: 32px !important;\n  cursor: pointer;\n  color: #c0c0c0;\n}\n\n.anticon-close:hover {\n  color: #272730;\n}\n\n.item {\n  position: relative;\n  display: flex;\n  width: 100%;\n  align-items: center;\n  justify-content: space-between;\n  gap: 20px;\n  overflow: hidden;\n}\n\n.item > span {\n  display: inline-block;\n  width: 100%;\n}\n\nh1 {\n  font-size: 10em;\n  font-weight: 800;\n  margin: 0;\n  padding: 0;\n  letter-spacing: -5px;\n  color: black;\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "examples/todos/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/todos/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  },\n  \"include\": [\"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/todos/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "examples/todos_with_atomFamily/README.md",
    "content": "# Todos with atomFamily [![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?style=flat-square&logo=stackblitz)](https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/todos_with_atomFamily)\n\n> **⚠️ Note:** `atomFamily` from `jotai/utils` is deprecated and will be removed in v3. For new projects, please use the [`jotai-family`](https://github.com/jotaijs/jotai-family) package instead.\n\n## Description\n\nImplement a todo list using atomFamily and localStorage, you can store your todo list to localStorage by click `Save to localStorage`, then remove your todo list and restore them by click `Load from localStorage`.\n\n## Set up locally\n\n```bash\ngit clone https://github.com/pmndrs/jotai\n\n# install project dependencies & build the library\ncd jotai && pnpm install\n\n# move to the examples folder & install dependencies\ncd examples/todos_with_atomFamily && pnpm install\n\n# start the dev server\npnpm dev\n```\n\n## Set up on `StackBlitz`\n\nLink: https://stackblitz.com/github/pmndrs/jotai/tree/main/examples/todos_with_atomFamily\n"
  },
  {
    "path": "examples/todos_with_atomFamily/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    <meta name=\"theme-color\" content=\"#000000\" />\n    <title>Jotai Examples | Todos with atomFamily</title>\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/todos_with_atomFamily/package.json",
    "content": "{\n  \"name\": \"todos_with_atomFamily\",\n  \"version\": \"2.0.0\",\n  \"description\": \"Implement a todo list using atomFamily and localStorage\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"serve\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.5.2\",\n    \"@react-spring/web\": \"^9.2.3\",\n    \"antd\": \"^4.16.2\",\n    \"jotai\": \"^2.10.4\",\n    \"nanoid\": \"^3.1.23\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"typescript\": \"^5.0.0\",\n    \"vite\": \"^6.0.5\"\n  }\n}\n"
  },
  {
    "path": "examples/todos_with_atomFamily/public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>Jotai Examples | Todos with atomFamily</title>\n  </head>\n\n  <body>\n    <noscript> You need to enable JavaScript to run this app. </noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "examples/todos_with_atomFamily/src/App.tsx",
    "content": "import type { FormEvent } from 'react'\nimport { CloseOutlined } from '@ant-design/icons'\nimport { a, useTransition } from '@react-spring/web'\nimport { Radio } from 'antd'\nimport { Provider, atom, useAtom, useSetAtom } from 'jotai'\nimport { atomFamily } from 'jotai/utils'\nimport { nanoid } from 'nanoid'\n\ntype Param = { id: string; title?: string }\nconst todoAtomFamily = atomFamily(\n  (param: Param) =>\n    atom({ title: param.title || 'No title', completed: false }),\n  (a: Param, b: Param) => a.id === b.id,\n)\n\nconst filterAtom = atom('all')\nconst todosAtom = atom<string[]>([])\nconst filteredAtom = atom((get) => {\n  const filter = get(filterAtom)\n  const todos = get(todosAtom)\n  if (filter === 'all') return todos\n  else if (filter === 'completed')\n    return todos.filter((id) => get(todoAtomFamily({ id })).completed)\n  else return todos.filter((id) => !get(todoAtomFamily({ id })).completed)\n})\n\nconst TodoItem = ({\n  id,\n  remove,\n}: {\n  id: string\n  remove: (id: string) => void\n}) => {\n  const [item, setItem] = useAtom(todoAtomFamily({ id }))\n  const toggleCompleted = () => setItem({ ...item, completed: !item.completed })\n  return (\n    <>\n      <input\n        type=\"checkbox\"\n        checked={item.completed}\n        onChange={toggleCompleted}\n      />\n      <span style={{ textDecoration: item.completed ? 'line-through' : '' }}>\n        {item.title}\n      </span>\n      <CloseOutlined onClick={() => remove(id)} />\n    </>\n  )\n}\n\nconst Filter = () => {\n  const [filter, set] = useAtom(filterAtom)\n  return (\n    <Radio.Group onChange={(e) => set(e.target.value)} value={filter}>\n      <Radio value=\"all\">All</Radio>\n      <Radio value=\"completed\">Completed</Radio>\n      <Radio value=\"incompleted\">Incompleted</Radio>\n    </Radio.Group>\n  )\n}\n\nconst Filtered = ({ remove }: { remove: (id: string) => void }) => {\n  const [todos] = useAtom(filteredAtom)\n  const transitions = useTransition(todos, {\n    keys: (id: string) => id,\n    from: { opacity: 0, height: 0 },\n    enter: { opacity: 1, height: 40 },\n    leave: { opacity: 0, height: 0 },\n  })\n  return transitions((style, id) => (\n    <a.div className=\"item\" style={style}>\n      <TodoItem id={id} remove={remove} />\n    </a.div>\n  ))\n}\n\nconst TodoList = () => {\n  // Use `useSetAtom` to avoid re-render\n  // const [, setTodos] = useAtom(todosAtom)\n  const setTodos = useSetAtom(todosAtom)\n  const remove = (id: string) => {\n    setTodos((prev) => prev.filter((item) => item !== id))\n    todoAtomFamily.remove({ id })\n  }\n  const add = (e: FormEvent<HTMLFormElement>) => {\n    e.preventDefault()\n    const title = e.currentTarget.inputTitle.value\n    e.currentTarget.inputTitle.value = ''\n    const id = nanoid()\n    todoAtomFamily({ id, title })\n    setTodos((prev) => [...prev, id])\n  }\n  return (\n    <form onSubmit={add}>\n      <Filter />\n      <input name=\"inputTitle\" placeholder=\"Type ...\" />\n      <Filtered remove={remove} />\n    </form>\n  )\n}\n\ntype Action =\n  | { type: 'serialize'; callback: (value: string) => void }\n  | { type: 'deserialize'; value: string }\n\nconst serializeAtom = atom(null, (get, set, action: Action) => {\n  if (action.type === 'serialize') {\n    const todos = get(todosAtom)\n    const todoMap: Record<string, { title: string; completed: boolean }> = {}\n    todos.forEach((id) => {\n      todoMap[id] = get(todoAtomFamily({ id }))\n    })\n    const obj = {\n      todos,\n      todoMap,\n      filter: get(filterAtom),\n    }\n    action.callback(JSON.stringify(obj))\n  } else if (action.type === 'deserialize') {\n    const obj = JSON.parse(action.value)\n    // needs error handling and type checking\n    set(filterAtom, obj.filter)\n    obj.todos.forEach((id: string) => {\n      const todo = obj.todoMap[id]\n      set(todoAtomFamily({ id, ...todo }), todo)\n    })\n    set(todosAtom, obj.todos)\n  }\n})\n\nconst Persist = () => {\n  const [, dispatch] = useAtom(serializeAtom)\n  const save = () => {\n    dispatch({\n      type: 'serialize',\n      callback: (value) => {\n        localStorage.setItem('serializedTodos', value)\n      },\n    })\n  }\n  const load = () => {\n    const value = localStorage.getItem('serializedTodos')\n    if (value) {\n      dispatch({ type: 'deserialize', value })\n    }\n  }\n  return (\n    <div>\n      <button onClick={save}>Save to localStorage</button>\n      <button onClick={load}>Load from localStorage</button>\n    </div>\n  )\n}\n\nexport default function App() {\n  return (\n    <Provider>\n      <h1>Jōtai</h1>\n      <TodoList />\n      <h3>Persist</h3>\n      <Persist />\n    </Provider>\n  )\n}\n"
  },
  {
    "path": "examples/todos_with_atomFamily/src/index.tsx",
    "content": "import { createRoot } from 'react-dom/client'\nimport 'antd/dist/antd.css'\nimport './styles.css'\nimport App from './App'\n\nconst rootElement = document.getElementById('root')\ncreateRoot(rootElement!).render(<App />)\n"
  },
  {
    "path": "examples/todos_with_atomFamily/src/styles.css",
    "content": "@import url('https://rsms.me/inter/inter.css');\n\n* {\n  box-sizing: border-box;\n}\n\nhtml,\nbody {\n  width: 100%;\n  height: 100%;\n}\n\nbody {\n  margin-top: 5em;\n  display: flex;\n  align-items: flex-start;\n  justify-content: center;\n  background: #fdfdfd;\n  font-family: 'Inter', sans-serif !important;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  filter: saturate(0);\n}\n\n#root {\n  width: 50ch;\n  display: flex;\n  flex-direction: column;\n  gap: 1em;\n}\n\ninput:not([type='checkbox']) {\n  width: 100%;\n  border: none;\n  box-shadow: 0px 15px 30px rgba(0, 0, 0, 0.05);\n  padding: 10px 20px;\n  margin-top: 2em;\n  margin-bottom: 4em;\n  background: white;\n}\n\ninput:focus {\n  outline: none;\n}\n\n.anticon-close {\n  width: 32px !important;\n  cursor: pointer;\n  color: #c0c0c0;\n}\n\n.anticon-close:hover {\n  color: #272730;\n}\n\n.item {\n  position: relative;\n  display: flex;\n  width: 100%;\n  align-items: center;\n  justify-content: space-between;\n  gap: 20px;\n  overflow: hidden;\n}\n\n.item > span {\n  display: inline-block;\n  width: 100%;\n}\n\nh1 {\n  font-size: 10em;\n  font-weight: 800;\n  margin: 0;\n  padding: 0;\n  letter-spacing: -5px;\n  color: black;\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "examples/todos_with_atomFamily/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/todos_with_atomFamily/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\": [\"./src/**/*\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/todos_with_atomFamily/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n  plugins: [react()],\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"jotai\",\n  \"description\": \"👻 Primitive and flexible state management for React\",\n  \"private\": true,\n  \"type\": \"commonjs\",\n  \"version\": \"2.18.1\",\n  \"main\": \"./index.js\",\n  \"types\": \"./index.d.ts\",\n  \"typesVersions\": {\n    \">=4.8\": {\n      \"esm/*\": [\n        \"esm/*\"\n      ],\n      \"*\": [\n        \"*\"\n      ]\n    },\n    \">=3.8\": {\n      \"esm/*\": [\n        \"ts3.8/*\"\n      ],\n      \"*\": [\n        \"ts3.8/*\"\n      ]\n    },\n    \"*\": {\n      \"esm/*\": [\n        \"ts_version_3.8_and_above_is_required.d.ts\"\n      ],\n      \"*\": [\n        \"ts_version_3.8_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:babel:plugin-debug-label\": \"rollup -c --config-babel_plugin-debug-label\",\n    \"build:babel:plugin-react-refresh\": \"rollup -c --config-babel_plugin-react-refresh\",\n    \"build:babel:preset\": \"rollup -c --config-babel_preset\",\n    \"build:vanilla\": \"rollup -c --config-vanilla\",\n    \"build:vanilla:utils\": \"rollup -c --config-vanilla_utils\",\n    \"build:vanilla:internals\": \"rollup -c --config-vanilla_internals\",\n    \"build:react\": \"rollup -c --config-react --client-only\",\n    \"build:react:utils\": \"rollup -c --config-react_utils --client-only\",\n    \"postbuild\": \"pnpm run patch-d-ts && pnpm run copy && pnpm run patch-ts3.8 && pnpm run patch-old-ts && pnpm run patch-esm-ts && pnpm run patch-readme\",\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 && shx rm -rf dist/{src,tests} && downlevel-dts dist dist/ts3.8 --to=3.8 && 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-ts3.8\": \"node -e \\\"require('shelljs').find('dist/ts3.8/**/*.d.ts').forEach(f=>require('fs').appendFileSync(f,'declare type Awaited<T> = T extends Promise<infer V> ? V : T;'))\\\"\",\n    \"patch-old-ts\": \"shx touch dist/ts_version_3.8_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    \"patch-readme\": \"shx sed -i 's/.*Jotai \\\\(dark mode\\\\).*//' dist/README.md\"\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/jotai.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"state\",\n    \"manager\",\n    \"management\",\n    \"recoil\",\n    \"store\"\n  ],\n  \"author\": \"Daishi Kato\",\n  \"contributors\": [],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/pmndrs/jotai/issues\"\n  },\n  \"homepage\": \"https://github.com/pmndrs/jotai\",\n  \"packageManager\": \"pnpm@10.18.3\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.29.0\",\n    \"@babel/plugin-transform-react-jsx\": \"^7.28.6\",\n    \"@babel/plugin-transform-typescript\": \"^7.28.6\",\n    \"@babel/preset-env\": \"^7.29.0\",\n    \"@babel/template\": \"^7.28.6\",\n    \"@eslint/js\": \"^9.39.2\",\n    \"@rollup/plugin-alias\": \"^6.0.0\",\n    \"@rollup/plugin-babel\": \"^7.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^16.0.3\",\n    \"@rollup/plugin-replace\": \"^6.0.3\",\n    \"@rollup/plugin-terser\": \"^0.4.4\",\n    \"@rollup/plugin-typescript\": \"^12.3.0\",\n    \"@testing-library/dom\": \"^10.4.1\",\n    \"@testing-library/jest-dom\": \"^6.9.1\",\n    \"@testing-library/react\": \"^16.3.2\",\n    \"@types/babel__core\": \"^7.20.5\",\n    \"@types/babel__template\": \"^7.4.4\",\n    \"@types/node\": \"^25.3.5\",\n    \"@types/react\": \"^19.2.14\",\n    \"@types/react-dom\": \"^19.2.3\",\n    \"@vitejs/plugin-react\": \"^5.1.4\",\n    \"@vitest/coverage-v8\": \"^4.0.18\",\n    \"@vitest/eslint-plugin\": \"^1.6.9\",\n    \"@vitest/ui\": \"^4.0.18\",\n    \"benny\": \"^3.7.1\",\n    \"downlevel-dts\": \"^0.11.0\",\n    \"esbuild\": \"^0.27.3\",\n    \"eslint\": \"^9.39.2\",\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    \"prettier\": \"^3.8.1\",\n    \"react\": \"^19.2.4\",\n    \"react-dom\": \"^19.2.4\",\n    \"rollup\": \"^4.59.0\",\n    \"rollup-plugin-banner2\": \"^1.3.1\",\n    \"rollup-plugin-esbuild\": \"^6.2.1\",\n    \"rxjs\": \"^7.8.2\",\n    \"shelljs\": \"^0.10.0\",\n    \"shx\": \"^0.4.0\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.56.1\",\n    \"vitest\": \"^4.0.18\",\n    \"wonka\": \"^6.3.5\"\n  },\n  \"peerDependencies\": {\n    \"@babel/core\": \">=7.0.0\",\n    \"@babel/template\": \">=7.0.0\",\n    \"@types/react\": \">=17.0.0\",\n    \"react\": \">=17.0.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@babel/core\": {\n      \"optional\": true\n    },\n    \"@babel/template\": {\n      \"optional\": true\n    },\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\nminimumReleaseAgeExclude:\n  - 'react'\n  - 'react-dom'\n  - 'scheduler'\n"
  },
  {
    "path": "rollup.config.mjs",
    "content": "/*global process*/\nimport path from 'path'\nimport alias from '@rollup/plugin-alias'\nimport babelPlugin from '@rollup/plugin-babel'\nimport resolve from '@rollup/plugin-node-resolve'\nimport replace from '@rollup/plugin-replace'\nimport terser from '@rollup/plugin-terser'\nimport typescript from '@rollup/plugin-typescript'\nimport banner2 from 'rollup-plugin-banner2'\nimport esbuild from 'rollup-plugin-esbuild'\nimport createBabelConfig from './babel.config.mjs'\n\nconst extensions = ['.js', '.ts', '.tsx']\nconst { root } = path.parse(process.cwd())\nexport const entries = [\n  { find: /.*\\/vanilla\\/utils\\.ts$/, replacement: 'jotai/vanilla/utils' },\n  { find: /.*\\/internals\\.ts$/, replacement: 'jotai/vanilla/internals' },\n  { find: /.*\\/react\\/utils\\.ts$/, replacement: 'jotai/react/utils' },\n  { find: /.*\\/vanilla\\.ts$/, replacement: 'jotai/vanilla' },\n  { find: /.*\\/react\\.ts$/, replacement: 'jotai/react' },\n]\n\nfunction external(id) {\n  return !id.startsWith('.') && !id.startsWith(root)\n}\n\nconst cscComment = `'use client';\\n`\n\nfunction getBabelOptions(targets) {\n  return {\n    ...createBabelConfig({ env: (env) => env === 'build' }, targets),\n    extensions,\n    comments: false,\n    babelHelpers: 'bundled',\n  }\n}\n\nfunction getEsbuild(env = 'development') {\n  return esbuild({\n    minify: env === 'production',\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, clientOnly) {\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      banner2(() => clientOnly && cscComment),\n    ],\n  }\n}\n\nfunction createCommonJSConfig(input, output, clientOnly) {\n  return {\n    input,\n    output: { file: `${output}.js`, 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      babelPlugin(getBabelOptions({ ie: 11 })),\n      banner2(() => clientOnly && cscComment),\n    ],\n  }\n}\n\nfunction createUMDConfig(input, output, env, clientOnly) {\n  let name = 'jotai'\n  const fileName = output.slice('dist/umd/'.length)\n  const capitalize = (str) => str.slice(0, 1).toUpperCase() + str.slice(1)\n  if (fileName !== 'index') {\n    name += fileName.replace(/(\\w+)\\W*/g, (_, p) => capitalize(p))\n  }\n  return {\n    input,\n    output: {\n      file: `${output}.${env}.js`,\n      format: 'umd',\n      name,\n      globals: {\n        react: 'React',\n        'jotai/vanilla': 'jotaiVanilla',\n        'jotai/utils': 'jotaiUtils',\n        'jotai/react': 'jotaiReact',\n        'jotai/vanilla/utils': 'jotaiVanillaUtils',\n        'jotai/vanilla/internals': 'jotaiVanillaInternals',\n        'jotai/react/utils': 'jotaiReactUtils',\n      },\n    },\n    external,\n    plugins: [\n      alias({ entries: entries.filter((entry) => !entry.find.test(input)) }),\n      resolve({ extensions }),\n      replace({\n        'import.meta.env?.MODE': JSON.stringify(env),\n        delimiters: ['\\\\b', '\\\\b(?!(\\\\.|/))'],\n        preventAssignment: true,\n      }),\n      babelPlugin(getBabelOptions({ ie: 11 })),\n      banner2(() => clientOnly && cscComment),\n      ...(env === 'production' ? [terser()] : []),\n    ],\n  }\n}\n\nfunction createSystemConfig(input, output, env, clientOnly) {\n  return {\n    input,\n    output: {\n      file: `${output}.${env}.js`,\n      format: 'system',\n    },\n    external,\n    plugins: [\n      alias({ entries: entries.filter((entry) => !entry.find.test(input)) }),\n      resolve({ extensions }),\n      replace({\n        'import.meta.env?.MODE': JSON.stringify(env),\n        delimiters: ['\\\\b', '\\\\b(?!(\\\\.|/))'],\n        preventAssignment: true,\n      }),\n      getEsbuild(env),\n      banner2(() => clientOnly && cscComment),\n    ],\n  }\n}\n\nexport default function (args) {\n  let c = Object.keys(args).find((key) => key.startsWith('config-'))\n  const clientOnly = Object.keys(args).some((key) => key === 'client-only')\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}`, clientOnly),\n    createESMConfig(`src/${c}.ts`, `dist/esm/${c}.mjs`, clientOnly),\n    createUMDConfig(`src/${c}.ts`, `dist/umd/${c}`, 'development', clientOnly),\n    createUMDConfig(`src/${c}.ts`, `dist/umd/${c}`, 'production', clientOnly),\n    createSystemConfig(\n      `src/${c}.ts`,\n      `dist/system/${c}`,\n      'development',\n      clientOnly,\n    ),\n    createSystemConfig(\n      `src/${c}.ts`,\n      `dist/system/${c}`,\n      'production',\n      clientOnly,\n    ),\n  ]\n}\n"
  },
  {
    "path": "src/babel/plugin-debug-label.ts",
    "content": "import babel from '@babel/core'\nimport type { PluginObj } from '@babel/core'\nimport _templateBuilder from '@babel/template'\nimport { isAtom } from './utils.ts'\nimport type { PluginOptions } from './utils.ts'\n\nconst templateBuilder = (_templateBuilder as any).default || _templateBuilder\n\n/** @deprecated Use `jotai-babel/plugin-debug-label` instead. */\nexport default function debugLabelPlugin(\n  { types: t }: typeof babel,\n  options?: PluginOptions,\n): PluginObj {\n  console.warn(\n    '[DEPRECATED] jotai/babel/plugin-debug-label is deprecated and will be removed in v3.\\n' +\n      'Please use the `jotai-babel` package instead: https://github.com/jotaijs/jotai-babel',\n  )\n  return {\n    visitor: {\n      ExportDefaultDeclaration(nodePath, state) {\n        const { node } = nodePath\n        if (\n          t.isCallExpression(node.declaration) &&\n          isAtom(t, node.declaration.callee, options?.customAtomNames)\n        ) {\n          const filename = (state.filename || 'unknown').replace(/\\.\\w+$/, '')\n\n          let displayName = filename.split('/').pop()!\n\n          // ./{module name}/index.js\n          if (displayName === 'index') {\n            displayName =\n              filename.slice(0, -'/index'.length).split('/').pop() || 'unknown'\n          }\n          // Relies on visiting the variable declaration to add the debugLabel\n          const buildExport = templateBuilder(`\n          const %%atomIdentifier%% = %%atom%%;\n          export default %%atomIdentifier%%\n          `)\n          const ast = buildExport({\n            atomIdentifier: t.identifier(displayName),\n            atom: node.declaration,\n          })\n          nodePath.replaceWithMultiple(ast as babel.Node[])\n        }\n      },\n      VariableDeclarator(path) {\n        if (\n          t.isIdentifier(path.node.id) &&\n          t.isCallExpression(path.node.init) &&\n          isAtom(t, path.node.init.callee, options?.customAtomNames)\n        ) {\n          path.parentPath.insertAfter(\n            t.expressionStatement(\n              t.assignmentExpression(\n                '=',\n                t.memberExpression(\n                  t.identifier(path.node.id.name),\n                  t.identifier('debugLabel'),\n                ),\n                t.stringLiteral(path.node.id.name),\n              ),\n            ),\n          )\n        }\n      },\n    },\n  }\n}\n"
  },
  {
    "path": "src/babel/plugin-react-refresh.ts",
    "content": "import babel from '@babel/core'\nimport type { PluginObj } from '@babel/core'\nimport _templateBuilder from '@babel/template'\nimport { isAtom } from './utils.ts'\nimport type { PluginOptions } from './utils.ts'\n\nconst templateBuilder = (_templateBuilder as any).default || _templateBuilder\n\n/** @deprecated Use `jotai-babel/plugin-react-refresh` instead. */\nexport default function reactRefreshPlugin(\n  { types: t }: typeof babel,\n  options?: PluginOptions,\n): PluginObj {\n  console.warn(\n    '[DEPRECATED] jotai/babel/plugin-react-refresh is deprecated and will be removed in v3.\\n' +\n      'Please use the `jotai-babel` package instead: https://github.com/jotaijs/jotai-babel',\n  )\n  return {\n    pre({ opts }) {\n      if (!opts.filename) {\n        throw new Error('Filename must be available')\n      }\n    },\n    visitor: {\n      Program: {\n        exit(path) {\n          const jotaiAtomCache = templateBuilder(`\n          globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n            cache: new Map(),\n            get(name, inst) { \n              if (this.cache.has(name)) {\n                return this.cache.get(name)\n              }\n              this.cache.set(name, inst)\n              return inst\n            },\n          }`)()\n          path.unshiftContainer('body', jotaiAtomCache)\n        },\n      },\n      ExportDefaultDeclaration(nodePath, state) {\n        const { node } = nodePath\n        if (\n          t.isCallExpression(node.declaration) &&\n          isAtom(t, node.declaration.callee, options?.customAtomNames)\n        ) {\n          const filename = state.filename || 'unknown'\n          const atomKey = `${filename}/defaultExport`\n\n          const buildExport = templateBuilder(\n            `export default globalThis.jotaiAtomCache.get(%%atomKey%%, %%atom%%)`,\n          )\n          const ast = buildExport({\n            atomKey: t.stringLiteral(atomKey),\n            atom: node.declaration,\n          })\n          nodePath.replaceWith(ast as babel.Node)\n        }\n      },\n      VariableDeclarator(nodePath, state) {\n        if (\n          t.isIdentifier(nodePath.node.id) &&\n          t.isCallExpression(nodePath.node.init) &&\n          isAtom(t, nodePath.node.init.callee, options?.customAtomNames) &&\n          // Make sure atom declaration is in module scope\n          (nodePath.parentPath.parentPath?.isProgram() ||\n            nodePath.parentPath.parentPath?.isExportNamedDeclaration())\n        ) {\n          const filename = state.filename || 'unknown'\n          const atomKey = `${filename}/${nodePath.node.id.name}`\n\n          const buildAtomDeclaration = templateBuilder(\n            `const %%atomIdentifier%% = globalThis.jotaiAtomCache.get(%%atomKey%%, %%atom%%)`,\n          )\n          const ast = buildAtomDeclaration({\n            atomIdentifier: t.identifier(nodePath.node.id.name),\n            atomKey: t.stringLiteral(atomKey),\n            atom: nodePath.node.init,\n          })\n          nodePath.parentPath.replaceWith(ast as babel.Node)\n        }\n      },\n    },\n  }\n}\n"
  },
  {
    "path": "src/babel/preset.ts",
    "content": "import babel from '@babel/core'\nimport pluginDebugLabel from './plugin-debug-label.ts'\nimport pluginReactRefresh from './plugin-react-refresh.ts'\nimport type { PluginOptions } from './utils.ts'\n\n/** @deprecated Use `jotai-babel/preset` instead. */\nexport default function jotaiPreset(\n  _: typeof babel,\n  options?: PluginOptions,\n): { plugins: babel.PluginItem[] } {\n  console.warn(\n    '[DEPRECATED] jotai/babel/preset is deprecated and will be removed in v3.\\n' +\n      'Please use the `jotai-babel` package instead: https://github.com/jotaijs/jotai-babel',\n  )\n  return {\n    plugins: [\n      [pluginDebugLabel, options],\n      [pluginReactRefresh, options],\n    ],\n  }\n}\n"
  },
  {
    "path": "src/babel/utils.ts",
    "content": "import { types } from '@babel/core'\n\nexport interface PluginOptions {\n  customAtomNames?: string[]\n}\n\nexport function isAtom(\n  t: typeof types,\n  callee: babel.types.Expression | babel.types.V8IntrinsicIdentifier,\n  customAtomNames: PluginOptions['customAtomNames'] = [],\n): boolean {\n  const atomNames = [...atomFunctionNames, ...customAtomNames]\n  if (t.isIdentifier(callee) && atomNames.includes(callee.name)) {\n    return true\n  }\n\n  if (t.isMemberExpression(callee)) {\n    const { property } = callee\n    if (t.isIdentifier(property) && atomNames.includes(property.name)) {\n      return true\n    }\n  }\n  return false\n}\n\nconst atomFunctionNames = [\n  // Core\n  'atom',\n  'atomFamily',\n  'atomWithDefault',\n  'atomWithObservable',\n  'atomWithReducer',\n  'atomWithReset',\n  'atomWithStorage',\n  'freezeAtom',\n  'loadable',\n  'selectAtom',\n  'splitAtom',\n  'unwrap',\n  // jotai-xstate\n  'atomWithMachine',\n  // jotai-immer\n  'atomWithImmer',\n  // jotai-valtio\n  'atomWithProxy',\n  // jotai-trpc + jotai-relay\n  'atomWithQuery',\n  'atomWithMutation',\n  'atomWithSubscription',\n  // jotai-redux + jotai-zustand\n  'atomWithStore',\n  // jotai-location\n  'atomWithHash',\n  'atomWithLocation',\n  // jotai-optics\n  'focusAtom',\n  // jotai-form\n  'atomWithValidate',\n  'validateAtoms',\n  // jotai-cache\n  'atomWithCache',\n  // jotai-recoil\n  'atomWithRecoilValue',\n]\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './vanilla.ts'\nexport * from './react.ts'\n"
  },
  {
    "path": "src/react/Provider.ts",
    "content": "import { createContext, createElement, useContext, useRef } from 'react'\nimport type { FunctionComponent, ReactElement, ReactNode } from 'react'\nimport { createStore, getDefaultStore } from '../vanilla.ts'\n\ntype Store = ReturnType<typeof createStore>\n\ntype StoreContextType = ReturnType<typeof createContext<Store | undefined>>\nconst StoreContext: StoreContextType = createContext<Store | undefined>(\n  undefined,\n)\n\ntype Options = {\n  store?: Store\n}\n\nexport function useStore(options?: Options): Store {\n  const store = useContext(StoreContext)\n  return options?.store || store || getDefaultStore()\n}\n\nexport function Provider({\n  children,\n  store,\n}: {\n  children?: ReactNode\n  store?: Store\n}): ReactElement<\n  { value: Store | undefined },\n  FunctionComponent<{ value: Store }>\n> {\n  const storeRef = useRef<Store>(null)\n  if (store) {\n    return createElement(StoreContext.Provider, { value: store }, children)\n  }\n  if (storeRef.current === null) {\n    storeRef.current = createStore()\n  }\n  return createElement(\n    StoreContext.Provider,\n    {\n      // TODO: If this is not a false positive, consider using useState instead of useRef like https://github.com/pmndrs/jotai/pull/2771\n      // eslint-disable-next-line react-hooks/refs\n      value: storeRef.current,\n    },\n    children,\n  )\n}\n"
  },
  {
    "path": "src/react/useAtom.ts",
    "content": "import type {\n  Atom,\n  ExtractAtomArgs,\n  ExtractAtomResult,\n  ExtractAtomValue,\n  PrimitiveAtom,\n  SetStateAction,\n  WritableAtom,\n} from '../vanilla.ts'\nimport { useAtomValue } from './useAtomValue.ts'\nimport { useSetAtom } from './useSetAtom.ts'\n\ntype SetAtom<Args extends unknown[], Result> = (...args: Args) => Result\n\ntype Options = Parameters<typeof useAtomValue>[1]\n\nexport function useAtom<Value, Args extends unknown[], Result>(\n  atom: WritableAtom<Value, Args, Result>,\n  options?: Options,\n): [Awaited<Value>, SetAtom<Args, Result>]\n\nexport function useAtom<Value>(\n  atom: PrimitiveAtom<Value>,\n  options?: Options,\n): [Awaited<Value>, SetAtom<[SetStateAction<Value>], void>]\n\nexport function useAtom<Value>(\n  atom: Atom<Value>,\n  options?: Options,\n): [Awaited<Value>, never]\n\nexport function useAtom<\n  AtomType extends WritableAtom<unknown, never[], unknown>,\n>(\n  atom: AtomType,\n  options?: Options,\n): [\n  Awaited<ExtractAtomValue<AtomType>>,\n  SetAtom<ExtractAtomArgs<AtomType>, ExtractAtomResult<AtomType>>,\n]\n\nexport function useAtom<AtomType extends Atom<unknown>>(\n  atom: AtomType,\n  options?: Options,\n): [Awaited<ExtractAtomValue<AtomType>>, never]\n\nexport function useAtom<Value, Args extends unknown[], Result>(\n  atom: Atom<Value> | WritableAtom<Value, Args, Result>,\n  options?: Options,\n) {\n  return [\n    useAtomValue(atom, options),\n    // We do wrong type assertion here, which results in throwing an error.\n    useSetAtom(atom as WritableAtom<Value, Args, Result>, options),\n  ]\n}\n"
  },
  {
    "path": "src/react/useAtomValue.ts",
    "content": "import React, { useDebugValue, useEffect, useReducer } from 'react'\nimport { INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks } from '../vanilla/internals.ts'\nimport type { Atom, ExtractAtomValue } from '../vanilla.ts'\nimport { useStore } from './Provider.ts'\n\ntype Store = ReturnType<typeof useStore>\n\nconst isPromiseLike = (x: unknown): x is PromiseLike<unknown> =>\n  typeof (x as any)?.then === 'function'\n\nconst attachPromiseStatus = <T>(\n  promise: PromiseLike<T> & {\n    status?: 'pending' | 'fulfilled' | 'rejected'\n    value?: T\n    reason?: unknown\n  },\n) => {\n  if (!promise.status) {\n    promise.status = 'pending'\n    promise.then(\n      (v) => {\n        promise.status = 'fulfilled'\n        promise.value = v\n      },\n      (e) => {\n        promise.status = 'rejected'\n        promise.reason = e\n      },\n    )\n  }\n}\n\nconst use =\n  React.use ||\n  // A shim for older React versions\n  (<T>(\n    promise: PromiseLike<T> & {\n      status?: 'pending' | 'fulfilled' | 'rejected'\n      value?: T\n      reason?: unknown\n    },\n  ): T => {\n    if (promise.status === 'pending') {\n      throw promise\n    } else if (promise.status === 'fulfilled') {\n      return promise.value as T\n    } else if (promise.status === 'rejected') {\n      throw promise.reason\n    } else {\n      attachPromiseStatus(promise)\n      throw promise\n    }\n  })\n\nconst continuablePromiseMap = new WeakMap<\n  PromiseLike<unknown>,\n  Promise<unknown>\n>()\n\nconst createContinuablePromise = <T>(\n  store: Store,\n  promise: PromiseLike<T>,\n  getValue: () => PromiseLike<T> | T,\n) => {\n  const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n  const registerAbortHandler = buildingBlocks[26]\n  let continuablePromise = continuablePromiseMap.get(promise)\n  if (!continuablePromise) {\n    continuablePromise = new Promise<T>((resolve, reject) => {\n      let curr = promise\n      const onFulfilled = (me: PromiseLike<T>) => (v: T) => {\n        if (curr === me) {\n          resolve(v)\n        }\n      }\n      const onRejected = (me: PromiseLike<T>) => (e: unknown) => {\n        if (curr === me) {\n          reject(e)\n        }\n      }\n      const onAbort = () => {\n        try {\n          const nextValue = getValue()\n          if (isPromiseLike(nextValue)) {\n            continuablePromiseMap.set(nextValue, continuablePromise!)\n            curr = nextValue\n            nextValue.then(onFulfilled(nextValue), onRejected(nextValue))\n            registerAbortHandler(store, nextValue, onAbort)\n          } else {\n            resolve(nextValue)\n          }\n        } catch (e) {\n          reject(e)\n        }\n      }\n      promise.then(onFulfilled(promise), onRejected(promise))\n      registerAbortHandler(store, promise, onAbort)\n    })\n    continuablePromiseMap.set(promise, continuablePromise)\n  }\n  return continuablePromise\n}\n\ntype Options = Parameters<typeof useStore>[0] & {\n  delay?: number\n  unstable_promiseStatus?: boolean\n}\n\nexport function useAtomValue<Value>(\n  atom: Atom<Value>,\n  options?: Options,\n): Awaited<Value>\n\nexport function useAtomValue<AtomType extends Atom<unknown>>(\n  atom: AtomType,\n  options?: Options,\n): Awaited<ExtractAtomValue<AtomType>>\n\nexport function useAtomValue<Value>(atom: Atom<Value>, options?: Options) {\n  const { delay, unstable_promiseStatus: promiseStatus = !React.use } =\n    options || {}\n  const store = useStore(options)\n\n  const [[valueFromReducer, storeFromReducer, atomFromReducer], rerender] =\n    useReducer<readonly [Value, Store, typeof atom], undefined, []>(\n      (prev) => {\n        const nextValue = store.get(atom)\n        if (\n          Object.is(prev[0], nextValue) &&\n          prev[1] === store &&\n          prev[2] === atom\n        ) {\n          return prev\n        }\n        return [nextValue, store, atom]\n      },\n      undefined,\n      () => [store.get(atom), store, atom],\n    )\n\n  let value = valueFromReducer\n  if (storeFromReducer !== store || atomFromReducer !== atom) {\n    rerender()\n    value = store.get(atom)\n  }\n\n  useEffect(() => {\n    const unsub = store.sub(atom, () => {\n      if (promiseStatus) {\n        try {\n          const value = store.get(atom)\n          if (isPromiseLike(value)) {\n            attachPromiseStatus(\n              createContinuablePromise(store, value, () => store.get(atom)),\n            )\n          }\n        } catch {\n          // ignore\n        }\n      }\n      if (typeof delay === 'number') {\n        // delay rerendering to wait a promise possibly to resolve\n        setTimeout(rerender, delay)\n        return\n      }\n      rerender()\n    })\n    rerender()\n    return unsub\n  }, [store, atom, delay, promiseStatus])\n\n  useDebugValue(value)\n  if (isPromiseLike(value)) {\n    const promise = createContinuablePromise(store, value, () =>\n      store.get(atom),\n    )\n    if (promiseStatus) {\n      attachPromiseStatus(promise)\n    }\n    return use(promise)\n  }\n  return value as Awaited<Value>\n}\n"
  },
  {
    "path": "src/react/useSetAtom.ts",
    "content": "import { useCallback } from 'react'\nimport type {\n  ExtractAtomArgs,\n  ExtractAtomResult,\n  WritableAtom,\n} from '../vanilla.ts'\nimport { useStore } from './Provider.ts'\n\ntype SetAtom<Args extends unknown[], Result> = (...args: Args) => Result\ntype Options = Parameters<typeof useStore>[0]\n\nexport function useSetAtom<Value, Args extends unknown[], Result>(\n  atom: WritableAtom<Value, Args, Result>,\n  options?: Options,\n): SetAtom<Args, Result>\n\nexport function useSetAtom<\n  AtomType extends WritableAtom<unknown, never[], unknown>,\n>(\n  atom: AtomType,\n  options?: Options,\n): SetAtom<ExtractAtomArgs<AtomType>, ExtractAtomResult<AtomType>>\n\nexport function useSetAtom<Value, Args extends unknown[], Result>(\n  atom: WritableAtom<Value, Args, Result>,\n  options?: Options,\n) {\n  const store = useStore(options)\n  const setAtom = useCallback(\n    (...args: Args) => {\n      if (import.meta.env?.MODE !== 'production' && !('write' in atom)) {\n        // useAtom can pass non writable atom with wrong type assertion,\n        // so we should check here.\n        throw new Error('not writable atom')\n      }\n      return store.set(atom, ...args)\n    },\n    [store, atom],\n  )\n  return setAtom\n}\n"
  },
  {
    "path": "src/react/utils/useAtomCallback.ts",
    "content": "import { useMemo } from 'react'\nimport { useSetAtom } from '../../react.ts'\nimport { atom } from '../../vanilla.ts'\nimport type { Getter, Setter } from '../../vanilla.ts'\n\ntype Options = Parameters<typeof useSetAtom>[1]\n\nexport function useAtomCallback<Result, Args extends unknown[]>(\n  callback: (get: Getter, set: Setter, ...arg: Args) => Result,\n  options?: Options,\n): (...args: Args) => Result {\n  const anAtom = useMemo(\n    () => atom(null, (get, set, ...args: Args) => callback(get, set, ...args)),\n    [callback],\n  )\n  return useSetAtom(anAtom, options)\n}\n"
  },
  {
    "path": "src/react/utils/useHydrateAtoms.ts",
    "content": "import { useStore } from '../../react.ts'\nimport { type WritableAtom } from '../../vanilla.ts'\n\ntype Store = ReturnType<typeof useStore>\ntype Options = Parameters<typeof useStore>[0] & {\n  dangerouslyForceHydrate?: boolean\n}\ntype AnyWritableAtom = WritableAtom<any, any[], any>\n\ntype InferAtomTuples<T> = {\n  [K in keyof T]: T[K] extends readonly [infer A, ...infer Rest]\n    ? A extends WritableAtom<unknown, infer Args, unknown>\n      ? Rest extends Args\n        ? readonly [A, ...Rest]\n        : never\n      : T[K]\n    : never\n}\n\n// For internal use only\n// This can be changed without notice.\nexport type INTERNAL_InferAtomTuples<T> = InferAtomTuples<T>\n\nconst hydratedMap: WeakMap<Store, WeakSet<AnyWritableAtom>> = new WeakMap()\n\nexport function useHydrateAtoms<\n  T extends (readonly [AnyWritableAtom, ...unknown[]])[],\n>(values: InferAtomTuples<T>, options?: Options): void\n\nexport function useHydrateAtoms<T extends Map<AnyWritableAtom, unknown>>(\n  values: T,\n  options?: Options,\n): void\n\nexport function useHydrateAtoms<\n  T extends Iterable<readonly [AnyWritableAtom, ...unknown[]]>,\n>(values: InferAtomTuples<T>, options?: Options): void\n\nexport function useHydrateAtoms<\n  T extends Iterable<readonly [AnyWritableAtom, ...unknown[]]>,\n>(values: T, options?: Options) {\n  const store = useStore(options)\n\n  const hydratedSet = getHydratedSet(store)\n  for (const [atom, ...args] of values) {\n    if (!hydratedSet.has(atom) || options?.dangerouslyForceHydrate) {\n      hydratedSet.add(atom)\n      store.set(atom, ...args)\n    }\n  }\n}\n\nconst getHydratedSet = (store: Store) => {\n  let hydratedSet = hydratedMap.get(store)\n  if (!hydratedSet) {\n    hydratedSet = new WeakSet()\n    hydratedMap.set(store, hydratedSet)\n  }\n  return hydratedSet\n}\n"
  },
  {
    "path": "src/react/utils/useReducerAtom.ts",
    "content": "import { useCallback } from 'react'\nimport { useAtom } from '../../react.ts'\nimport type { PrimitiveAtom } from '../../vanilla.ts'\n\ntype Options = Parameters<typeof useAtom>[1]\n\n/**\n * @deprecated please use a recipe instead\n * https://github.com/pmndrs/jotai/pull/2467\n */\nexport function useReducerAtom<Value, Action>(\n  anAtom: PrimitiveAtom<Value>,\n  reducer: (v: Value, a?: Action) => Value,\n  options?: Options,\n): [Value, (action?: Action) => void]\n\n/**\n * @deprecated please use a recipe instead\n * https://github.com/pmndrs/jotai/pull/2467\n */\nexport function useReducerAtom<Value, Action>(\n  anAtom: PrimitiveAtom<Value>,\n  reducer: (v: Value, a: Action) => Value,\n  options?: Options,\n): [Value, (action: Action) => void]\n\nexport function useReducerAtom<Value, Action>(\n  anAtom: PrimitiveAtom<Value>,\n  reducer: (v: Value, a: Action) => Value,\n  options?: Options,\n) {\n  if (import.meta.env?.MODE !== 'production') {\n    console.warn(\n      '[DEPRECATED] useReducerAtom is deprecated and will be removed in the future. Please create your own version using the recipe. https://github.com/pmndrs/jotai/pull/2467',\n    )\n  }\n  const [state, setState] = useAtom(anAtom, options)\n  const dispatch = useCallback(\n    (action: Action) => {\n      setState((prev) => reducer(prev, action))\n    },\n    [setState, reducer],\n  )\n  return [state, dispatch]\n}\n"
  },
  {
    "path": "src/react/utils/useResetAtom.ts",
    "content": "import { useCallback } from 'react'\nimport { useSetAtom } from '../../react.ts'\nimport { RESET } from '../../vanilla/utils.ts'\nimport type { WritableAtom } from '../../vanilla.ts'\n\ntype Options = Parameters<typeof useSetAtom>[1]\n\nexport function useResetAtom<T>(\n  anAtom: WritableAtom<unknown, [typeof RESET], T>,\n  options?: Options,\n): () => T {\n  const setAtom = useSetAtom(anAtom, options)\n  const resetAtom = useCallback(() => setAtom(RESET), [setAtom])\n  return resetAtom\n}\n"
  },
  {
    "path": "src/react/utils.ts",
    "content": "export { useResetAtom } from './utils/useResetAtom.ts'\nexport { useReducerAtom } from './utils/useReducerAtom.ts'\nexport { useAtomCallback } from './utils/useAtomCallback.ts'\nexport { useHydrateAtoms } from './utils/useHydrateAtoms.ts'\n"
  },
  {
    "path": "src/react.ts",
    "content": "export { Provider, useStore } from './react/Provider.ts'\nexport { useAtomValue } from './react/useAtomValue.ts'\nexport { useSetAtom } from './react/useSetAtom.ts'\nexport { useAtom } from './react/useAtom.ts'\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/atom.ts",
    "content": "import type { Store } from './store'\n\ntype Getter = <Value>(atom: Atom<Value>) => Value\n\ntype Setter = <Value, Args extends unknown[], Result>(\n  atom: WritableAtom<Value, Args, Result>,\n  ...args: Args\n) => Result\n\ntype SetAtom<Args extends unknown[], Result> = <A extends Args>(\n  ...args: A\n) => Result\n\n/**\n * setSelf is for internal use only and subject to change without notice.\n */\ntype Read<Value, SetSelf = never> = (\n  get: Getter,\n  options: { readonly signal: AbortSignal; readonly setSelf: SetSelf },\n) => Value\n\ntype Write<Args extends unknown[], Result> = (\n  get: Getter,\n  set: Setter,\n  ...args: Args\n) => Result\n\n// This is an internal type and not part of public API.\n// Do not depend on it as it can change without notice.\ntype WithInitialValue<Value> = {\n  init: Value\n}\n\ntype OnUnmount = () => void\n\ntype OnMount<Args extends unknown[], Result> = <\n  S extends SetAtom<Args, Result>,\n>(\n  setAtom: S,\n) => OnUnmount | void\n\nexport interface Atom<Value> {\n  toString: () => string\n  read: Read<Value>\n  debugLabel?: string\n  /**\n   * To ONLY be used by Jotai libraries to mark atoms as private. Subject to change.\n   * @private\n   */\n  debugPrivate?: boolean\n  /**\n   * Fires after atom is referenced by the store for the first time\n   * This is an internal API and subject to change without notice.\n   */\n  INTERNAL_onInit?: (store: Store) => void\n}\n\nexport interface WritableAtom<\n  Value,\n  Args extends unknown[],\n  Result,\n> extends Atom<Value> {\n  read: Read<Value, SetAtom<Args, unknown>>\n  write: Write<Args, Result>\n  onMount?: OnMount<Args, Result>\n}\n\ntype SetStateAction<Value> = Value | ((prev: Value) => Value)\n\nexport type PrimitiveAtom<Value> = WritableAtom<\n  Value,\n  [SetStateAction<Value>],\n  void\n>\n\nlet keyCount = 0 // global key count for all atoms\n\n// writable derived atom\nexport function atom<Value, Args extends unknown[], Result>(\n  read: Read<Value, SetAtom<Args, unknown>>,\n  write: Write<Args, Result>,\n): WritableAtom<Value, Args, Result>\n\n// read-only derived atom\nexport function atom<Value>(read: Read<Value>): Atom<Value>\n\n// write-only derived atom\nexport function atom<Value, Args extends unknown[], Result>(\n  initialValue: Value,\n  write: Write<Args, Result>,\n): WritableAtom<Value, Args, Result> & WithInitialValue<Value>\n\n// primitive atom without initial value\nexport function atom<Value>(): PrimitiveAtom<Value | undefined> &\n  WithInitialValue<Value | undefined>\n\n// primitive atom\nexport function atom<Value>(\n  initialValue: Value,\n): PrimitiveAtom<Value> & WithInitialValue<Value>\n\nexport function atom<Value, Args extends unknown[], Result>(\n  read?: Value | Read<Value, SetAtom<Args, unknown>>,\n  write?: Write<Args, Result>,\n) {\n  const key = `atom${++keyCount}`\n  const config = {\n    toString() {\n      return import.meta.env?.MODE !== 'production' && this.debugLabel\n        ? key + ':' + this.debugLabel\n        : key\n    },\n  } as WritableAtom<Value, Args, Result> & { init?: Value | undefined }\n  if (typeof read === 'function') {\n    config.read = read as Read<Value, SetAtom<Args, unknown>>\n  } else {\n    config.init = read\n    config.read = defaultRead\n    config.write = defaultWrite as unknown as Write<Args, Result>\n  }\n  if (write) {\n    config.write = write\n  }\n  return config\n}\n\nfunction defaultRead<Value>(this: Atom<Value>, get: Getter) {\n  return get(this)\n}\n\nfunction defaultWrite<Value>(\n  this: PrimitiveAtom<Value>,\n  get: Getter,\n  set: Setter,\n  arg: SetStateAction<Value>,\n) {\n  return set(\n    this,\n    typeof arg === 'function'\n      ? (arg as (prev: Value) => Value)(get(this))\n      : arg,\n  )\n}\n"
  },
  {
    "path": "src/vanilla/internals.ts",
    "content": "// Internal functions (subject to change without notice)\n// In case you rely on them, be sure to pin the version\n\nimport type { Atom, WritableAtom } from './atom.ts'\n\ntype AnyValue = unknown\ntype AnyError = unknown\ntype AnyAtom = Atom<AnyValue>\ntype AnyWritableAtom = WritableAtom<AnyValue, unknown[], unknown>\ntype OnUnmount = () => void\ntype Getter = Parameters<AnyAtom['read']>[0]\ntype Setter = Parameters<AnyWritableAtom['write']>[1]\ntype EpochNumber = number\n\n/**\n * Mutable atom state,\n * tracked for both mounted and unmounted atoms in a store.\n *\n * This should be garbage collectable.\n * We can mutate it during atom read. (except for fields with TODO)\n */\ntype AtomState<Value = AnyValue> = {\n  /**\n   * Map of atoms that the atom depends on.\n   * The map value is the epoch number of the dependency.\n   */\n  readonly d: Map<AnyAtom, EpochNumber>\n  /**\n   * Set of atoms with pending promise that depend on the atom.\n   *\n   * This may cause memory leaks, but it's for the capability to continue promises\n   * TODO(daishi): revisit how to handle this\n   */\n  readonly p: Set<AnyAtom>\n  /** The epoch number of the atom. */\n  n: EpochNumber\n  /** Atom value */\n  v?: Value\n  /** Atom error */\n  e?: AnyError\n}\n\n/**\n * State tracked for mounted atoms. An atom is considered \"mounted\" if it has a\n * subscriber, or is a transitive dependency of another atom that has a\n * subscriber.\n * The mounted state of an atom is freed once it is no longer mounted.\n */\ntype Mounted = {\n  /** Set of listeners to notify when the atom value changes. */\n  readonly l: Set<() => void>\n  /** Set of mounted atoms that the atom depends on. */\n  readonly d: Set<AnyAtom>\n  /** Set of mounted atoms that depends on the atom. */\n  readonly t: Set<AnyAtom>\n  /** Function to run when the atom is unmounted. */\n  u?: () => void\n}\n\ntype WeakMapLike<K extends object, V> = {\n  get(key: K): V | undefined\n  set(key: K, value: V): void\n  has(key: K): boolean\n  delete(key: K): boolean\n}\n\ntype SetLike<T> = {\n  readonly size: number\n  add(value: T): void\n  has(value: T): boolean\n  delete(value: T): boolean\n  clear(): void\n  forEach(callback: (value: T) => void): void\n  [Symbol.iterator](): IterableIterator<T>\n}\n\ntype AtomStateMap = WeakMapLike<AnyAtom, AtomState>\ntype MountedMap = WeakMapLike<AnyAtom, Mounted>\ntype InvalidatedAtoms = WeakMapLike<AnyAtom, EpochNumber>\ntype ChangedAtoms = SetLike<AnyAtom>\ntype Callbacks = SetLike<() => void>\n\ntype AtomRead = <Value>(\n  store: Store,\n  atom: Atom<Value>,\n  ...params: Parameters<Atom<Value>['read']>\n) => Value\ntype AtomWrite = <Value, Args extends unknown[], Result>(\n  store: Store,\n  atom: WritableAtom<Value, Args, Result>,\n  ...params: Parameters<WritableAtom<Value, Args, Result>['write']>\n) => Result\ntype AtomOnInit = <Value>(store: Store, atom: Atom<Value>) => void\ntype AtomOnMount = <Value, Args extends unknown[], Result>(\n  store: Store,\n  atom: WritableAtom<Value, Args, Result>,\n  setAtom: (...args: Args) => Result,\n) => OnUnmount | void\n\ntype EnsureAtomState = <Value>(\n  store: Store,\n  atom: Atom<Value>,\n) => AtomState<Value>\ntype FlushCallbacks = (store: Store) => void\ntype RecomputeInvalidatedAtoms = (store: Store) => void\ntype ReadAtomState = <Value>(\n  store: Store,\n  atom: Atom<Value>,\n) => AtomState<Value>\ntype InvalidateDependents = (store: Store, atom: AnyAtom) => void\ntype WriteAtomState = <Value, Args extends unknown[], Result>(\n  store: Store,\n  atom: WritableAtom<Value, Args, Result>,\n  ...args: Args\n) => Result\ntype MountDependencies = (store: Store, atom: AnyAtom) => void\ntype MountAtom = <Value>(store: Store, atom: Atom<Value>) => Mounted\ntype UnmountAtom = <Value>(\n  store: Store,\n  atom: Atom<Value>,\n) => Mounted | undefined\ntype SetAtomStateValueOrPromise = <Value>(\n  store: Store,\n  atom: Atom<Value>,\n  valueOrPromise: Value,\n) => void\ntype StoreGet = <Value>(store: Store, atom: Atom<Value>) => Value\ntype StoreSet = <Value, Args extends unknown[], Result>(\n  store: Store,\n  atom: WritableAtom<Value, Args, Result>,\n  ...args: Args\n) => Result\ntype StoreSub = (\n  store: Store,\n  atom: AnyAtom,\n  listener: () => void,\n) => () => void\ntype EnhanceBuildingBlocks = (\n  buildingBlocks: Readonly<BuildingBlocks>,\n) => Readonly<BuildingBlocks>\ntype AbortHandlersMap = WeakMapLike<PromiseLike<unknown>, Set<() => void>>\ntype RegisterAbortHandler = <T>(\n  store: Store,\n  promise: PromiseLike<T>,\n  abortHandler: () => void,\n) => void\ntype AbortPromise = <T>(store: Store, promise: PromiseLike<T>) => void\n\ntype Store = {\n  get: <Value>(atom: Atom<Value>) => Value\n  set: <Value, Args extends unknown[], Result>(\n    atom: WritableAtom<Value, Args, Result>,\n    ...args: Args\n  ) => Result\n  sub: (atom: AnyAtom, listener: () => void) => () => void\n}\n\ntype BuildingBlocks = [\n  // store state\n  atomStateMap: AtomStateMap, //                               0\n  mountedMap: MountedMap, //                                   1\n  invalidatedAtoms: InvalidatedAtoms, //                       2\n  changedAtoms: ChangedAtoms, //                               3\n  mountCallbacks: Callbacks, //                                4\n  unmountCallbacks: Callbacks, //                              5\n  storeHooks: StoreHooks, //                                   6\n  // atom interceptors\n  atomRead: AtomRead, //                                       7\n  atomWrite: AtomWrite, //                                     8\n  atomOnInit: AtomOnInit, //                                   9\n  atomOnMount: AtomOnMount, //                                 10\n  // building-block functions\n  ensureAtomState: EnsureAtomState, //                         11\n  flushCallbacks: FlushCallbacks, //                           12\n  recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms, //     13\n  readAtomState: ReadAtomState, //                             14\n  invalidateDependents: InvalidateDependents, //               15\n  writeAtomState: WriteAtomState, //                           16\n  mountDependencies: MountDependencies, //                     17\n  mountAtom: MountAtom, //                                     18\n  unmountAtom: UnmountAtom, //                                 19\n  setAtomStateValueOrPromise: SetAtomStateValueOrPromise, //   20\n  // store api\n  storeGet: StoreGet, //                                       21\n  storeSet: StoreSet, //                                       22\n  storeSub: StoreSub, //                                       23\n  enhanceBuildingBlocks: EnhanceBuildingBlocks | undefined, // 24\n  // abortable promise support\n  abortHandlersMap: AbortHandlersMap, //                       25\n  registerAbortHandler: RegisterAbortHandler, //               26\n  abortPromise: AbortPromise, //                               27\n]\n\nexport type {\n  AtomState as INTERNAL_AtomState,\n  Mounted as INTERNAL_Mounted,\n  AtomStateMap as INTERNAL_AtomStateMap,\n  MountedMap as INTERNAL_MountedMap,\n  InvalidatedAtoms as INTERNAL_InvalidatedAtoms,\n  ChangedAtoms as INTERNAL_ChangedAtoms,\n  Callbacks as INTERNAL_Callbacks,\n  AtomRead as INTERNAL_AtomRead,\n  AtomWrite as INTERNAL_AtomWrite,\n  AtomOnInit as INTERNAL_AtomOnInit,\n  AtomOnMount as INTERNAL_AtomOnMount,\n  EnsureAtomState as INTERNAL_EnsureAtomState,\n  FlushCallbacks as INTERNAL_FlushCallbacks,\n  RecomputeInvalidatedAtoms as INTERNAL_RecomputeInvalidatedAtoms,\n  ReadAtomState as INTERNAL_ReadAtomState,\n  InvalidateDependents as INTERNAL_InvalidateDependents,\n  WriteAtomState as INTERNAL_WriteAtomState,\n  MountDependencies as INTERNAL_MountDependencies,\n  MountAtom as INTERNAL_MountAtom,\n  UnmountAtom as INTERNAL_UnmountAtom,\n  Store as INTERNAL_Store,\n  BuildingBlocks as INTERNAL_BuildingBlocks,\n  StoreHooks as INTERNAL_StoreHooks,\n}\n\n//\n// Some util functions\n//\n\nfunction hasInitialValue<T extends Atom<AnyValue>>(\n  atom: T,\n): atom is T & (T extends Atom<infer Value> ? { init: Value } : never) {\n  return 'init' in atom\n}\n\nfunction isActuallyWritableAtom(atom: AnyAtom): atom is AnyWritableAtom {\n  return !!(atom as AnyWritableAtom).write\n}\n\nfunction isAtomStateInitialized<Value>(atomState: AtomState<Value>): boolean {\n  return 'v' in atomState || 'e' in atomState\n}\n\nfunction returnAtomValue<Value>(atomState: AtomState<Value>): Value {\n  if ('e' in atomState) {\n    throw atomState.e\n  }\n  if (import.meta.env?.MODE !== 'production' && !('v' in atomState)) {\n    throw new Error('[Bug] atom state is not initialized')\n  }\n  return atomState.v!\n}\n\nfunction isPromiseLike(p: unknown): p is PromiseLike<unknown> {\n  return typeof (p as any)?.then === 'function'\n}\n\nfunction addPendingPromiseToDependency(\n  atom: AnyAtom,\n  promise: PromiseLike<AnyValue>,\n  dependencyAtomState: AtomState,\n): void {\n  if (!dependencyAtomState.p.has(atom)) {\n    dependencyAtomState.p.add(atom)\n    const cleanup = () => dependencyAtomState.p.delete(atom)\n    promise.then(cleanup, cleanup)\n  }\n}\n\nfunction getMountedOrPendingDependents(\n  atom: AnyAtom,\n  atomState: AtomState,\n  mountedMap: MountedMap,\n): Iterable<AnyAtom> {\n  const dependents = new Set<AnyAtom>()\n  for (const a of mountedMap.get(atom)?.t || []) {\n    dependents.add(a)\n  }\n  for (const atomWithPendingPromise of atomState.p) {\n    dependents.add(atomWithPendingPromise)\n  }\n  return dependents\n}\n\n//\n// Store hooks\n//\n\ntype StoreHook = {\n  (): void\n  add(callback: () => void): () => void\n}\n\ntype StoreHookForAtoms = {\n  (atom: AnyAtom): void\n  add(atom: AnyAtom, callback: () => void): () => void\n  add(atom: undefined, callback: (atom: AnyAtom) => void): () => void\n}\n\n/** StoreHooks are an experimental API. */\ntype StoreHooks = {\n  /** Listener to notify when the atom state is created. */\n  readonly i?: StoreHookForAtoms\n  /** Listener to notify when the atom is read. */\n  readonly r?: StoreHookForAtoms\n  /** Listener to notify when the atom value is changed. */\n  readonly c?: StoreHookForAtoms\n  /** Listener to notify when the atom is mounted. */\n  readonly m?: StoreHookForAtoms\n  /** Listener to notify when the atom is unmounted. */\n  readonly u?: StoreHookForAtoms\n  /** Listener to notify when callbacks are being flushed. */\n  readonly f?: StoreHook\n}\n\nconst createStoreHook = (): StoreHook => {\n  const callbacks = new Set<() => void>()\n  const notify = () => callbacks.forEach((fn) => fn())\n  notify.add = (fn: () => void) => {\n    callbacks.add(fn)\n    return () => callbacks.delete(fn)\n  }\n  return notify\n}\n\nconst createStoreHookForAtoms = (): StoreHookForAtoms => {\n  const all: object = {}\n  const callbacks = new WeakMap<\n    AnyAtom | typeof all,\n    Set<(atom?: AnyAtom) => void>\n  >()\n  const notify = (atom: AnyAtom) => {\n    callbacks.get(all)?.forEach((fn) => fn(atom))\n    callbacks.get(atom)?.forEach((fn) => fn())\n  }\n  notify.add = (atom: AnyAtom | undefined, fn: (atom?: AnyAtom) => void) => {\n    const key = atom || all\n    let fns = callbacks.get(key)\n    if (!fns) {\n      fns = new Set()\n      callbacks.set(key, fns)\n    }\n    fns.add(fn)\n    return () => {\n      fns!.delete(fn)\n      if (!fns!.size) {\n        callbacks.delete(key)\n      }\n    }\n  }\n  return notify as StoreHookForAtoms\n}\n\nfunction initializeStoreHooks(storeHooks: StoreHooks): Required<StoreHooks> {\n  type SH = { -readonly [P in keyof StoreHooks]: StoreHooks[P] }\n  ;(storeHooks as SH).i ||= createStoreHookForAtoms()\n  ;(storeHooks as SH).r ||= createStoreHookForAtoms()\n  ;(storeHooks as SH).c ||= createStoreHookForAtoms()\n  ;(storeHooks as SH).m ||= createStoreHookForAtoms()\n  ;(storeHooks as SH).u ||= createStoreHookForAtoms()\n  ;(storeHooks as SH).f ||= createStoreHook()\n  return storeHooks as Required<StoreHooks>\n}\n\n//\n// Main functions\n//\n\nconst BUILDING_BLOCK_atomRead: AtomRead = (_store, atom, ...params) =>\n  atom.read(...params)\nconst BUILDING_BLOCK_atomWrite: AtomWrite = (_store, atom, ...params) =>\n  atom.write(...params)\nconst BUILDING_BLOCK_atomOnInit: AtomOnInit = (store, atom) =>\n  atom.INTERNAL_onInit?.(store)\nconst BUILDING_BLOCK_atomOnMount: AtomOnMount = (_store, atom, setAtom) =>\n  atom.onMount?.(setAtom)\n\nconst BUILDING_BLOCK_ensureAtomState: EnsureAtomState = (store, atom) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const atomStateMap = buildingBlocks[0]\n  const storeHooks = buildingBlocks[6]\n  const atomOnInit = buildingBlocks[9]\n  if (import.meta.env?.MODE !== 'production' && !atom) {\n    throw new Error('Atom is undefined or null')\n  }\n  let atomState = atomStateMap.get(atom)\n  if (!atomState) {\n    atomState = { d: new Map(), p: new Set(), n: 0 }\n    atomStateMap.set(atom, atomState)\n    storeHooks.i?.(atom)\n    atomOnInit?.(store, atom)\n  }\n  return atomState as never\n}\n\nconst BUILDING_BLOCK_flushCallbacks: FlushCallbacks = (store) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const changedAtoms = buildingBlocks[3]\n  const mountCallbacks = buildingBlocks[4]\n  const unmountCallbacks = buildingBlocks[5]\n  const storeHooks = buildingBlocks[6]\n  const recomputeInvalidatedAtoms = buildingBlocks[13]\n  const errors: unknown[] = []\n  const call = (fn: () => void) => {\n    try {\n      fn()\n    } catch (e) {\n      errors.push(e)\n    }\n  }\n  do {\n    if (storeHooks.f) {\n      call(storeHooks.f)\n    }\n    const callbacks = new Set<() => void>()\n    const add = callbacks.add.bind(callbacks)\n    changedAtoms.forEach((atom) => mountedMap.get(atom)?.l.forEach(add))\n    changedAtoms.clear()\n    unmountCallbacks.forEach(add)\n    unmountCallbacks.clear()\n    mountCallbacks.forEach(add)\n    mountCallbacks.clear()\n    callbacks.forEach(call)\n    if (changedAtoms.size) {\n      recomputeInvalidatedAtoms(store)\n    }\n  } while (changedAtoms.size || unmountCallbacks.size || mountCallbacks.size)\n  if (errors.length) {\n    throw new AggregateError(errors)\n  }\n}\n\nconst BUILDING_BLOCK_recomputeInvalidatedAtoms: RecomputeInvalidatedAtoms = (\n  store,\n) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const invalidatedAtoms = buildingBlocks[2]\n  const changedAtoms = buildingBlocks[3]\n  const ensureAtomState = buildingBlocks[11]\n  const readAtomState = buildingBlocks[14]\n  const mountDependencies = buildingBlocks[17]\n  // Step 1: traverse the dependency graph to build the topologically sorted atom list\n  // We don't bother to check for cycles, which simplifies the algorithm.\n  // This is a topological sort via depth-first search, slightly modified from\n  // what's described here for simplicity and performance reasons:\n  // https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search\n  const topSortedReversed: [atom: AnyAtom, atomState: AtomState][] = []\n  const visiting = new WeakSet<AnyAtom>()\n  const visited = new WeakSet<AnyAtom>()\n  // Visit the root atom. This is the only atom in the dependency graph\n  // without incoming edges, which is one reason we can simplify the algorithm\n  const stack: AnyAtom[] = Array.from(changedAtoms)\n  while (stack.length) {\n    const a = stack[stack.length - 1]!\n    const aState = ensureAtomState(store, a)\n    if (visited.has(a)) {\n      // All dependents have been processed, now process this atom\n      stack.pop()\n      continue\n    }\n    if (visiting.has(a)) {\n      // The algorithm calls for pushing onto the front of the list. For\n      // performance, we will simply push onto the end, and then will iterate in\n      // reverse order later.\n      if (invalidatedAtoms.get(a) === aState.n) {\n        topSortedReversed.push([a, aState])\n      } else if (\n        import.meta.env?.MODE !== 'production' &&\n        invalidatedAtoms.has(a)\n      ) {\n        throw new Error('[Bug] invalidated atom exists')\n      }\n      // Atom has been visited but not yet processed\n      visited.add(a)\n      stack.pop()\n      continue\n    }\n    visiting.add(a)\n    // Push unvisited dependents onto the stack\n    for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) {\n      if (!visiting.has(d)) {\n        stack.push(d)\n      }\n    }\n  }\n  // Step 2: use the topSortedReversed atom list to recompute all affected atoms\n  // Track what's changed, so that we can short circuit when possible\n  for (let i = topSortedReversed.length - 1; i >= 0; --i) {\n    const [a, aState] = topSortedReversed[i]!\n    let hasChangedDeps = false\n    for (const dep of aState.d.keys()) {\n      if (dep !== a && changedAtoms.has(dep)) {\n        hasChangedDeps = true\n        break\n      }\n    }\n    if (hasChangedDeps) {\n      invalidatedAtoms.set(a, aState.n)\n      readAtomState(store, a)\n      mountDependencies(store, a)\n    }\n    invalidatedAtoms.delete(a)\n  }\n}\n\n// Dev only\nconst storeMutationSet = new WeakSet<Store>()\n\nconst BUILDING_BLOCK_readAtomState: ReadAtomState = (store, atom) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const invalidatedAtoms = buildingBlocks[2]\n  const changedAtoms = buildingBlocks[3]\n  const storeHooks = buildingBlocks[6]\n  const atomRead = buildingBlocks[7]\n  const ensureAtomState = buildingBlocks[11]\n  const flushCallbacks = buildingBlocks[12]\n  const recomputeInvalidatedAtoms = buildingBlocks[13]\n  const readAtomState = buildingBlocks[14]\n  const writeAtomState = buildingBlocks[16]\n  const mountDependencies = buildingBlocks[17]\n  const setAtomStateValueOrPromise = buildingBlocks[20]\n  const registerAbortHandler = buildingBlocks[26]\n  const atomState = ensureAtomState(store, atom)\n  // See if we can skip recomputing this atom.\n  if (isAtomStateInitialized(atomState)) {\n    // If the atom is mounted, we can use cached atom state.\n    // because it should have been updated by dependencies.\n    // We can't use the cache if the atom is invalidated.\n    if (mountedMap.has(atom) && invalidatedAtoms.get(atom) !== atomState.n) {\n      return atomState\n    }\n    // Otherwise, check if the dependencies have changed.\n    // If all dependencies haven't changed, we can use the cache.\n    let hasChangedDeps = false\n    for (const [a, n] of atomState.d) {\n      if (readAtomState(store, a).n !== n) {\n        hasChangedDeps = true\n        break\n      }\n    }\n    if (!hasChangedDeps) {\n      return atomState\n    }\n  }\n  // Compute a new state for this atom.\n  let isSync = true\n  const prevDeps = new Set<AnyAtom>(atomState.d.keys())\n  const nextDeps = new Map<AnyAtom, EpochNumber>()\n  const pruneDependencies = () => {\n    for (const a of prevDeps) {\n      if (!nextDeps.has(a)) {\n        atomState.d.delete(a)\n      }\n    }\n  }\n  const mountDependenciesIfAsync = () => {\n    if (mountedMap.has(atom)) {\n      // If changedAtoms is already populated, an outer recompute cycle will handle it\n      const shouldRecompute = !changedAtoms.size\n      mountDependencies(store, atom)\n      if (shouldRecompute) {\n        recomputeInvalidatedAtoms(store)\n        flushCallbacks(store)\n      }\n    }\n  }\n  const getter = <V>(a: Atom<V>) => {\n    if (a === (atom as AnyAtom)) {\n      const aState = ensureAtomState(store, a)\n      if (!isAtomStateInitialized(aState)) {\n        if (hasInitialValue(a)) {\n          setAtomStateValueOrPromise(store, a, a.init)\n        } else {\n          // NOTE invalid derived atoms can reach here\n          throw new Error('no atom init')\n        }\n      }\n      return returnAtomValue(aState)\n    }\n    // a !== atom\n    const aState = readAtomState(store, a)\n    try {\n      return returnAtomValue(aState)\n    } finally {\n      nextDeps.set(a, aState.n)\n      atomState.d.set(a, aState.n)\n      if (isPromiseLike(atomState.v)) {\n        addPendingPromiseToDependency(atom, atomState.v, aState)\n      }\n      if (mountedMap.has(atom)) {\n        mountedMap.get(a)?.t.add(atom)\n      }\n      if (!isSync) {\n        mountDependenciesIfAsync()\n      }\n    }\n  }\n  let controller: AbortController | undefined\n  let setSelf: ((...args: unknown[]) => unknown) | undefined\n  const options = {\n    get signal() {\n      if (!controller) {\n        controller = new AbortController()\n      }\n      return controller.signal\n    },\n    get setSelf() {\n      if (import.meta.env?.MODE !== 'production') {\n        // This is shown even before calling. It's a strong warning.\n        console.warn(\n          '[DEPRECATED] setSelf is deprecated and will be removed in v3.',\n        )\n      }\n      if (\n        import.meta.env?.MODE !== 'production' &&\n        !isActuallyWritableAtom(atom)\n      ) {\n        console.warn('setSelf function cannot be used with read-only atom')\n      }\n      if (!setSelf && isActuallyWritableAtom(atom)) {\n        setSelf = (...args) => {\n          if (import.meta.env?.MODE !== 'production' && isSync) {\n            console.warn('setSelf function cannot be called in sync')\n          }\n          if (!isSync) {\n            try {\n              return writeAtomState(store, atom, ...args)\n            } finally {\n              recomputeInvalidatedAtoms(store)\n              flushCallbacks(store)\n            }\n          }\n        }\n      }\n      return setSelf\n    },\n  }\n  const prevEpochNumber = atomState.n\n  const prevInvalidated = invalidatedAtoms.get(atom) === prevEpochNumber\n  try {\n    if (import.meta.env?.MODE !== 'production') {\n      storeMutationSet.delete(store)\n    }\n    const valueOrPromise = atomRead(store, atom, getter, options as never)\n    if (import.meta.env?.MODE !== 'production' && storeMutationSet.has(store)) {\n      console.warn(\n        'Detected store mutation during atom read. This is not supported.',\n      )\n    }\n    setAtomStateValueOrPromise(store, atom, valueOrPromise)\n    if (isPromiseLike(valueOrPromise)) {\n      registerAbortHandler(store, valueOrPromise, () => controller?.abort())\n      const settle = () => {\n        pruneDependencies()\n        mountDependenciesIfAsync()\n      }\n      valueOrPromise.then(settle, settle)\n    } else {\n      pruneDependencies()\n    }\n    storeHooks.r?.(atom)\n    return atomState\n  } catch (error) {\n    delete atomState.v\n    atomState.e = error\n    ++atomState.n\n    return atomState\n  } finally {\n    isSync = false\n    if (atomState.n !== prevEpochNumber && prevInvalidated) {\n      invalidatedAtoms.set(atom, atomState.n)\n      changedAtoms.add(atom)\n      storeHooks.c?.(atom)\n    }\n  }\n}\n\nconst BUILDING_BLOCK_invalidateDependents: InvalidateDependents = (\n  store,\n  atom,\n) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const invalidatedAtoms = buildingBlocks[2]\n  const ensureAtomState = buildingBlocks[11]\n  const stack: AnyAtom[] = [atom]\n  while (stack.length) {\n    const a = stack.pop()!\n    const aState = ensureAtomState(store, a)\n    for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) {\n      const dState = ensureAtomState(store, d)\n      if (invalidatedAtoms.get(d) !== dState.n) {\n        invalidatedAtoms.set(d, dState.n)\n        stack.push(d)\n      }\n    }\n  }\n}\n\nconst BUILDING_BLOCK_writeAtomState: WriteAtomState = (\n  store,\n  atom,\n  ...args\n) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const changedAtoms = buildingBlocks[3]\n  const storeHooks = buildingBlocks[6]\n  const atomWrite = buildingBlocks[8]\n  const ensureAtomState = buildingBlocks[11]\n  const flushCallbacks = buildingBlocks[12]\n  const recomputeInvalidatedAtoms = buildingBlocks[13]\n  const readAtomState = buildingBlocks[14]\n  const invalidateDependents = buildingBlocks[15]\n  const writeAtomState = buildingBlocks[16]\n  const mountDependencies = buildingBlocks[17]\n  const setAtomStateValueOrPromise = buildingBlocks[20]\n  let isSync = true\n  const getter: Getter = <V>(a: Atom<V>) =>\n    returnAtomValue(readAtomState(store, a))\n  const setter: Setter = <V, As extends unknown[], R>(\n    a: WritableAtom<V, As, R>,\n    ...args: As\n  ) => {\n    const aState = ensureAtomState(store, a)\n    try {\n      if (a === (atom as AnyAtom)) {\n        if (!hasInitialValue(a)) {\n          // NOTE technically possible but restricted as it may cause bugs\n          throw new Error('atom not writable')\n        }\n        if (import.meta.env?.MODE !== 'production') {\n          storeMutationSet.add(store)\n        }\n        const prevEpochNumber = aState.n\n        const v = args[0] as V\n        setAtomStateValueOrPromise(store, a, v)\n        mountDependencies(store, a)\n        if (prevEpochNumber !== aState.n) {\n          changedAtoms.add(a)\n          invalidateDependents(store, a)\n          storeHooks.c?.(a)\n        }\n        return undefined as R\n      } else {\n        return writeAtomState(store, a, ...args)\n      }\n    } finally {\n      if (!isSync) {\n        recomputeInvalidatedAtoms(store)\n        flushCallbacks(store)\n      }\n    }\n  }\n  try {\n    return atomWrite(store, atom, getter, setter, ...args)\n  } finally {\n    isSync = false\n  }\n}\n\nconst BUILDING_BLOCK_mountDependencies: MountDependencies = (store, atom) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const changedAtoms = buildingBlocks[3]\n  const storeHooks = buildingBlocks[6]\n  const ensureAtomState = buildingBlocks[11]\n  const invalidateDependents = buildingBlocks[15]\n  const mountAtom = buildingBlocks[18]\n  const unmountAtom = buildingBlocks[19]\n  const atomState = ensureAtomState(store, atom)\n  const mounted = mountedMap.get(atom)\n  if (mounted) {\n    for (const [a, n] of atomState.d) {\n      if (!mounted.d.has(a)) {\n        const aState = ensureAtomState(store, a)\n        const aMounted = mountAtom(store, a)\n        aMounted.t.add(atom)\n        mounted.d.add(a)\n        if (n !== aState.n) {\n          changedAtoms.add(a)\n          invalidateDependents(store, a)\n          storeHooks.c?.(a)\n        }\n      }\n    }\n    for (const a of mounted.d) {\n      if (!atomState.d.has(a)) {\n        mounted.d.delete(a)\n        const aMounted = unmountAtom(store, a)\n        aMounted?.t.delete(atom)\n      }\n    }\n  }\n}\n\nconst BUILDING_BLOCK_mountAtom: MountAtom = (store, atom) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const mountCallbacks = buildingBlocks[4]\n  const storeHooks = buildingBlocks[6]\n  const atomOnMount = buildingBlocks[10]\n  const ensureAtomState = buildingBlocks[11]\n  const flushCallbacks = buildingBlocks[12]\n  const recomputeInvalidatedAtoms = buildingBlocks[13]\n  const readAtomState = buildingBlocks[14]\n  const writeAtomState = buildingBlocks[16]\n  const mountAtom = buildingBlocks[18]\n  const atomState = ensureAtomState(store, atom)\n  let mounted = mountedMap.get(atom)\n  if (!mounted) {\n    // recompute atom state\n    readAtomState(store, atom)\n    // mount dependencies first\n    for (const a of atomState.d.keys()) {\n      const aMounted = mountAtom(store, a)\n      aMounted.t.add(atom)\n    }\n    // mount self\n    mounted = {\n      l: new Set(),\n      d: new Set(atomState.d.keys()),\n      t: new Set(),\n    }\n    mountedMap.set(atom, mounted)\n    if (isActuallyWritableAtom(atom)) {\n      const processOnMount = () => {\n        let isSync = true\n        const setAtom = (...args: unknown[]) => {\n          try {\n            return writeAtomState(store, atom, ...args)\n          } finally {\n            if (!isSync) {\n              recomputeInvalidatedAtoms(store)\n              flushCallbacks(store)\n            }\n          }\n        }\n        try {\n          const onUnmount = atomOnMount(store, atom, setAtom)\n          if (onUnmount) {\n            mounted!.u = () => {\n              isSync = true\n              try {\n                onUnmount()\n              } finally {\n                isSync = false\n              }\n            }\n          }\n        } finally {\n          isSync = false\n        }\n      }\n      mountCallbacks.add(processOnMount)\n    }\n    storeHooks.m?.(atom)\n  }\n  return mounted\n}\n\nconst BUILDING_BLOCK_unmountAtom: UnmountAtom = (store, atom) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const mountedMap = buildingBlocks[1]\n  const unmountCallbacks = buildingBlocks[5]\n  const storeHooks = buildingBlocks[6]\n  const ensureAtomState = buildingBlocks[11]\n  const unmountAtom = buildingBlocks[19]\n  const atomState = ensureAtomState(store, atom)\n  let mounted = mountedMap.get(atom)\n  if (!mounted || mounted.l.size) {\n    return mounted\n  }\n  let isDependent = false\n  for (const a of mounted.t) {\n    if (mountedMap.get(a)?.d.has(atom)) {\n      isDependent = true\n      break\n    }\n  }\n  if (!isDependent) {\n    // unmount self\n    if (mounted.u) {\n      unmountCallbacks.add(mounted.u)\n    }\n    mounted = undefined\n    mountedMap.delete(atom)\n    // unmount dependencies\n    for (const a of atomState.d.keys()) {\n      const aMounted = unmountAtom(store, a)\n      aMounted?.t.delete(atom)\n    }\n    storeHooks.u?.(atom)\n    return undefined\n  }\n  return mounted\n}\n\nconst BUILDING_BLOCK_setAtomStateValueOrPromise: SetAtomStateValueOrPromise = (\n  store,\n  atom,\n  valueOrPromise,\n) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const ensureAtomState = buildingBlocks[11]\n  const abortPromise = buildingBlocks[27]\n  const atomState = ensureAtomState(store, atom)\n  const hasPrevValue = 'v' in atomState\n  const prevValue = atomState.v\n  if (isPromiseLike(valueOrPromise)) {\n    for (const a of atomState.d.keys()) {\n      addPendingPromiseToDependency(\n        atom,\n        valueOrPromise,\n        ensureAtomState(store, a),\n      )\n    }\n  }\n  atomState.v = valueOrPromise\n  delete atomState.e\n  if (!hasPrevValue || !Object.is(prevValue, atomState.v)) {\n    ++atomState.n\n    if (isPromiseLike(prevValue)) {\n      abortPromise(store, prevValue)\n    }\n  }\n}\n\nconst BUILDING_BLOCK_storeGet: StoreGet = (store, atom) => {\n  const readAtomState = getInternalBuildingBlocks(store)[14]\n  return returnAtomValue(readAtomState(store, atom)) as any\n}\n\nconst BUILDING_BLOCK_storeSet: StoreSet = (store, atom, ...args) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const changedAtoms = buildingBlocks[3]\n  const flushCallbacks = buildingBlocks[12]\n  const recomputeInvalidatedAtoms = buildingBlocks[13]\n  const writeAtomState = buildingBlocks[16]\n  const prevChangedAtomsSize = changedAtoms.size\n  try {\n    return writeAtomState(store, atom, ...args) as any\n  } finally {\n    if (changedAtoms.size !== prevChangedAtomsSize) {\n      recomputeInvalidatedAtoms(store)\n      flushCallbacks(store)\n    }\n  }\n}\n\nconst BUILDING_BLOCK_storeSub: StoreSub = (store, atom, listener) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const flushCallbacks = buildingBlocks[12]\n  const mountAtom = buildingBlocks[18]\n  const unmountAtom = buildingBlocks[19]\n  const mounted = mountAtom(store, atom)\n  const listeners = mounted.l\n  listeners.add(listener)\n  flushCallbacks(store)\n  return () => {\n    listeners.delete(listener)\n    unmountAtom(store, atom)\n    flushCallbacks(store)\n  }\n}\n\nconst BUILDING_BLOCK_registerAbortHandler: RegisterAbortHandler = (\n  store,\n  promise,\n  abortHandler,\n) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const abortHandlersMap = buildingBlocks[25]\n  let abortHandlers = abortHandlersMap.get(promise)\n  if (!abortHandlers) {\n    abortHandlers = new Set()\n    abortHandlersMap.set(promise, abortHandlers)\n    const cleanup = () => abortHandlersMap.delete(promise)\n    promise.then(cleanup, cleanup)\n  }\n  abortHandlers.add(abortHandler)\n}\n\nconst BUILDING_BLOCK_abortPromise: AbortPromise = (store, promise) => {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const abortHandlersMap = buildingBlocks[25]\n  const abortHandlers = abortHandlersMap.get(promise)\n  abortHandlers?.forEach((fn) => fn())\n}\n\nconst buildingBlockMap = new WeakMap<Store, Readonly<BuildingBlocks>>()\n\nconst getInternalBuildingBlocks = (store: Store): Readonly<BuildingBlocks> => {\n  const buildingBlocks = buildingBlockMap.get(store)!\n  if (import.meta.env?.MODE !== 'production' && !buildingBlocks) {\n    throw new Error(\n      'Store must be created by buildStore to read its building blocks',\n    )\n  }\n  return buildingBlocks\n}\n\nfunction getBuildingBlocks(store: Store): Readonly<BuildingBlocks> {\n  const buildingBlocks = getInternalBuildingBlocks(store)\n  const enhanceBuildingBlocks = buildingBlocks[24]\n  if (enhanceBuildingBlocks) {\n    return enhanceBuildingBlocks(buildingBlocks)\n  }\n  return buildingBlocks\n}\n\nfunction buildStore(...buildArgs: Partial<BuildingBlocks>): Store {\n  const store = {\n    get(atom) {\n      const storeGet = getInternalBuildingBlocks(store)[21]\n      return storeGet(store, atom)\n    },\n    set(atom, ...args) {\n      const storeSet = getInternalBuildingBlocks(store)[22]\n      return storeSet(store, atom, ...args)\n    },\n    sub(atom, listener) {\n      const storeSub = getInternalBuildingBlocks(store)[23]\n      return storeSub(store, atom, listener)\n    },\n  } as Store\n\n  const buildingBlocks = (\n    [\n      // store state\n      new WeakMap(), // atomStateMap\n      new WeakMap(), // mountedMap\n      new WeakMap(), // invalidatedAtoms\n      new Set(), // changedAtoms\n      new Set(), // mountCallbacks\n      new Set(), // unmountCallbacks\n      {}, // storeHooks\n      // atom interceptors\n      BUILDING_BLOCK_atomRead,\n      BUILDING_BLOCK_atomWrite,\n      BUILDING_BLOCK_atomOnInit,\n      BUILDING_BLOCK_atomOnMount,\n      // building-block functions\n      BUILDING_BLOCK_ensureAtomState,\n      BUILDING_BLOCK_flushCallbacks,\n      BUILDING_BLOCK_recomputeInvalidatedAtoms,\n      BUILDING_BLOCK_readAtomState,\n      BUILDING_BLOCK_invalidateDependents,\n      BUILDING_BLOCK_writeAtomState,\n      BUILDING_BLOCK_mountDependencies,\n      BUILDING_BLOCK_mountAtom,\n      BUILDING_BLOCK_unmountAtom,\n      BUILDING_BLOCK_setAtomStateValueOrPromise,\n      BUILDING_BLOCK_storeGet,\n      BUILDING_BLOCK_storeSet,\n      BUILDING_BLOCK_storeSub,\n      undefined,\n      // abortable promise support\n      new WeakMap(), // abortHandlersMap\n      BUILDING_BLOCK_registerAbortHandler,\n      BUILDING_BLOCK_abortPromise,\n    ] satisfies BuildingBlocks\n  ).map((fn, i) => buildArgs[i] || fn) as BuildingBlocks\n  buildingBlockMap.set(store, Object.freeze(buildingBlocks))\n  return store\n}\n\nexport {\n  //\n  // Export internal functions\n  //\n  buildStore as INTERNAL_buildStoreRev2,\n  getBuildingBlocks as INTERNAL_getBuildingBlocksRev2,\n  initializeStoreHooks as INTERNAL_initializeStoreHooksRev2,\n\n  //\n  // Still experimental and some of them will be gone soon\n  //\n  hasInitialValue as INTERNAL_hasInitialValue,\n  isActuallyWritableAtom as INTERNAL_isActuallyWritableAtom,\n  isAtomStateInitialized as INTERNAL_isAtomStateInitialized,\n  returnAtomValue as INTERNAL_returnAtomValue,\n  isPromiseLike as INTERNAL_isPromiseLike,\n  addPendingPromiseToDependency as INTERNAL_addPendingPromiseToDependency,\n  getMountedOrPendingDependents as INTERNAL_getMountedOrPendingDependents,\n}\n"
  },
  {
    "path": "src/vanilla/store.ts",
    "content": "import { INTERNAL_buildStoreRev2 as INTERNAL_buildStore } from './internals.ts'\nimport type { INTERNAL_Store } from './internals.ts'\n\nexport type Store = INTERNAL_Store\n\nlet overriddenCreateStore: typeof createStore | undefined\n\nexport function INTERNAL_overrideCreateStore(\n  fn: (prev: typeof createStore | undefined) => typeof createStore,\n): void {\n  overriddenCreateStore = fn(overriddenCreateStore)\n}\n\nexport function createStore(): Store {\n  if (overriddenCreateStore) {\n    return overriddenCreateStore()\n  }\n  return INTERNAL_buildStore()\n}\n\nlet defaultStore: Store | undefined\n\nexport function getDefaultStore(): Store {\n  if (!defaultStore) {\n    defaultStore = createStore()\n    if (import.meta.env?.MODE !== 'production') {\n      ;(globalThis as any).__JOTAI_DEFAULT_STORE__ ||= defaultStore\n      if ((globalThis as any).__JOTAI_DEFAULT_STORE__ !== defaultStore) {\n        console.warn(\n          'Detected multiple Jotai instances. It may cause unexpected behavior with the default store. https://github.com/pmndrs/jotai/discussions/2044',\n        )\n      }\n    }\n  }\n  return defaultStore\n}\n"
  },
  {
    "path": "src/vanilla/typeUtils.ts",
    "content": "import type { Atom, PrimitiveAtom, WritableAtom } from './atom.ts'\n\nexport type Getter = Parameters<Atom<unknown>['read']>[0]\nexport type Setter = Parameters<\n  WritableAtom<unknown, unknown[], unknown>['write']\n>[1]\n\nexport type ExtractAtomValue<AtomType> =\n  AtomType extends Atom<infer Value> ? Value : never\n\nexport type ExtractAtomArgs<AtomType> =\n  AtomType extends WritableAtom<unknown, infer Args, infer _Result>\n    ? Args\n    : never\n\nexport type ExtractAtomResult<AtomType> =\n  AtomType extends WritableAtom<unknown, infer _Args, infer Result>\n    ? Result\n    : never\n\nexport type SetStateAction<Value> = ExtractAtomArgs<PrimitiveAtom<Value>>[0]\n"
  },
  {
    "path": "src/vanilla/utils/atomFamily.ts",
    "content": "import { type Atom } from '../../vanilla.ts'\n\n/**\n * in milliseconds\n */\ntype CreatedAt = number\ntype ShouldRemove<Param> = (createdAt: CreatedAt, param: Param) => boolean\ntype Cleanup = () => void\ntype Callback<Param, AtomType> = (event: {\n  type: 'CREATE' | 'REMOVE'\n  param: Param\n  atom: AtomType\n}) => void\n\n/**\n * @deprecated atomFamily is deprecated and will be removed in v3.\n * Please use the `jotai-family` package instead: https://github.com/jotaijs/jotai-family\n *\n * Install: `npm install jotai-family`\n *\n * Migration:\n * ```ts\n * // Before\n * import { atomFamily } from 'jotai/utils'\n *\n * // After\n * import { atomFamily } from 'jotai-family'\n * ```\n */\nexport interface AtomFamily<Param, AtomType> {\n  (param: Param): AtomType\n  getParams(): Iterable<Param>\n  remove(param: Param): void\n  setShouldRemove(shouldRemove: ShouldRemove<Param> | null): void\n  /**\n   * fires when an atom is created or removed\n   * This API is for advanced use cases, and can change without notice.\n   */\n  unstable_listen(callback: Callback<Param, AtomType>): Cleanup\n}\n\nlet didWarnDeprecation = false\n\n/**\n * @deprecated atomFamily is deprecated and will be removed in v3.\n * Please use the `jotai-family` package instead: https://github.com/jotaijs/jotai-family\n *\n * Install: `npm install jotai-family`\n *\n * Migration:\n * ```ts\n * // Before\n * import { atomFamily } from 'jotai/utils'\n *\n * // After\n * import { atomFamily } from 'jotai-family'\n * ```\n */\nexport function atomFamily<Param, AtomType extends Atom<unknown>>(\n  initializeAtom: (param: Param) => AtomType,\n  areEqual?: (a: Param, b: Param) => boolean,\n): AtomFamily<Param, AtomType>\n\nexport function atomFamily<Param, AtomType extends Atom<unknown>>(\n  initializeAtom: (param: Param) => AtomType,\n  areEqual?: (a: Param, b: Param) => boolean,\n) {\n  if (import.meta.env?.MODE !== 'production' && !didWarnDeprecation) {\n    console.warn(\n      '[DEPRECATED] atomFamily is deprecated and will be removed in v3. ' +\n        'Please use the `jotai-family` package instead: https://github.com/jotaijs/jotai-family',\n    )\n    didWarnDeprecation = true\n  }\n  let shouldRemove: ShouldRemove<Param> | null = null\n  const atoms: Map<Param, [AtomType, CreatedAt]> = new Map()\n  const listeners = new Set<Callback<Param, AtomType>>()\n  const createAtom = (param: Param) => {\n    let item: [AtomType, CreatedAt] | undefined\n    if (areEqual === undefined) {\n      item = atoms.get(param)\n    } else {\n      // Custom comparator, iterate over all elements\n      for (const [key, value] of atoms) {\n        if (areEqual(key, param)) {\n          item = value\n          break\n        }\n      }\n    }\n\n    if (item !== undefined) {\n      if (shouldRemove?.(item[1], param)) {\n        createAtom.remove(param)\n      } else {\n        return item[0]\n      }\n    }\n\n    const newAtom = initializeAtom(param)\n    atoms.set(param, [newAtom, Date.now()])\n    notifyListeners('CREATE', param, newAtom)\n    return newAtom\n  }\n\n  const notifyListeners = (\n    type: 'CREATE' | 'REMOVE',\n    param: Param,\n    atom: AtomType,\n  ) => {\n    for (const listener of listeners) {\n      listener({ type, param, atom })\n    }\n  }\n\n  createAtom.unstable_listen = (callback: Callback<Param, AtomType>) => {\n    listeners.add(callback)\n    return () => {\n      listeners.delete(callback)\n    }\n  }\n\n  createAtom.getParams = () => atoms.keys()\n\n  createAtom.remove = (param: Param) => {\n    if (areEqual === undefined) {\n      if (!atoms.has(param)) return\n      const [atom] = atoms.get(param)!\n      atoms.delete(param)\n      notifyListeners('REMOVE', param, atom)\n    } else {\n      for (const [key, [atom]] of atoms) {\n        if (areEqual(key, param)) {\n          atoms.delete(key)\n          notifyListeners('REMOVE', key, atom)\n          break\n        }\n      }\n    }\n  }\n\n  createAtom.setShouldRemove = (fn: ShouldRemove<Param> | null) => {\n    shouldRemove = fn\n    if (!shouldRemove) return\n    for (const [key, [atom, createdAt]] of atoms) {\n      if (shouldRemove(createdAt, key)) {\n        atoms.delete(key)\n        notifyListeners('REMOVE', key, atom)\n      }\n    }\n  }\n  return createAtom\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithDefault.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { WritableAtom } from '../../vanilla.ts'\nimport { RESET } from './constants.ts'\n\ntype Read<Value, Args extends unknown[], Result> = WritableAtom<\n  Value,\n  Args,\n  Result\n>['read']\n\ntype DefaultSetStateAction<Value> =\n  | Value\n  | typeof RESET\n  | ((prev: Value) => Value | typeof RESET)\n\nexport function atomWithDefault<Value>(\n  getDefault: Read<Value, [DefaultSetStateAction<Value>], void>,\n): WritableAtom<Value, [DefaultSetStateAction<Value>], void> {\n  const EMPTY = Symbol()\n  const overwrittenAtom = atom<Value | typeof EMPTY>(EMPTY)\n\n  if (import.meta.env?.MODE !== 'production') {\n    overwrittenAtom.debugPrivate = true\n  }\n\n  const anAtom: WritableAtom<Value, [DefaultSetStateAction<Value>], void> =\n    atom(\n      (get, options) => {\n        const overwritten = get(overwrittenAtom)\n        if (overwritten !== EMPTY) {\n          return overwritten\n        }\n        return getDefault(get, options)\n      },\n      (get, set, update) => {\n        const newValue =\n          typeof update === 'function'\n            ? (update as (prev: Value) => Value)(get(anAtom))\n            : update\n        set(overwrittenAtom, newValue === RESET ? EMPTY : newValue)\n      },\n    )\n  return anAtom\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithLazy.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { PrimitiveAtom } from '../../vanilla.ts'\n\nexport function atomWithLazy<Value>(\n  makeInitial: () => Value,\n): PrimitiveAtom<Value> {\n  const a = atom(undefined as unknown as Value)\n  delete (a as { init?: Value }).init\n  Object.defineProperty(a, 'init', {\n    get() {\n      return makeInitial()\n    },\n  })\n  return a\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithObservable.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { Atom, Getter, WritableAtom } from '../../vanilla.ts'\n\nconst isPromiseLike = (x: unknown): x is PromiseLike<unknown> =>\n  typeof (x as any)?.then === 'function'\n\ntype Timeout = ReturnType<typeof setTimeout>\ntype AnyError = unknown\n\ntype Subscription = {\n  unsubscribe: () => void\n}\n\ntype Observer<T> = {\n  next: (value: T) => void\n  error: (error: AnyError) => void\n  complete: () => void\n}\n\ntype SubscribableObservable<T> =\n  | {\n      subscribe(observer: Observer<T>): Subscription\n    }\n  | {\n      subscribe(observer: Partial<Observer<T>>): Subscription\n    }\n  | {\n      subscribe(observer: Partial<Observer<T>>): Subscription\n      // Overload function to make typing happy\n      subscribe(next: (value: T) => void): Subscription\n    }\n\ntype SymbolObservable<T> = {\n  [Symbol.observable]: () => SubscribableObservable<T>\n}\n\ntype ObservableLike<T> = SubscribableObservable<T> | SymbolObservable<T>\n\ntype SubjectLike<T> = ObservableLike<T> & Observer<T>\n\ntype Options<Data> = {\n  initialValue?: Data | (() => Data)\n  unstable_timeout?: number\n}\n\ntype OptionsWithInitialValue<Data> = {\n  initialValue: Data | (() => Data)\n  unstable_timeout?: number\n}\n\nexport function atomWithObservable<Data>(\n  getObservable: (get: Getter) => SubjectLike<Data>,\n  options: OptionsWithInitialValue<Data>,\n): WritableAtom<Data, [Data], void>\n\nexport function atomWithObservable<Data>(\n  getObservable: (get: Getter) => SubjectLike<Data>,\n  options?: Options<Data>,\n): WritableAtom<Data | Promise<Data>, [Data], void>\n\nexport function atomWithObservable<Data>(\n  getObservable: (get: Getter) => ObservableLike<Data>,\n  options: OptionsWithInitialValue<Data>,\n): Atom<Data>\n\nexport function atomWithObservable<Data>(\n  getObservable: (get: Getter) => ObservableLike<Data>,\n  options?: Options<Data>,\n): Atom<Data | Promise<Data>>\n\nexport function atomWithObservable<Data>(\n  getObservable: (get: Getter) => ObservableLike<Data> | SubjectLike<Data>,\n  options?: Options<Data>,\n) {\n  type Result = { d: Data } | { e: AnyError }\n  const returnResultData = (result: Result) => {\n    if ('e' in result) {\n      throw result.e\n    }\n    return result.d\n  }\n\n  const observableResultAtom = atom((get) => {\n    const observable = getObservable(get)\n    const subscribable =\n      (observable as Partial<SymbolObservable<Data>>)[Symbol.observable]?.() ||\n      (observable as SubscribableObservable<Data>)\n\n    let resolve: ((result: Result) => void) | undefined\n    const makePending = () =>\n      new Promise<Result>((r) => {\n        resolve = r\n      })\n    const initialResult: Result | Promise<Result> =\n      options && 'initialValue' in options\n        ? {\n            d:\n              typeof options.initialValue === 'function'\n                ? (options.initialValue as () => Data)()\n                : (options.initialValue as Data),\n          }\n        : makePending()\n\n    let setResult: ((result: Result) => void) | undefined\n    let lastResult: Result | undefined\n    const listener = (result: Result) => {\n      lastResult = result\n      resolve?.(result)\n      setResult?.(result)\n    }\n\n    let subscription: Subscription | undefined\n    let timer: Timeout | undefined\n    const isNotMounted = () => !setResult\n    const unsubscribe = () => {\n      if (subscription) {\n        subscription.unsubscribe()\n        subscription = undefined\n      }\n    }\n    const start = () => {\n      if (subscription) {\n        clearTimeout(timer)\n        subscription.unsubscribe()\n      }\n      subscription = subscribable.subscribe({\n        next: (d) => listener({ d }),\n        error: (e) => listener({ e }),\n        complete: () => {},\n      })\n      if (isNotMounted() && options?.unstable_timeout) {\n        timer = setTimeout(unsubscribe, options.unstable_timeout)\n      }\n    }\n    start()\n\n    const resultAtom = atom(lastResult || initialResult)\n\n    if (import.meta.env?.MODE !== 'production') {\n      resultAtom.debugPrivate = true\n    }\n\n    resultAtom.onMount = (update) => {\n      setResult = update\n      if (lastResult) {\n        update(lastResult)\n      }\n      if (subscription) {\n        clearTimeout(timer)\n      } else {\n        start()\n      }\n      return () => {\n        setResult = undefined\n        if (options?.unstable_timeout) {\n          timer = setTimeout(unsubscribe, options.unstable_timeout)\n        } else {\n          unsubscribe()\n        }\n      }\n    }\n    return [resultAtom, observable, makePending, start, isNotMounted] as const\n  })\n\n  if (import.meta.env?.MODE !== 'production') {\n    observableResultAtom.debugPrivate = true\n  }\n\n  const observableAtom = atom(\n    (get) => {\n      const [resultAtom] = get(observableResultAtom)\n      const result = get(resultAtom)\n      if (isPromiseLike(result)) {\n        return result.then(returnResultData)\n      }\n      return returnResultData(result)\n    },\n    (get, set, data: Data) => {\n      const [resultAtom, observable, makePending, start, isNotMounted] =\n        get(observableResultAtom)\n      if ('next' in observable) {\n        if (isNotMounted()) {\n          set(resultAtom, makePending())\n          start()\n        }\n        observable.next(data)\n      } else {\n        throw new Error('observable is not subject')\n      }\n    },\n  )\n\n  return observableAtom\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithReducer.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { WritableAtom } from '../../vanilla.ts'\n\nexport function atomWithReducer<Value, Action>(\n  initialValue: Value,\n  reducer: (value: Value, action?: Action) => Value,\n): WritableAtom<Value, [Action?], void>\n\nexport function atomWithReducer<Value, Action>(\n  initialValue: Value,\n  reducer: (value: Value, action: Action) => Value,\n): WritableAtom<Value, [Action], void>\n\nexport function atomWithReducer<Value, Action>(\n  initialValue: Value,\n  reducer: (value: Value, action: Action) => Value,\n) {\n  return atom(initialValue, function (this: never, get, set, action: Action) {\n    set(this, reducer(get(this), action))\n  })\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithRefresh.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { WritableAtom } from '../../vanilla.ts'\n\ntype Read<Value, Args extends unknown[], Result> = WritableAtom<\n  Value,\n  Args,\n  Result\n>['read']\ntype Write<Value, Args extends unknown[], Result> = WritableAtom<\n  Value,\n  Args,\n  Result\n>['write']\n\nexport function atomWithRefresh<Value, Args extends unknown[], Result>(\n  read: Read<Value, Args, Result>,\n  write: Write<Value, Args, Result>,\n): WritableAtom<Value, Args | [], Result | void>\n\nexport function atomWithRefresh<Value>(\n  read: Read<Value, [], void>,\n): WritableAtom<Value, [], void>\n\nexport function atomWithRefresh<Value, Args extends unknown[], Result>(\n  read: Read<Value, Args, Result>,\n  write?: Write<Value, Args, Result>,\n) {\n  const refreshAtom = atom(0)\n  if (import.meta.env?.MODE !== 'production') {\n    refreshAtom.debugPrivate = true\n  }\n  return atom(\n    (get, options) => {\n      get(refreshAtom)\n      return read(get, options as never)\n    },\n    (get, set, ...args: Args) => {\n      if (args.length === 0) {\n        set(refreshAtom, (c) => c + 1)\n      } else if (write) {\n        return write(get, set, ...args)\n      } else if (import.meta.env?.MODE !== 'production') {\n        throw new Error('refresh must be called without arguments')\n      }\n    },\n  )\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithReset.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { WritableAtom } from '../../vanilla.ts'\nimport { RESET } from './constants.ts'\n\ntype SetStateActionWithReset<Value> =\n  | Value\n  | typeof RESET\n  | ((prev: Value) => Value | typeof RESET)\n\n// This is an internal type and not part of public API.\n// Do not depend on it as it can change without notice.\ntype WithInitialValue<Value> = {\n  init: Value\n}\n\nexport function atomWithReset<Value>(\n  initialValue: Value,\n): WritableAtom<Value, [SetStateActionWithReset<Value>], void> &\n  WithInitialValue<Value> {\n  type Update = SetStateActionWithReset<Value>\n  const anAtom = atom<Value, [Update], void>(\n    initialValue,\n    (get, set, update) => {\n      const nextValue =\n        typeof update === 'function'\n          ? (update as (prev: Value) => Value | typeof RESET)(get(anAtom))\n          : update\n\n      set(anAtom, nextValue === RESET ? initialValue : nextValue)\n    },\n  )\n  return anAtom as WritableAtom<Value, [Update], void> & WithInitialValue<Value>\n}\n"
  },
  {
    "path": "src/vanilla/utils/atomWithStorage.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { WritableAtom } from '../../vanilla.ts'\nimport { RESET } from './constants.ts'\n\nconst isPromiseLike = (x: unknown): x is PromiseLike<unknown> =>\n  typeof (x as any)?.then === 'function'\n\ntype Unsubscribe = () => void\n\ntype Subscribe<Value> = (\n  key: string,\n  callback: (value: Value) => void,\n  initialValue: Value,\n) => Unsubscribe | undefined\n\ntype StringSubscribe = (\n  key: string,\n  callback: (value: string | null) => void,\n) => Unsubscribe | undefined\n\ntype SetStateActionWithReset<Value> =\n  | Value\n  | typeof RESET\n  | ((prev: Value) => Value | typeof RESET)\n\nexport interface AsyncStorage<Value> {\n  getItem: (key: string, initialValue: Value) => PromiseLike<Value>\n  setItem: (key: string, newValue: Value) => PromiseLike<void>\n  removeItem: (key: string) => PromiseLike<void>\n  subscribe?: Subscribe<Value>\n}\n\nexport interface SyncStorage<Value> {\n  getItem: (key: string, initialValue: Value) => Value\n  setItem: (key: string, newValue: Value) => void\n  removeItem: (key: string) => void\n  subscribe?: Subscribe<Value>\n}\n\nexport interface AsyncStringStorage {\n  getItem: (key: string) => PromiseLike<string | null>\n  setItem: (key: string, newValue: string) => PromiseLike<void>\n  removeItem: (key: string) => PromiseLike<void>\n  subscribe?: StringSubscribe\n}\n\nexport interface SyncStringStorage {\n  getItem: (key: string) => string | null\n  setItem: (key: string, newValue: string) => void\n  removeItem: (key: string) => void\n  subscribe?: StringSubscribe\n}\n\nexport function withStorageValidator<Value>(\n  validator: (value: unknown) => value is Value,\n): {\n  (storage: AsyncStorage<unknown>): AsyncStorage<Value>\n  (storage: SyncStorage<unknown>): SyncStorage<Value>\n}\n\nexport function withStorageValidator<Value>(\n  validator: (value: unknown) => value is Value,\n) {\n  return (unknownStorage: AsyncStorage<unknown> | SyncStorage<unknown>) => {\n    const storage = {\n      ...unknownStorage,\n      getItem: (key: string, initialValue: Value) => {\n        const validate = (value: unknown) => {\n          if (!validator(value)) {\n            return initialValue\n          }\n          return value\n        }\n        const value = unknownStorage.getItem(key, initialValue)\n        if (isPromiseLike(value)) {\n          return value.then(validate)\n        }\n        return validate(value)\n      },\n    }\n    return storage\n  }\n}\n\ntype JsonStorageOptions = {\n  reviver?: (key: string, value: unknown) => unknown\n  replacer?: (key: string, value: unknown) => unknown\n}\n\nexport function createJSONStorage<Value>(): SyncStorage<Value>\n\nexport function createJSONStorage<Value>(\n  getStringStorage: () => AsyncStringStorage,\n  options?: JsonStorageOptions,\n): AsyncStorage<Value>\n\nexport function createJSONStorage<Value>(\n  getStringStorage: () => SyncStringStorage,\n  options?: JsonStorageOptions,\n): SyncStorage<Value>\n\nexport function createJSONStorage<Value>(\n  getStringStorage: () =>\n    | AsyncStringStorage\n    | SyncStringStorage\n    | undefined = () => {\n    try {\n      return window.localStorage\n    } catch (e) {\n      if (import.meta.env?.MODE !== 'production') {\n        if (typeof window !== 'undefined') {\n          console.warn(e)\n        }\n      }\n      return undefined\n    }\n  },\n  options?: JsonStorageOptions,\n): AsyncStorage<Value> | SyncStorage<Value> {\n  let lastStr: string | undefined\n  let lastValue: Value\n\n  const storage: AsyncStorage<Value> | SyncStorage<Value> = {\n    getItem: (key, initialValue) => {\n      const parse = (str: string | null) => {\n        str = str || ''\n        if (lastStr !== str) {\n          try {\n            lastValue = JSON.parse(str, options?.reviver)\n          } catch {\n            return initialValue\n          }\n          lastStr = str\n        }\n        return lastValue\n      }\n      const str = getStringStorage()?.getItem(key) ?? null\n      if (isPromiseLike(str)) {\n        return str.then(parse) as never\n      }\n      return parse(str) as never\n    },\n    setItem: (key, newValue) =>\n      getStringStorage()?.setItem(\n        key,\n        JSON.stringify(newValue, options?.replacer),\n      ),\n    removeItem: (key) => getStringStorage()?.removeItem(key),\n  }\n\n  const createHandleSubscribe =\n    (subscriber: StringSubscribe): Subscribe<Value> =>\n    (key, callback, initialValue) =>\n      subscriber(key, (v) => {\n        let newValue: Value\n        try {\n          newValue = JSON.parse(v || '')\n        } catch {\n          newValue = initialValue\n        }\n        callback(newValue)\n      })\n\n  let subscriber: StringSubscribe | undefined\n  try {\n    subscriber = getStringStorage()?.subscribe\n  } catch {\n    // ignore\n  }\n  if (\n    !subscriber &&\n    typeof window !== 'undefined' &&\n    typeof window.addEventListener === 'function' &&\n    window.Storage\n  ) {\n    subscriber = (key, callback) => {\n      if (!(getStringStorage() instanceof window.Storage)) {\n        return () => {}\n      }\n      const storageEventCallback = (e: StorageEvent) => {\n        if (e.storageArea === getStringStorage() && e.key === key) {\n          callback(e.newValue)\n        }\n      }\n      window.addEventListener('storage', storageEventCallback)\n      return () => {\n        window.removeEventListener('storage', storageEventCallback)\n      }\n    }\n  }\n\n  if (subscriber) {\n    storage.subscribe = createHandleSubscribe(subscriber)\n  }\n  return storage\n}\n\nconst defaultStorage = createJSONStorage()\n\nexport function atomWithStorage<Value>(\n  key: string,\n  initialValue: Value,\n  storage: AsyncStorage<Value>,\n  options?: { getOnInit?: boolean },\n): WritableAtom<\n  Value | Promise<Value>,\n  [SetStateActionWithReset<Value | Promise<Value>>],\n  Promise<void>\n>\n\nexport function atomWithStorage<Value>(\n  key: string,\n  initialValue: Value,\n  storage?: SyncStorage<Value>,\n  options?: { getOnInit?: boolean },\n): WritableAtom<Value, [SetStateActionWithReset<Value>], void>\n\nexport function atomWithStorage<Value>(\n  key: string,\n  initialValue: Value,\n  storage:\n    | SyncStorage<Value>\n    | AsyncStorage<Value> = defaultStorage as SyncStorage<Value>,\n  options?: { getOnInit?: boolean },\n) {\n  const getOnInit = options?.getOnInit\n  const baseAtom = atom(\n    getOnInit\n      ? (storage.getItem(key, initialValue) as Value | Promise<Value>)\n      : initialValue,\n  )\n\n  if (import.meta.env?.MODE !== 'production') {\n    baseAtom.debugPrivate = true\n  }\n\n  baseAtom.onMount = (setAtom) => {\n    setAtom(storage.getItem(key, initialValue) as Value | Promise<Value>)\n    return storage.subscribe?.(key, setAtom, initialValue)\n  }\n\n  const anAtom = atom(\n    (get) => get(baseAtom),\n    (get, set, update: SetStateActionWithReset<Value | Promise<Value>>) => {\n      const nextValue =\n        typeof update === 'function'\n          ? (\n              update as (\n                prev: Value | Promise<Value>,\n              ) => Value | Promise<Value> | typeof RESET\n            )(get(baseAtom))\n          : update\n      if (nextValue === RESET) {\n        set(baseAtom, initialValue)\n        return storage.removeItem(key)\n      }\n      if (isPromiseLike(nextValue)) {\n        return nextValue.then((resolvedValue) => {\n          set(baseAtom, resolvedValue)\n          return storage.setItem(key, resolvedValue)\n        })\n      }\n      set(baseAtom, nextValue)\n      return storage.setItem(key, nextValue)\n    },\n  )\n\n  return anAtom as never\n}\n"
  },
  {
    "path": "src/vanilla/utils/constants.ts",
    "content": "export const RESET: unique symbol = Symbol(\n  import.meta.env?.MODE !== 'production' ? 'RESET' : '',\n)\n"
  },
  {
    "path": "src/vanilla/utils/freezeAtom.ts",
    "content": "import type { Atom, WritableAtom } from '../../vanilla.ts'\n\nconst frozenAtoms = new WeakSet<Atom<any>>()\n\nconst deepFreeze = <T>(value: T): T => {\n  if (typeof value !== 'object' || value === null) {\n    return value\n  }\n  Object.freeze(value)\n  const propNames = Object.getOwnPropertyNames(value)\n  for (const name of propNames) {\n    deepFreeze((value as never)[name])\n  }\n  return value\n}\n\nexport function freezeAtom<AtomType extends Atom<unknown>>(\n  anAtom: AtomType,\n): AtomType\n\nexport function freezeAtom(\n  anAtom: WritableAtom<unknown, unknown[], unknown>,\n): WritableAtom<unknown, unknown[], unknown> {\n  if (frozenAtoms.has(anAtom)) {\n    return anAtom\n  }\n  frozenAtoms.add(anAtom)\n\n  const origRead = anAtom.read\n  anAtom.read = function (get, options) {\n    return deepFreeze(origRead.call(this, get, options))\n  }\n  if ('write' in anAtom) {\n    const origWrite = anAtom.write\n    anAtom.write = function (get, set, ...args) {\n      return origWrite.call(\n        this,\n        get,\n        (...setArgs) => {\n          if (setArgs[0] === anAtom) {\n            setArgs[1] = deepFreeze(setArgs[1])\n          }\n\n          return set(...setArgs)\n        },\n        ...args,\n      )\n    }\n  }\n  return anAtom\n}\n\n/**\n * @deprecated Define it on users end\n */\nexport function freezeAtomCreator<\n  CreateAtom extends (...args: unknown[]) => Atom<unknown>,\n>(createAtom: CreateAtom): CreateAtom {\n  if (import.meta.env?.MODE !== 'production') {\n    console.warn(\n      '[DEPRECATED] freezeAtomCreator is deprecated, define it on users end',\n    )\n  }\n  return ((...args: unknown[]) => freezeAtom(createAtom(...args))) as never\n}\n"
  },
  {
    "path": "src/vanilla/utils/loadable.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { Atom } from '../../vanilla.ts'\nimport { unwrap } from './unwrap.ts'\n\nconst cache1 = new WeakMap()\nconst memo1 = <T>(create: () => T, dep1: object): T =>\n  (cache1.has(dep1) ? cache1 : cache1.set(dep1, create())).get(dep1)\n\nexport type Loadable<Value> =\n  | { state: 'loading' }\n  | { state: 'hasError'; error: unknown }\n  | { state: 'hasData'; data: Awaited<Value> }\n\nlet didWarnDeprecation = false\n\n/**\n * @deprecated `loadable` is deprecated infavor of `unwrap`.\n *\n * Userland implementation of loadable:\n * ```js\n * function loadable(anAtom) {\n *   const LOADING = { state: 'loading' }\n *   const unwrappedAtom = unwrap(anAtom, () => LOADING)\n *   return atom((get) => {\n *     try {\n *       const data = get(unwrappedAtom)\n *       if (data === LOADING) {\n *         return LOADING\n *       }\n *       return { state: 'hasData', data }\n *     } catch (error) {\n *       return { state: 'hasError', error }\n *     }\n *   })\n * }\n * ```\n */\nexport function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>> {\n  if (import.meta.env?.MODE !== 'production' && !didWarnDeprecation) {\n    console.warn(\n      '[DEPRECATED] loadable is deprecated and will be removed in v3. ' +\n        'Please use a userland util with the `unwrap` util: https://github.com/pmndrs/jotai/pull/3217',\n    )\n    didWarnDeprecation = true\n  }\n  return memo1(() => {\n    const LOADING: Loadable<Value> = { state: 'loading' }\n    const unwrappedAtom = unwrap(anAtom, () => LOADING)\n    return atom((get) => {\n      try {\n        const data = get(unwrappedAtom)\n        if (data === LOADING) {\n          return LOADING\n        }\n        return { state: 'hasData', data } as Loadable<Value>\n      } catch (error) {\n        return { state: 'hasError', error }\n      }\n    })\n  }, anAtom)\n}\n"
  },
  {
    "path": "src/vanilla/utils/selectAtom.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { Atom } from '../../vanilla.ts'\n\nconst getCached = <T>(c: () => T, m: WeakMap<object, T>, k: object): T =>\n  (m.has(k) ? m : m.set(k, c())).get(k) as T\nconst cache1 = new WeakMap()\nconst memo3 = <T>(\n  create: () => T,\n  dep1: object,\n  dep2: object,\n  dep3: object,\n): T => {\n  const cache2 = getCached(() => new WeakMap(), cache1, dep1)\n  const cache3 = getCached(() => new WeakMap(), cache2, dep2)\n  return getCached(create, cache3, dep3)\n}\n\nexport function selectAtom<Value, Slice>(\n  anAtom: Atom<Value>,\n  selector: (v: Value, prevSlice?: Slice) => Slice,\n  equalityFn?: (a: Slice, b: Slice) => boolean,\n): Atom<Slice>\n\nexport function selectAtom<Value, Slice>(\n  anAtom: Atom<Value>,\n  selector: (v: Value, prevSlice?: Slice) => Slice,\n  equalityFn: (prevSlice: Slice, slice: Slice) => boolean = Object.is,\n) {\n  return memo3(\n    () => {\n      const EMPTY = Symbol()\n      const selectValue = ([value, prevSlice]: readonly [\n        Value,\n        Slice | typeof EMPTY,\n      ]) => {\n        if (prevSlice === EMPTY) {\n          return selector(value)\n        }\n        const slice = selector(value, prevSlice)\n        return equalityFn(prevSlice, slice) ? prevSlice : slice\n      }\n      const derivedAtom: Atom<Slice | typeof EMPTY> & {\n        init?: typeof EMPTY\n      } = atom((get) => {\n        const prev = get(derivedAtom)\n        const value = get(anAtom)\n        return selectValue([value, prev] as const)\n      })\n      // HACK to read derived atom before initialization\n      derivedAtom.init = EMPTY\n      return derivedAtom\n    },\n    anAtom,\n    selector,\n    equalityFn,\n  )\n}\n"
  },
  {
    "path": "src/vanilla/utils/splitAtom.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type {\n  Atom,\n  Getter,\n  PrimitiveAtom,\n  SetStateAction,\n  Setter,\n  WritableAtom,\n} from '../../vanilla.ts'\n\nconst getCached = <T>(c: () => T, m: WeakMap<object, T>, k: object): T =>\n  (m.has(k) ? m : m.set(k, c())).get(k) as T\nconst cache1 = new WeakMap()\nconst memo2 = <T>(create: () => T, dep1: object, dep2: object): T => {\n  const cache2 = getCached(() => new WeakMap(), cache1, dep1)\n  return getCached(create, cache2, dep2)\n}\nconst cacheKeyForEmptyKeyExtractor = {}\n\nconst isWritable = <Value, Args extends unknown[], Result>(\n  atom: Atom<Value> | WritableAtom<Value, Args, Result>,\n): atom is WritableAtom<Value, Args, Result> =>\n  !!(atom as WritableAtom<Value, Args, Result>).write\n\nconst isFunction = <T>(x: T): x is T & ((...args: never[]) => unknown) =>\n  typeof x === 'function'\n\ntype SplitAtomAction<Item> =\n  | { type: 'remove'; atom: PrimitiveAtom<Item> }\n  | {\n      type: 'insert'\n      value: Item\n      before?: PrimitiveAtom<Item>\n    }\n  | {\n      type: 'move'\n      atom: PrimitiveAtom<Item>\n      before?: PrimitiveAtom<Item>\n    }\n\nexport function splitAtom<Item, Key>(\n  arrAtom: WritableAtom<Item[], [Item[]], void>,\n  keyExtractor?: (item: Item) => Key,\n): WritableAtom<PrimitiveAtom<Item>[], [SplitAtomAction<Item>], void>\n\nexport function splitAtom<Item, Key>(\n  arrAtom: Atom<Item[]>,\n  keyExtractor?: (item: Item) => Key,\n): Atom<Atom<Item>[]>\n\nexport function splitAtom<Item, Key>(\n  arrAtom: WritableAtom<Item[], [Item[]], void> | Atom<Item[]>,\n  keyExtractor?: (item: Item) => Key,\n) {\n  return memo2(\n    () => {\n      type ItemAtom = PrimitiveAtom<Item> | Atom<Item>\n      type Mapping = {\n        arr: Item[]\n        atomList: ItemAtom[]\n        keyList: Key[]\n      }\n      const mappingCache = new WeakMap<Item[], Mapping>()\n      const getMapping = (arr: Item[], prev?: Item[]) => {\n        let mapping = mappingCache.get(arr)\n        if (mapping) {\n          return mapping\n        }\n        const prevMapping = prev && mappingCache.get(prev)\n        const atomList: Atom<Item>[] = []\n        const keyList: Key[] = []\n        arr.forEach((item, index) => {\n          const key = keyExtractor\n            ? keyExtractor(item)\n            : (index as unknown as Key)\n          keyList[index] = key\n          const cachedAtom =\n            prevMapping &&\n            prevMapping.atomList[prevMapping.keyList.indexOf(key)]\n          if (cachedAtom) {\n            atomList[index] = cachedAtom\n            return\n          }\n          const read = (get: Getter) => {\n            const prev = get(mappingAtom) as Mapping | undefined\n            const currArr = get(arrAtom)\n            const mapping = getMapping(currArr, prev?.arr)\n            const index = mapping.keyList.indexOf(key)\n            if (index < 0 || index >= currArr.length) {\n              // returning a stale value to avoid errors for use cases such as react-spring\n              const prevItem = arr[getMapping(arr).keyList.indexOf(key)]\n              if (prevItem) {\n                return prevItem\n              }\n              throw new Error('splitAtom: index out of bounds for read')\n            }\n            return currArr[index]!\n          }\n          const write = (\n            get: Getter,\n            set: Setter,\n            update: SetStateAction<Item>,\n          ) => {\n            const prev = get(mappingAtom) as Mapping | undefined\n            const arr = get(arrAtom)\n            const mapping = getMapping(arr, prev?.arr)\n            const index = mapping.keyList.indexOf(key)\n            if (index < 0 || index >= arr.length) {\n              throw new Error('splitAtom: index out of bounds for write')\n            }\n            const nextItem = isFunction(update)\n              ? (update as (prev: Item) => Item)(arr[index]!)\n              : update\n            if (!Object.is(arr[index], nextItem)) {\n              set(arrAtom as WritableAtom<Item[], [Item[]], void>, [\n                ...arr.slice(0, index),\n                nextItem,\n                ...arr.slice(index + 1),\n              ])\n            }\n          }\n          atomList[index] = isWritable(arrAtom) ? atom(read, write) : atom(read)\n        })\n        if (\n          prevMapping &&\n          prevMapping.keyList.length === keyList.length &&\n          prevMapping.keyList.every((x, i) => x === keyList[i])\n        ) {\n          // not changed\n          mapping = prevMapping\n        } else {\n          mapping = { arr, atomList, keyList }\n        }\n        mappingCache.set(arr, mapping)\n        return mapping\n      }\n      const mappingAtom: Atom<Mapping> & {\n        init?: undefined\n      } = atom((get) => {\n        const prev = get(mappingAtom) as Mapping | undefined\n        const arr = get(arrAtom)\n        const mapping = getMapping(arr, prev?.arr)\n        return mapping\n      })\n\n      if (import.meta.env?.MODE !== 'production') {\n        mappingAtom.debugPrivate = true\n      }\n\n      // HACK to read mapping atom before initialization\n      mappingAtom.init = undefined\n      const splittedAtom = isWritable(arrAtom)\n        ? atom(\n            (get) => get(mappingAtom).atomList,\n            (get, set, action: SplitAtomAction<Item>) => {\n              switch (action.type) {\n                case 'remove': {\n                  const index = get(splittedAtom).indexOf(action.atom)\n                  if (index >= 0) {\n                    const arr = get(arrAtom)\n                    set(arrAtom as WritableAtom<Item[], [Item[]], void>, [\n                      ...arr.slice(0, index),\n                      ...arr.slice(index + 1),\n                    ])\n                  }\n                  break\n                }\n                case 'insert': {\n                  const index = action.before\n                    ? get(splittedAtom).indexOf(action.before)\n                    : get(splittedAtom).length\n                  if (index >= 0) {\n                    const arr = get(arrAtom)\n                    set(arrAtom as WritableAtom<Item[], [Item[]], void>, [\n                      ...arr.slice(0, index),\n                      action.value,\n                      ...arr.slice(index),\n                    ])\n                  }\n                  break\n                }\n                case 'move': {\n                  const index1 = get(splittedAtom).indexOf(action.atom)\n                  const index2 = action.before\n                    ? get(splittedAtom).indexOf(action.before)\n                    : get(splittedAtom).length\n                  if (index1 >= 0 && index2 >= 0) {\n                    const arr = get(arrAtom)\n                    if (index1 < index2) {\n                      set(arrAtom as WritableAtom<Item[], [Item[]], void>, [\n                        ...arr.slice(0, index1),\n                        ...arr.slice(index1 + 1, index2),\n                        arr[index1]!,\n                        ...arr.slice(index2),\n                      ])\n                    } else {\n                      set(arrAtom as WritableAtom<Item[], [Item[]], void>, [\n                        ...arr.slice(0, index2),\n                        arr[index1]!,\n                        ...arr.slice(index2, index1),\n                        ...arr.slice(index1 + 1),\n                      ])\n                    }\n                  }\n                  break\n                }\n              }\n            },\n          )\n        : atom((get) => get(mappingAtom).atomList) // read-only atom\n      return splittedAtom\n    },\n    arrAtom,\n    keyExtractor || cacheKeyForEmptyKeyExtractor,\n  )\n}\n"
  },
  {
    "path": "src/vanilla/utils/unwrap.ts",
    "content": "import { atom } from '../../vanilla.ts'\nimport type { Atom, WritableAtom } from '../../vanilla.ts'\n\nconst getCached = <T>(c: () => T, m: WeakMap<object, T>, k: object): T =>\n  (m.has(k) ? m : m.set(k, c())).get(k) as T\nconst cache1 = new WeakMap()\nconst memo2 = <T>(create: () => T, dep1: object, dep2: object): T => {\n  const cache2 = getCached(() => new WeakMap(), cache1, dep1)\n  return getCached(create, cache2, dep2)\n}\n\nconst isPromiseLike = (p: unknown): p is PromiseLike<unknown> =>\n  typeof (p as any)?.then === 'function'\n\nconst defaultFallback = () => undefined\n\nexport function unwrap<Value, Args extends unknown[], Result>(\n  anAtom: WritableAtom<Value, Args, Result>,\n): WritableAtom<Awaited<Value> | undefined, Args, Result>\n\nexport function unwrap<Value, Args extends unknown[], Result, PendingValue>(\n  anAtom: WritableAtom<Value, Args, Result>,\n  fallback: (prev?: Awaited<Value>) => PendingValue,\n): WritableAtom<Awaited<Value> | PendingValue, Args, Result>\n\nexport function unwrap<Value>(\n  anAtom: Atom<Value>,\n): Atom<Awaited<Value> | undefined>\n\nexport function unwrap<Value, PendingValue>(\n  anAtom: Atom<Value>,\n  fallback: (prev?: Awaited<Value>) => PendingValue,\n): Atom<Awaited<Value> | PendingValue>\n\nexport function unwrap<Value, Args extends unknown[], Result, PendingValue>(\n  anAtom: WritableAtom<Value, Args, Result> | Atom<Value>,\n  fallback: (prev?: Awaited<Value>) => PendingValue = defaultFallback as never,\n) {\n  return memo2(\n    () => {\n      type PromiseAndValue = { readonly p?: PromiseLike<unknown> } & (\n        | { readonly v: Awaited<Value> }\n        | { readonly f: PendingValue; readonly v?: Awaited<Value> }\n      )\n      const promiseErrorCache = new WeakMap<PromiseLike<unknown>, unknown>()\n      const promiseResultCache = new WeakMap<\n        PromiseLike<unknown>,\n        Awaited<Value>\n      >()\n      const refreshAtom = atom(0)\n      const triggerRefreshAtom = atom([] as [triggerRefresh?: () => void])\n      triggerRefreshAtom.INTERNAL_onInit = (store) => {\n        store.set(triggerRefreshAtom, [\n          () => store.set(refreshAtom, (c) => c + 1),\n        ])\n      }\n\n      if (import.meta.env?.MODE !== 'production') {\n        refreshAtom.debugPrivate = true\n        triggerRefreshAtom.debugPrivate = true\n      }\n\n      const promiseAndValueAtom: Atom<PromiseAndValue> & {\n        init?: undefined\n      } = atom((get) => {\n        get(refreshAtom)\n        let prev: PromiseAndValue | undefined\n        try {\n          prev = get(promiseAndValueAtom) as PromiseAndValue | undefined\n        } catch {\n          // ignore previous errors to avoid getting stuck in error state\n        }\n        const promise = get(anAtom)\n        if (!isPromiseLike(promise)) {\n          return { v: promise as Awaited<Value> }\n        }\n        if (promise !== prev?.p) {\n          promise.then(\n            (v) => {\n              promiseResultCache.set(promise, v as Awaited<Value>)\n              const [triggerRefresh] = get(triggerRefreshAtom)\n              triggerRefresh!()\n            },\n            (e) => {\n              promiseErrorCache.set(promise, e)\n              const [triggerRefresh] = get(triggerRefreshAtom)\n              triggerRefresh!()\n            },\n          )\n        }\n        if (promiseErrorCache.has(promise)) {\n          throw promiseErrorCache.get(promise)\n        }\n        if (promiseResultCache.has(promise)) {\n          return {\n            p: promise,\n            v: promiseResultCache.get(promise) as Awaited<Value>,\n          }\n        }\n        if (prev && 'v' in prev) {\n          return { p: promise, f: fallback(prev.v), v: prev.v }\n        }\n        return { p: promise, f: fallback() }\n      })\n      // HACK to read PromiseAndValue atom before initialization\n      promiseAndValueAtom.init = undefined\n\n      if (import.meta.env?.MODE !== 'production') {\n        promiseAndValueAtom.debugPrivate = true\n      }\n\n      return atom(\n        (get) => {\n          const state = get(promiseAndValueAtom)\n          if ('f' in state) {\n            // is pending\n            return state.f\n          }\n          return state.v\n        },\n        (_get, set, ...args) =>\n          set(anAtom as WritableAtom<Value, unknown[], unknown>, ...args),\n      )\n    },\n    anAtom,\n    fallback,\n  )\n}\n"
  },
  {
    "path": "src/vanilla/utils.ts",
    "content": "export { RESET } from './utils/constants.ts'\nexport { atomWithReset } from './utils/atomWithReset.ts'\nexport { atomWithReducer } from './utils/atomWithReducer.ts'\nexport { atomFamily } from './utils/atomFamily.ts'\nexport { selectAtom } from './utils/selectAtom.ts'\nexport { freezeAtom, freezeAtomCreator } from './utils/freezeAtom.ts'\nexport { splitAtom } from './utils/splitAtom.ts'\nexport { atomWithDefault } from './utils/atomWithDefault.ts'\nexport {\n  atomWithStorage,\n  createJSONStorage,\n  withStorageValidator as unstable_withStorageValidator,\n} from './utils/atomWithStorage.ts'\nexport { atomWithObservable } from './utils/atomWithObservable.ts'\nexport { loadable } from './utils/loadable.ts'\nexport { unwrap } from './utils/unwrap.ts'\nexport { atomWithRefresh } from './utils/atomWithRefresh.ts'\nexport { atomWithLazy } from './utils/atomWithLazy.ts'\n"
  },
  {
    "path": "src/vanilla.ts",
    "content": "export { atom } from './vanilla/atom.ts'\nexport type { Atom, WritableAtom, PrimitiveAtom } from './vanilla/atom.ts'\n\nexport {\n  createStore,\n  getDefaultStore,\n  INTERNAL_overrideCreateStore,\n} from './vanilla/store.ts'\n\nexport type {\n  Getter,\n  Setter,\n  ExtractAtomValue,\n  ExtractAtomArgs,\n  ExtractAtomResult,\n  SetStateAction,\n} from './vanilla/typeUtils.ts'\n"
  },
  {
    "path": "tests/babel/plugin-debug-label.test.ts",
    "content": "import { transformSync } from '@babel/core'\nimport { expect, it } from 'vitest'\nimport plugin from 'jotai/babel/plugin-debug-label'\n\nconst transform = (\n  code: string,\n  filename?: string,\n  customAtomNames?: string[],\n) =>\n  transformSync(code, {\n    babelrc: false,\n    configFile: false,\n    filename,\n    plugins: [[plugin, { customAtomNames }]],\n  })?.code\n\nit('Should add a debugLabel to an atom', () => {\n  expect(transform(`const countAtom = atom(0);`)).toMatchInlineSnapshot(`\n    \"const countAtom = atom(0);\n    countAtom.debugLabel = \"countAtom\";\"\n  `)\n})\n\nit('Should handle a atom from a default export', () => {\n  expect(transform(`const countAtom = jotai.atom(0);`)).toMatchInlineSnapshot(`\n    \"const countAtom = jotai.atom(0);\n    countAtom.debugLabel = \"countAtom\";\"\n  `)\n})\n\nit('Should handle a atom being exported', () => {\n  expect(transform(`export const countAtom = atom(0);`)).toMatchInlineSnapshot(`\n    \"export const countAtom = atom(0);\n    countAtom.debugLabel = \"countAtom\";\"\n  `)\n})\n\nit('Should handle a default exported atom', () => {\n  expect(transform(`export default atom(0);`, 'countAtom.ts'))\n    .toMatchInlineSnapshot(`\n      \"const countAtom = atom(0);\n      countAtom.debugLabel = \"countAtom\";\n      export default countAtom;\"\n    `)\n})\n\nit('Should handle a default exported atom in a barrel file', () => {\n  expect(transform(`export default atom(0);`, 'atoms/index.ts'))\n    .toMatchInlineSnapshot(`\n      \"const atoms = atom(0);\n      atoms.debugLabel = \"atoms\";\n      export default atoms;\"\n    `)\n})\n\nit('Should handle all types of exports', () => {\n  expect(\n    transform(\n      `\n      export const countAtom = atom(0);\n      export default atom(0);\n    `,\n      'atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"export const countAtom = atom(0);\n    countAtom.debugLabel = \"countAtom\";\n    const atoms = atom(0);\n    atoms.debugLabel = \"atoms\";\n    export default atoms;\"\n  `)\n})\n\nit('Should handle all atom types', () => {\n  expect(\n    transform(\n      `\n      export const countAtom = atom(0);\n      const myFamily = atomFamily((param) => atom(param));\n      const countAtomWithDefault = atomWithDefault((get) => get(countAtom) * 2);\n      const observableAtom = atomWithObservable(() => {});\n      const reducerAtom = atomWithReducer(0, () => {});\n      const resetAtom = atomWithReset(0);\n      const storageAtom = atomWithStorage('count', 1);\n      const freezedAtom = freezeAtom(atom({ count: 0 }));\n      const loadedAtom = loadable(countAtom);\n      const selectedValueAtom = selectAtom(atom({ a: 0, b: 'othervalue' }), (v) => v.a);\n      const splittedAtom = splitAtom(atom([]));\n\n      const unwrappedAtom = unwrap(asyncArrayAtom, () => []);\n\n      const someatomWithSubscription = atomWithSubscription(() => {});\n\n      const someAtomWithStore = atomWithStore(() => {});\n\n      const someAtomWithHash = atomWithHash('', '');\n      \n      const someAtomWithLocation = atomWithLocation();\n\n      const someFocusAtom = focusAtom(someAtom, () => {});\n\n      const someAtomWithValidate = atomWithValidate('', {});\n\n      const someValidateAtoms = validateAtoms({}, () => {});\n\n      const someAtomWithCache = atomWithCache(async () => {});\n\n      const someAtomWithRecoilValue = atomWithRecoilValue({});\n      \n    `,\n      'atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"export const countAtom = atom(0);\n    countAtom.debugLabel = \"countAtom\";\n    const myFamily = atomFamily(param => atom(param));\n    myFamily.debugLabel = \"myFamily\";\n    const countAtomWithDefault = atomWithDefault(get => get(countAtom) * 2);\n    countAtomWithDefault.debugLabel = \"countAtomWithDefault\";\n    const observableAtom = atomWithObservable(() => {});\n    observableAtom.debugLabel = \"observableAtom\";\n    const reducerAtom = atomWithReducer(0, () => {});\n    reducerAtom.debugLabel = \"reducerAtom\";\n    const resetAtom = atomWithReset(0);\n    resetAtom.debugLabel = \"resetAtom\";\n    const storageAtom = atomWithStorage('count', 1);\n    storageAtom.debugLabel = \"storageAtom\";\n    const freezedAtom = freezeAtom(atom({\n      count: 0\n    }));\n    freezedAtom.debugLabel = \"freezedAtom\";\n    const loadedAtom = loadable(countAtom);\n    loadedAtom.debugLabel = \"loadedAtom\";\n    const selectedValueAtom = selectAtom(atom({\n      a: 0,\n      b: 'othervalue'\n    }), v => v.a);\n    selectedValueAtom.debugLabel = \"selectedValueAtom\";\n    const splittedAtom = splitAtom(atom([]));\n    splittedAtom.debugLabel = \"splittedAtom\";\n    const unwrappedAtom = unwrap(asyncArrayAtom, () => []);\n    unwrappedAtom.debugLabel = \"unwrappedAtom\";\n    const someatomWithSubscription = atomWithSubscription(() => {});\n    someatomWithSubscription.debugLabel = \"someatomWithSubscription\";\n    const someAtomWithStore = atomWithStore(() => {});\n    someAtomWithStore.debugLabel = \"someAtomWithStore\";\n    const someAtomWithHash = atomWithHash('', '');\n    someAtomWithHash.debugLabel = \"someAtomWithHash\";\n    const someAtomWithLocation = atomWithLocation();\n    someAtomWithLocation.debugLabel = \"someAtomWithLocation\";\n    const someFocusAtom = focusAtom(someAtom, () => {});\n    someFocusAtom.debugLabel = \"someFocusAtom\";\n    const someAtomWithValidate = atomWithValidate('', {});\n    someAtomWithValidate.debugLabel = \"someAtomWithValidate\";\n    const someValidateAtoms = validateAtoms({}, () => {});\n    someValidateAtoms.debugLabel = \"someValidateAtoms\";\n    const someAtomWithCache = atomWithCache(async () => {});\n    someAtomWithCache.debugLabel = \"someAtomWithCache\";\n    const someAtomWithRecoilValue = atomWithRecoilValue({});\n    someAtomWithRecoilValue.debugLabel = \"someAtomWithRecoilValue\";\"\n  `)\n})\n\nit('Handles custom atom names a debugLabel to an atom', () => {\n  expect(\n    transform(`const mySpecialThing = myCustomAtom(0);`, undefined, [\n      'myCustomAtom',\n    ]),\n  ).toMatchInlineSnapshot(`\n    \"const mySpecialThing = myCustomAtom(0);\n    mySpecialThing.debugLabel = \"mySpecialThing\";\"\n  `)\n})\n"
  },
  {
    "path": "tests/babel/plugin-react-refresh.test.ts",
    "content": "import { transformSync } from '@babel/core'\nimport { expect, it } from 'vitest'\nimport plugin from 'jotai/babel/plugin-react-refresh'\n\nconst transform = (\n  code: string,\n  filename?: string,\n  customAtomNames?: string[],\n) =>\n  transformSync(code, {\n    babelrc: false,\n    configFile: false,\n    filename,\n    root: '.',\n    plugins: [[plugin, { customAtomNames }]],\n  })?.code\n\nit('Should add a cache for a single atom', () => {\n  expect(transform(`const countAtom = atom(0);`, '/src/atoms/index.ts'))\n    .toMatchInlineSnapshot(`\n      \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n        cache: new Map(),\n        get(name, inst) {\n          if (this.cache.has(name)) {\n            return this.cache.get(name);\n          }\n          this.cache.set(name, inst);\n          return inst;\n        }\n      };\n      const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\"\n    `)\n})\n\nit('Should add a cache for multiple atoms', () => {\n  expect(\n    transform(\n      `\n  const countAtom = atom(0);\n  const doubleAtom = atom((get) => get(countAtom) * 2);\n  `,\n      '/src/atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\n    const doubleAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/doubleAtom\", atom(get => get(countAtom) * 2));\"\n  `)\n})\n\nit('Should add a cache for multiple exported atoms', () => {\n  expect(\n    transform(\n      `\n  export const countAtom = atom(0);\n  export const doubleAtom = atom((get) => get(countAtom) * 2);\n  `,\n      '/src/atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    export const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\n    export const doubleAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/doubleAtom\", atom(get => get(countAtom) * 2));\"\n  `)\n})\n\nit('Should add a cache for a default exported atom', () => {\n  expect(transform(`export default atom(0);`, '/src/atoms/index.ts'))\n    .toMatchInlineSnapshot(`\n      \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n        cache: new Map(),\n        get(name, inst) {\n          if (this.cache.has(name)) {\n            return this.cache.get(name);\n          }\n          this.cache.set(name, inst);\n          return inst;\n        }\n      };\n      export default globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/defaultExport\", atom(0));\"\n    `)\n})\n\nit('Should add a cache for mixed exports of atoms', () => {\n  expect(\n    transform(\n      `\n  export const countAtom = atom(0);\n  export default atom((get) => get(countAtom) * 2);\n  `,\n      '/src/atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    export const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\n    export default globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/defaultExport\", atom(get => get(countAtom) * 2));\"\n  `)\n})\n\nit('Should fail if no filename is available', () => {\n  expect(() => transform(`const countAtom = atom(0);`)).toThrow(\n    'Filename must be available',\n  )\n})\n\nit('Should handle atoms returned from functions (#891)', () => {\n  expect(\n    transform(\n      `function createAtom(label) {\n    const anAtom = atom(0);\n    anAtom.debugLabel = label;\n    return anAtom;\n  }\n  \n  const countAtom = atom(0);\n  const countAtom2 = createAtom(\"countAtom2\");\n  const countAtom3 = createAtom(\"countAtom3\");`,\n      '/src/atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    function createAtom(label) {\n      const anAtom = atom(0);\n      anAtom.debugLabel = label;\n      return anAtom;\n    }\n    const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\n    const countAtom2 = createAtom(\"countAtom2\");\n    const countAtom3 = createAtom(\"countAtom3\");\"\n  `)\n})\n\nit('Should handle custom atom names', () => {\n  expect(\n    transform(\n      `const mySpecialThing = myCustomAtom(0);`,\n      '/src/atoms/index.ts',\n      ['myCustomAtom'],\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    const mySpecialThing = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/mySpecialThing\", myCustomAtom(0));\"\n  `)\n})\n"
  },
  {
    "path": "tests/babel/preset.test.ts",
    "content": "import { transformSync } from '@babel/core'\nimport { expect, it } from 'vitest'\nimport preset from 'jotai/babel/preset'\n\nconst transform = (\n  code: string,\n  filename?: string,\n  customAtomNames?: string[],\n) =>\n  transformSync(code, {\n    babelrc: false,\n    configFile: false,\n    filename,\n    presets: [[preset, { customAtomNames }]],\n  })?.code\n\nit('Should add a debugLabel and cache to an atom', () => {\n  expect(transform(`const countAtom = atom(0);`, '/src/atoms.ts'))\n    .toMatchInlineSnapshot(`\n      \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n        cache: new Map(),\n        get(name, inst) {\n          if (this.cache.has(name)) {\n            return this.cache.get(name);\n          }\n          this.cache.set(name, inst);\n          return inst;\n        }\n      };\n      const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms.ts/countAtom\", atom(0));\n      countAtom.debugLabel = \"countAtom\";\"\n    `)\n})\n\nit('Should add a debugLabel and cache to multiple atoms', () => {\n  expect(\n    transform(\n      `\n  const countAtom = atom(0);\n  const doubleAtom = atom((get) => get(countAtom) * 2);`,\n      '/src/atoms.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms.ts/countAtom\", atom(0));\n    countAtom.debugLabel = \"countAtom\";\n    const doubleAtom = globalThis.jotaiAtomCache.get(\"/src/atoms.ts/doubleAtom\", atom(get => get(countAtom) * 2));\n    doubleAtom.debugLabel = \"doubleAtom\";\"\n  `)\n})\n\nit('Should add a cache and debugLabel for multiple exported atoms', () => {\n  expect(\n    transform(\n      `\n  export const countAtom = atom(0);\n  export const doubleAtom = atom((get) => get(countAtom) * 2);\n  `,\n      '/src/atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    export const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\n    countAtom.debugLabel = \"countAtom\";\n    export const doubleAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/doubleAtom\", atom(get => get(countAtom) * 2));\n    doubleAtom.debugLabel = \"doubleAtom\";\"\n  `)\n})\n\nit('Should add a cache and debugLabel for a default exported atom', () => {\n  expect(transform(`export default atom(0);`, '/src/atoms/index.ts'))\n    .toMatchInlineSnapshot(`\n      \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n        cache: new Map(),\n        get(name, inst) {\n          if (this.cache.has(name)) {\n            return this.cache.get(name);\n          }\n          this.cache.set(name, inst);\n          return inst;\n        }\n      };\n      const atoms = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/atoms\", atom(0));\n      atoms.debugLabel = \"atoms\";\n      export default atoms;\"\n    `)\n})\n\nit('Should add a cache and debugLabel for mixed exports of atoms', () => {\n  expect(\n    transform(\n      `\n  export const countAtom = atom(0);\n  export default atom((get) => get(countAtom) * 2);\n  `,\n      '/src/atoms/index.ts',\n    ),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    export const countAtom = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/countAtom\", atom(0));\n    countAtom.debugLabel = \"countAtom\";\n    const atoms = globalThis.jotaiAtomCache.get(\"/src/atoms/index.ts/atoms\", atom(get => get(countAtom) * 2));\n    atoms.debugLabel = \"atoms\";\n    export default atoms;\"\n  `)\n})\n\nit('Should fail if no filename is available', () => {\n  expect(() => transform(`const countAtom = atom(0);`)).toThrow(\n    'Filename must be available',\n  )\n})\n\nit('Should handle custom atom names', () => {\n  expect(\n    transform(`const mySpecialThing = myCustomAtom(0);`, '/src/atoms.ts', [\n      'myCustomAtom',\n    ]),\n  ).toMatchInlineSnapshot(`\n    \"globalThis.jotaiAtomCache = globalThis.jotaiAtomCache || {\n      cache: new Map(),\n      get(name, inst) {\n        if (this.cache.has(name)) {\n          return this.cache.get(name);\n        }\n        this.cache.set(name, inst);\n        return inst;\n      }\n    };\n    const mySpecialThing = globalThis.jotaiAtomCache.get(\"/src/atoms.ts/mySpecialThing\", myCustomAtom(0));\n    mySpecialThing.debugLabel = \"mySpecialThing\";\"\n  `)\n})\n"
  },
  {
    "path": "tests/react/abortable.test.tsx",
    "content": "import { StrictMode, Suspense, useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { sleep } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\ndescribe('abortable atom test', () => {\n  it('can abort with signal.aborted', async () => {\n    const countAtom = atom(0)\n    let abortedCount = 0\n    const derivedAtom = atom(async (get, { signal }) => {\n      const count = get(countAtom)\n      await sleep(100)\n      if (signal.aborted) {\n        ++abortedCount\n      }\n      return count\n    })\n\n    const Component = () => {\n      const count = useAtomValue(derivedAtom)\n      return <div>count: {count}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n            <Controls />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(0)\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(1)\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(1)\n  })\n\n  it('can abort with event listener', async () => {\n    const countAtom = atom(0)\n    let abortedCount = 0\n    const derivedAtom = atom(async (get, { signal }) => {\n      const count = get(countAtom)\n      const callback = () => {\n        ++abortedCount\n      }\n      signal.addEventListener('abort', callback)\n      await sleep(100)\n      signal.removeEventListener('abort', callback)\n      return count\n    })\n\n    const Component = () => {\n      const count = useAtomValue(derivedAtom)\n      return <div>count: {count}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n            <Controls />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(0)\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(1)\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(1)\n  })\n\n  it('does not abort on unmount', async () => {\n    const countAtom = atom(0)\n    let abortedCount = 0\n    const derivedAtom = atom(async (get, { signal }) => {\n      const count = get(countAtom)\n      await sleep(100)\n      if (signal.aborted) {\n        ++abortedCount\n      }\n      return count\n    })\n\n    const Component = () => {\n      const count = useAtomValue(derivedAtom)\n      return <div>count: {count}</div>\n    }\n\n    const Parent = () => {\n      const setCount = useSetAtom(countAtom)\n      const [show, setShow] = useState(true)\n      return (\n        <>\n          {show ? <Component /> : 'hidden'}\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n          <button onClick={() => setShow((x) => !x)}>toggle</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Parent />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => fireEvent.click(screen.getByText('toggle')))\n    expect(screen.getByText('hidden')).toBeInTheDocument()\n\n    expect(abortedCount).toBe(0)\n  })\n\n  it('throws aborted error (like fetch)', async () => {\n    const countAtom = atom(0)\n    const derivedAtom = atom(async (get, { signal }) => {\n      const count = get(countAtom)\n      await sleep(100)\n      if (signal.aborted) {\n        throw new Error('aborted')\n      }\n      return count\n    })\n\n    const Component = () => {\n      const count = useAtomValue(derivedAtom)\n      return <div>count: {count}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n            <Controls />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/react/async.test.tsx",
    "content": "import { StrictMode, Suspense, useEffect } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport type { Atom } from 'jotai/vanilla'\nimport { sleep, useCommitCount } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('does not show async stale result', async () => {\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(async (get) => {\n    await sleep(100)\n    return get(countAtom)\n  })\n\n  const committed: number[] = []\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const onClick = async () => {\n      setCount((c) => c + 1)\n      await sleep(100)\n      setCount((c) => c + 1)\n    }\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={onClick}>button</button>\n      </>\n    )\n  }\n\n  const DelayedCounter = () => {\n    const [delayedCount] = useAtom(asyncCountAtom)\n    useEffect(() => {\n      committed.push(delayedCount)\n    })\n    return <div>delayedCount: {delayedCount}</div>\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Counter />\n        <Suspense fallback=\"loading\">\n          <DelayedCounter />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('delayedCount: 0')).toBeInTheDocument()\n\n  expect(committed).toEqual([0])\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('delayedCount: 2')).toBeInTheDocument()\n\n  // React 18+ uses automatic batching, so committed is [0, 2]\n  // React 16-17 doesn't batch async updates, so committed is [0, 1, 2]\n  // Different build types (cjs, umd, esm) may also affect batching behavior\n  expect(committed.length).toBeGreaterThanOrEqual(2)\n  expect(committed[0]).toBe(0)\n  expect(committed[committed.length - 1]).toBe(2)\n})\n\nit('does not show async stale result on derived atom', async () => {\n  const countAtom = atom(0)\n  const asyncAlwaysNullAtom = atom(async (get) => {\n    get(countAtom)\n    await sleep(100)\n    return null\n  })\n  const derivedAtom = atom((get) => get(asyncAlwaysNullAtom))\n\n  const DisplayAsyncValue = () => {\n    const [asyncValue] = useAtom(asyncAlwaysNullAtom)\n\n    return <div>async value: {JSON.stringify(asyncValue)}</div>\n  }\n\n  const DisplayDerivedValue = () => {\n    const [derivedValue] = useAtom(derivedAtom)\n    return <div>derived value: {JSON.stringify(derivedValue)}</div>\n  }\n\n  const Test = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <div>\n        <div>count: {count}</div>\n        <Suspense fallback={<div>loading async value</div>}>\n          <DisplayAsyncValue />\n        </Suspense>\n        <Suspense fallback={<div>loading derived value</div>}>\n          <DisplayDerivedValue />\n        </Suspense>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </div>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Test />\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('loading async value')).toBeInTheDocument()\n  expect(screen.getByText('loading derived value')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('async value: null')).toBeInTheDocument()\n  expect(screen.getByText('derived value: null')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('loading async value')).toBeInTheDocument()\n  expect(screen.getByText('loading derived value')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('async value: null')).toBeInTheDocument()\n  expect(screen.getByText('derived value: null')).toBeInTheDocument()\n})\n\nit('works with async get with extra deps', async () => {\n  const countAtom = atom(0)\n  const anotherAtom = atom(-1)\n  const asyncCountAtom = atom(async (get) => {\n    get(anotherAtom)\n    await sleep(100)\n    return get(countAtom)\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  const DelayedCounter = () => {\n    const [delayedCount] = useAtom(asyncCountAtom)\n    return <div>delayedCount: {delayedCount}</div>\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n          <DelayedCounter />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('delayedCount: 0')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('delayedCount: 1')).toBeInTheDocument()\n})\n\nit('reuses promises on initial read', async () => {\n  let invokeCount = 0\n  const asyncAtom = atom(async () => {\n    invokeCount += 1\n    await sleep(100)\n    return 'ready'\n  })\n\n  const Child = () => {\n    const [str] = useAtom(asyncAtom)\n    return <div>{str}</div>\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Child />\n          <Child />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  const elements = screen.getAllByText('ready')\n  elements.forEach((element) => {\n    expect(element).toBeInTheDocument()\n  })\n\n  expect(invokeCount).toBe(1)\n})\n\nit('uses multiple async atoms at once', async () => {\n  const someAtom = atom(async () => {\n    await sleep(100)\n    return 'ready'\n  })\n  const someAtom2 = atom(async () => {\n    await sleep(50)\n    return 'ready2'\n  })\n\n  const Component = () => {\n    const [some] = useAtom(someAtom)\n    const [some2] = useAtom(someAtom2)\n    return (\n      <>\n        <div>\n          {some} {some2}\n        </div>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Component />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  await act(() => vi.advanceTimersByTimeAsync(50))\n  expect(screen.getByText('ready ready2')).toBeInTheDocument()\n})\n\nit('uses async atom in the middle of dependency chain', async () => {\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(async (get) => {\n    await sleep(100)\n    return get(countAtom)\n  })\n  const delayedCountAtom = atom((get) => get(asyncCountAtom))\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [delayedCount] = useAtom(delayedCountAtom)\n    return (\n      <>\n        <div>\n          count: {count}, delayed: {delayedCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('count: 0, delayed: 0')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 1, delayed: 1')).toBeInTheDocument()\n})\n\nit('updates an async atom in child useEffect on remount without setTimeout', async () => {\n  const toggleAtom = atom(true)\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(\n    async (get) => get(countAtom),\n    async (get, set) => set(countAtom, get(countAtom) + 1),\n  )\n\n  const Counter = () => {\n    const [count, incCount] = useAtom(asyncCountAtom)\n    useEffect(() => {\n      incCount()\n    }, [incCount])\n    return <div>count: {count}</div>\n  }\n\n  const Parent = () => {\n    const [toggle, setToggle] = useAtom(toggleAtom)\n    return (\n      <>\n        <button onClick={() => setToggle((x) => !x)}>button</button>\n        {toggle ? <Counter /> : <div>no child</div>}\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Parent />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('no child')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n})\n\nit('updates an async atom in child useEffect on remount', async () => {\n  const toggleAtom = atom(true)\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(\n    async (get) => {\n      await sleep(100)\n      return get(countAtom)\n    },\n    async (get, set) => {\n      await sleep(100)\n      set(countAtom, get(countAtom) + 1)\n    },\n  )\n\n  const Counter = () => {\n    const [count, incCount] = useAtom(asyncCountAtom)\n    useEffect(() => {\n      incCount()\n    }, [incCount])\n    return <div>count: {count}</div>\n  }\n\n  const Parent = () => {\n    const [toggle, setToggle] = useAtom(toggleAtom)\n    return (\n      <>\n        <button onClick={() => setToggle((x) => !x)}>button</button>\n        {toggle ? <Counter /> : <div>no child</div>}\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Parent />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  // NOTE: 1000ms to wait for useEffect's write operation with React scheduling overhead\n  await act(() => vi.advanceTimersByTimeAsync(1000))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('no child')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  // NOTE: 1000ms to wait for useEffect's write operation with React scheduling overhead\n  await act(() => vi.advanceTimersByTimeAsync(1000))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n})\n\nit('async get and useEffect on parent', async () => {\n  const countAtom = atom(0)\n  const asyncAtom = atom(async (get) => {\n    const count = get(countAtom)\n    if (!count) return 'none'\n    return 'resolved'\n  })\n\n  const AsyncComponent = () => {\n    const [text] = useAtom(asyncAtom)\n    return <div>text: {text}</div>\n  }\n\n  const Parent = () => {\n    const [count, setCount] = useAtom(countAtom)\n    useEffect(() => {\n      setCount((c) => c + 1)\n    }, [setCount])\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n        <AsyncComponent />\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Parent />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('text: resolved')).toBeInTheDocument()\n})\n\nit('async get with another dep and useEffect on parent', async () => {\n  const countAtom = atom(0)\n  const derivedAtom = atom((get) => get(countAtom))\n  const asyncAtom = atom(async (get) => {\n    const count = get(derivedAtom)\n    if (!count) return 'none'\n    return count\n  })\n\n  const AsyncComponent = () => {\n    const [count] = useAtom(asyncAtom)\n    return <div>async: {count}</div>\n  }\n\n  const Parent = () => {\n    const [count, setCount] = useAtom(countAtom)\n    useEffect(() => {\n      setCount((c) => c + 1)\n    }, [setCount])\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n        <AsyncComponent />\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Parent />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('async: 1')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('async: 2')).toBeInTheDocument()\n})\n\nit('set promise atom value on write (#304)', async () => {\n  const countAtom = atom(Promise.resolve(0))\n  const asyncAtom = atom(null, (get, set, _arg) => {\n    set(\n      countAtom,\n      Promise.resolve(get(countAtom)).then(async (c) => {\n        await sleep(100)\n        return c + 1\n      }),\n    )\n  })\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return <div>count: {count * 1}</div>\n  }\n\n  const Parent = () => {\n    const [, dispatch] = useAtom(asyncAtom)\n    return (\n      <>\n        <Counter />\n        <button onClick={dispatch}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Parent />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  // FIXME this is not working\n  //await screen.findByText('loading')\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('uses async atom double chain (#306)', async () => {\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(async (get) => {\n    await sleep(100)\n    return get(countAtom)\n  })\n  const delayedCountAtom = atom(async (get) => {\n    return get(asyncCountAtom)\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [delayedCount] = useAtom(delayedCountAtom)\n    return (\n      <>\n        <div>\n          count: {count}, delayed: {delayedCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('count: 0, delayed: 0')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 1, delayed: 1')).toBeInTheDocument()\n})\n\nit('uses an async atom that depends on another async atom', async () => {\n  const asyncAtom = atom(async (get) => {\n    await sleep(100)\n    get(anotherAsyncAtom)\n    return 1\n  })\n  const anotherAsyncAtom = atom(async () => {\n    return 2\n  })\n\n  const Counter = () => {\n    const [num] = useAtom(asyncAtom)\n    return <div>num: {num}</div>\n  }\n\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(100))\n  expect(screen.getByText('num: 1')).toBeInTheDocument()\n})\n\nit('a derived atom from a newly created async atom (#351)', async () => {\n  const countAtom = atom(1)\n  const atomCache = new Map<number, Atom<Promise<number>>>()\n  const getAsyncAtom = (n: number) => {\n    if (!atomCache.has(n)) {\n      atomCache.set(\n        n,\n        atom(async () => {\n          return n + 10\n        }),\n      )\n    }\n    return atomCache.get(n) as Atom<Promise<number>>\n  }\n  const derivedAtom = atom((get) => get(getAsyncAtom(get(countAtom))))\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    const [derived] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>\n          derived: {derived}, commits: {useCommitCount()}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('derived: 11, commits: 1')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('derived: 12, commits: 2')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('derived: 13, commits: 3')).toBeInTheDocument()\n})\n\nit('Handles synchronously invoked async set (#375)', async () => {\n  const loadingAtom = atom(false)\n  const documentAtom = atom<string | undefined>(undefined)\n  const loadDocumentAtom = atom(null, (_get, set) => {\n    const fetch = async () => {\n      set(loadingAtom, true)\n      const response = await new Promise<string>((resolve) =>\n        setTimeout(() => resolve('great document'), 100),\n      )\n      set(documentAtom, response)\n      set(loadingAtom, false)\n    }\n    fetch()\n  })\n\n  const ListDocuments = () => {\n    const [loading] = useAtom(loadingAtom)\n    const [document] = useAtom(documentAtom)\n    const [, loadDocument] = useAtom(loadDocumentAtom)\n\n    useEffect(() => {\n      loadDocument()\n    }, [loadDocument])\n\n    return (\n      <>\n        {loading && <div>loading</div>}\n        {!loading && <div>{document}</div>}\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ListDocuments />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('great document')).toBeInTheDocument()\n})\n\nit('async write self atom', async () => {\n  const countAtom = atom(0, async (get, set, _arg) => {\n    set(countAtom, get(countAtom) + 1)\n    await sleep(100)\n    set(countAtom, -1)\n  })\n\n  const Counter = () => {\n    const [count, inc] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={inc}>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(100))\n  expect(screen.getByText('count: -1')).toBeInTheDocument()\n})\n\nit('non suspense async write self atom with setTimeout (#389)', async () => {\n  const countAtom = atom(0, (get, set, _arg) => {\n    set(countAtom, get(countAtom) + 1)\n    setTimeout(() => set(countAtom, -1))\n  })\n\n  const Counter = () => {\n    const [count, inc] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={inc}>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')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('count: -1')).toBeInTheDocument()\n})\n\nit('should override promise as atom value (#430)', async () => {\n  const countAtom = atom(new Promise<number>(() => {}))\n  const setCountAtom = atom(null, (_get, set, arg: number) => {\n    set(countAtom, Promise.resolve(arg))\n  })\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return <div>count: {count * 1}</div>\n  }\n\n  const Control = () => {\n    const [, setCount] = useAtom(setCountAtom)\n    return <button onClick={() => setCount(1)}>button</button>\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n        <Control />\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('combine two promise atom values (#442)', async () => {\n  const count1Atom = atom(new Promise<number>(() => {}))\n  const count2Atom = atom(new Promise<number>(() => {}))\n  const derivedAtom = atom(\n    async (get) => (await get(count1Atom)) + (await get(count2Atom)),\n  )\n  const initAtom = atom(null, (_get, set) => {\n    setTimeout(() => set(count1Atom, Promise.resolve(1)))\n    setTimeout(() => set(count2Atom, Promise.resolve(2)))\n  })\n  initAtom.onMount = (init) => {\n    init()\n  }\n\n  const Counter = () => {\n    const [count] = useAtom(derivedAtom)\n    return <div>count: {count}</div>\n  }\n\n  const Control = () => {\n    useAtom(initAtom)\n    return null\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n        <Control />\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n\nit('set two promise atoms at once', async () => {\n  const count1Atom = atom(new Promise<number>(() => {}))\n  const count2Atom = atom(new Promise<number>(() => {}))\n  const derivedAtom = atom(\n    async (get) => (await get(count1Atom)) + (await get(count2Atom)),\n  )\n  const setCountsAtom = atom(null, (_get, set) => {\n    set(count1Atom, Promise.resolve(1))\n    set(count2Atom, Promise.resolve(2))\n  })\n\n  const Counter = () => {\n    const [count] = useAtom(derivedAtom)\n    return <div>count: {count}</div>\n  }\n\n  const Control = () => {\n    const [, setCounts] = useAtom(setCountsAtom)\n    return <button onClick={() => setCounts()}>button</button>\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n        <Control />\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n\nit('async write chain', async () => {\n  const countAtom = atom(0)\n  const asyncWriteAtom = atom(null, async (_get, set, _arg) => {\n    await sleep(100)\n    set(countAtom, 2)\n  })\n  const controlAtom = atom(null, async (_get, set, _arg) => {\n    set(countAtom, 1)\n    await set(asyncWriteAtom, null)\n    await sleep(100)\n    set(countAtom, 3)\n  })\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return <div>count: {count}</div>\n  }\n\n  const Control = () => {\n    const [, invoke] = useAtom(controlAtom)\n    return <button onClick={invoke}>button</button>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n      <Control />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n\nit('async atom double chain without setTimeout (#751)', async () => {\n  const enabledAtom = atom(false)\n  const asyncAtom = atom(async (get) => {\n    const enabled = get(enabledAtom)\n    if (!enabled) {\n      return 'init'\n    }\n    await sleep(100)\n    return 'ready'\n  })\n  const derivedAsyncAtom = atom(async (get) => get(asyncAtom))\n  const anotherAsyncAtom = atom(async (get) => get(derivedAsyncAtom))\n\n  const AsyncComponent = () => {\n    const [text] = useAtom(anotherAsyncAtom)\n    return <div>async: {text}</div>\n  }\n\n  const Parent = () => {\n    // Use useAtom to reproduce the issue\n    const [, setEnabled] = useAtom(enabledAtom)\n    return (\n      <>\n        <Suspense fallback=\"loading\">\n          <AsyncComponent />\n        </Suspense>\n        <button\n          onClick={() => {\n            setEnabled(true)\n          }}\n        >\n          button\n        </button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Parent />\n      </StrictMode>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('async: init')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('async: ready')).toBeInTheDocument()\n})\n\nit('async atom double chain with setTimeout', async () => {\n  const enabledAtom = atom(false)\n  const asyncAtom = atom(async (get) => {\n    const enabled = get(enabledAtom)\n    if (!enabled) {\n      return 'init'\n    }\n    await sleep(100)\n    return 'ready'\n  })\n  const derivedAsyncAtom = atom(async (get) => {\n    await sleep(100)\n    return get(asyncAtom)\n  })\n  const anotherAsyncAtom = atom(async (get) => {\n    await sleep(100)\n    return get(derivedAsyncAtom)\n  })\n\n  const AsyncComponent = () => {\n    const [text] = useAtom(anotherAsyncAtom)\n    return <div>async: {text}</div>\n  }\n\n  const Parent = () => {\n    // Use useAtom to reproduce the issue\n    const [, setEnabled] = useAtom(enabledAtom)\n    return (\n      <>\n        <Suspense fallback=\"loading\">\n          <AsyncComponent />\n        </Suspense>\n        <button\n          onClick={() => {\n            setEnabled(true)\n          }}\n        >\n          button\n        </button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Parent />\n      </StrictMode>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('async: init')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('async: ready')).toBeInTheDocument()\n})\n\nit('update unmounted async atom with intermediate atom', async () => {\n  const enabledAtom = atom(true)\n  const countAtom = atom(1)\n\n  const intermediateAtom = atom((get) => {\n    const count = get(countAtom)\n    const enabled = get(enabledAtom)\n    const tmpAtom = atom(async () => {\n      if (!enabled) {\n        return -1\n      }\n      await sleep(100)\n      return count * 2\n    })\n    return tmpAtom\n  })\n  const derivedAtom = atom((get) => {\n    const tmpAtom = get(intermediateAtom)\n    return get(tmpAtom)\n  })\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Control = () => {\n    const [, setEnabled] = useAtom(enabledAtom)\n    const [, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <button onClick={() => setCount((c) => c + 1)}>increment count</button>\n        <button onClick={() => setEnabled((x) => !x)}>toggle enabled</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <DerivedCounter />\n        </Suspense>\n        <Control />\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('derived: 2')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('toggle enabled')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  await act(() => fireEvent.click(screen.getByText('increment count')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('derived: -1')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('toggle enabled')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('derived: 4')).toBeInTheDocument()\n})\n\nit('multiple derived atoms with dependency chaining and async write (#813)', async () => {\n  const responseBaseAtom = atom<{ name: string }[] | null>(null)\n\n  const response1 = [{ name: 'alpha' }, { name: 'beta' }]\n  const responseAtom = atom(\n    (get) => get(responseBaseAtom),\n    (_get, set) => {\n      setTimeout(() => set(responseBaseAtom, response1))\n    },\n  )\n  responseAtom.onMount = (init) => {\n    init()\n  }\n\n  const mapAtom = atom((get) => get(responseAtom))\n  const itemA = atom((get) => get(mapAtom)?.[0])\n  const itemB = atom((get) => get(mapAtom)?.[1])\n  const itemAName = atom((get) => get(itemA)?.name)\n  const itemBName = atom((get) => get(itemB)?.name)\n\n  const App = () => {\n    const [aName] = useAtom(itemAName)\n    const [bName] = useAtom(itemBName)\n    return (\n      <>\n        <div>aName: {aName}</div>\n        <div>bName: {bName}</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  await act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('aName: alpha')).toBeInTheDocument()\n  expect(screen.getByText('bName: beta')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/async2.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 { useAtom, useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { sleep } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\ndescribe('useAtom delay option test', () => {\n  it('suspend for Promise.resolve without delay option', async () => {\n    const countAtom = atom(0)\n    const asyncAtom = atom((get) => {\n      const count = get(countAtom)\n      if (count === 0) {\n        return 0\n      }\n      return Promise.resolve(count)\n    })\n\n    const Component = () => {\n      const count = useAtomValue(asyncAtom)\n      return <div>count: {count}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Component />\n          <Controls />\n        </Suspense>\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('do not suspend for Promise.resolve with delay option', async () => {\n    const countAtom = atom(0)\n    const asyncAtom = atom((get) => {\n      const count = get(countAtom)\n      if (count === 0) {\n        return 0\n      }\n      return Promise.resolve(count)\n    })\n\n    const Component = () => {\n      const count = useAtomValue(asyncAtom, { delay: 0 })\n      return <div>count: {count}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Component />\n        <Controls />\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\ndescribe('atom read function setSelf option test', () => {\n  it('do not suspend with promise resolving with setSelf', async () => {\n    const countAtom = atom(0)\n    const asyncAtom = atom(async () => {\n      await sleep(100)\n      return 'hello'\n    })\n    const refreshAtom = atom(0)\n    const promiseCache = new WeakMap<object, string>()\n    const derivedAtom = atom(\n      (get, { setSelf }) => {\n        get(refreshAtom)\n        const count = get(countAtom)\n        const promise = get(asyncAtom)\n        if (promiseCache.has(promise)) {\n          return (promiseCache.get(promise) as string) + count\n        }\n        promise.then((v) => {\n          promiseCache.set(promise, v)\n          setSelf()\n        })\n        return 'pending' + count\n      },\n      (_get, set) => {\n        set(refreshAtom, (c) => c + 1)\n      },\n    )\n\n    const Component = () => {\n      const text = useAtomValue(derivedAtom)\n      return <div>text: {text}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Component />\n        <Controls />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('text: pending0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('text: hello0')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('text: hello1')).toBeInTheDocument()\n  })\n})\n\ndescribe('timing issue with setSelf', () => {\n  it('resolves dependencies reliably after a delay (#2192)', async () => {\n    expect.assertions(6)\n    const countAtom = atom(0)\n\n    let result: number | null = null\n    const asyncAtom = atom(async (get) => {\n      const count = get(countAtom)\n      await sleep(100)\n      return count\n    })\n\n    const derivedAtom = atom(\n      async (get, { setSelf }) => {\n        get(countAtom)\n        await Promise.resolve()\n        const resultCount = await get(asyncAtom)\n        result = resultCount\n        if (resultCount === 2) setSelf() // <-- necessary\n      },\n      () => {},\n    )\n\n    const derivedSyncAtom = atom((get) => {\n      get(derivedAtom)\n    })\n\n    const increment = (c: number) => c + 1\n    function TestComponent() {\n      useAtom(derivedSyncAtom)\n      const [count, setCount] = useAtom(countAtom)\n      const onClick = () => {\n        setCount(increment)\n        setCount(increment)\n      }\n      return (\n        <>\n          count: {count}\n          <button onClick={onClick}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <TestComponent />\n      </StrictMode>,\n    )\n\n    await vi.advanceTimersByTimeAsync(100)\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    expect(result).toBe(0)\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    await vi.advanceTimersByTimeAsync(100)\n    expect(result).toBe(2)\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('count: 4')).toBeInTheDocument()\n\n    await vi.advanceTimersByTimeAsync(100)\n    expect(result).toBe(4)\n  })\n})\n\ndescribe('infinite pending', () => {\n  it('odd counter', async () => {\n    const countAtom = atom(0)\n    const asyncAtom = atom((get) => {\n      const count = get(countAtom)\n      if (count % 2 === 0) {\n        const infinitePending = new Promise<never>(() => {})\n        return infinitePending\n      }\n      return count\n    })\n\n    const Component = () => {\n      const count = useAtomValue(asyncAtom)\n      return <div>count: {count}</div>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Controls />\n          <Suspense fallback=\"loading\">\n            <Component />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n  })\n})\n\ndescribe('write to async atom twice', async () => {\n  it('no wait', async () => {\n    const asyncAtom = atom(Promise.resolve(2))\n    const writer = atom(null, async (get, set) => {\n      set(asyncAtom, async (c) => (await c) + 1)\n      set(asyncAtom, async (c) => (await c) + 1)\n      return get(asyncAtom)\n    })\n\n    const Component = () => {\n      const count = useAtomValue(asyncAtom)\n      const write = useSetAtom(writer)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={write}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 4')).toBeInTheDocument()\n  })\n\n  it('wait Promise.resolve()', async () => {\n    const asyncAtom = atom(Promise.resolve(2))\n    const writer = atom(null, async (get, set) => {\n      set(asyncAtom, async (c) => (await c) + 1)\n      await Promise.resolve()\n      set(asyncAtom, async (c) => (await c) + 1)\n      return get(asyncAtom)\n    })\n\n    const Component = () => {\n      const count = useAtomValue(asyncAtom)\n      const write = useSetAtom(writer)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={write}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 4')).toBeInTheDocument()\n  })\n\n  it('wait setTimeout()', async () => {\n    const asyncAtom = atom(Promise.resolve(2))\n    const writer = atom(null, async (get, set) => {\n      set(asyncAtom, async (c) => (await c) + 1)\n      await sleep(100)\n      set(asyncAtom, async (c) => (await c) + 1)\n      return get(asyncAtom)\n    })\n\n    const Component = () => {\n      const count = useAtomValue(asyncAtom)\n      const write = useSetAtom(writer)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={write}>button</button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 4')).toBeInTheDocument()\n  })\n})\n\ndescribe('with onMount', () => {\n  it('does not infinite loop with setting a promise (#2931)', async () => {\n    const firstPromise = Promise.resolve(1)\n    const secondPromise = Promise.resolve(2)\n    const asyncAtom = atom(firstPromise)\n    let onMountCallCount = 0\n    asyncAtom.onMount = (setCount) => {\n      onMountCallCount++\n      setCount((prev) => (prev === firstPromise ? secondPromise : prev))\n    }\n    const Component = () => {\n      const [count, setCount] = useAtom(asyncAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount(async (c) => (await c) + 1)}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    // onMount should be called a limited number of times (not infinitely)\n    // In StrictMode, React may mount/unmount/remount, so allow up to a few calls\n    const initialCallCount = onMountCallCount\n    expect(initialCallCount).toBeGreaterThan(0)\n    expect(initialCallCount).toBeLessThanOrEqual(4)\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n    // onMount may be called a few more times due to StrictMode, but not infinitely\n    expect(onMountCallCount).toBeLessThanOrEqual(initialCallCount + 2)\n    expect(onMountCallCount).toBeLessThan(10) // If infinite loop, this would be much higher\n  })\n})\n"
  },
  {
    "path": "tests/react/basic.test.tsx",
    "content": "import {\n  StrictMode,\n  Suspense,\n  version as reactVersion,\n  useEffect,\n  useMemo,\n  useState,\n} from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { unstable_batchedUpdates } from 'react-dom'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport type { PrimitiveAtom } from 'jotai/vanilla'\nimport { sleep, useCommitCount } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nconst IS_REACT18 = /^18\\./.test(reactVersion)\n\nconst batchedUpdates = (fn: () => void) => {\n  if (IS_REACT18) {\n    fn()\n  } else {\n    unstable_batchedUpdates(fn)\n  }\n}\n\nit('uses a primitive atom', () => {\n  const countAtom = atom(0)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 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')).toBeInTheDocument()\n})\n\nit('uses a read-only derived atom', () => {\n  const countAtom = atom(0)\n  const doubledCountAtom = atom((get) => get(countAtom) * 2)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [doubledCount] = useAtom(doubledCountAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>doubledCount: {doubledCount}</div>\n        <button onClick={() => setCount((c) => c + 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  expect(screen.getByText('doubledCount: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('doubledCount: 2')).toBeInTheDocument()\n})\n\nit('uses a read-write derived atom', () => {\n  const countAtom = atom(0)\n  const doubledCountAtom = atom(\n    (get) => get(countAtom) * 2,\n    (get, set, update: number) => set(countAtom, get(countAtom) + update),\n  )\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    const [doubledCount, increaseCount] = useAtom(doubledCountAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>doubledCount: {doubledCount}</div>\n        <button onClick={() => increaseCount(2)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('doubledCount: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('doubledCount: 4')).toBeInTheDocument()\n})\n\nit('uses a write-only derived atom', () => {\n  const countAtom = atom(0)\n  const incrementCountAtom = atom(null, (get, set) =>\n    set(countAtom, get(countAtom) + 1),\n  )\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return (\n      <div>\n        commits: {useCommitCount()}, count: {count}\n      </div>\n    )\n  }\n\n  const Control = () => {\n    const [, increment] = useAtom(incrementCountAtom)\n    return (\n      <>\n        <div>button commits: {useCommitCount()}</div>\n        <button onClick={() => increment()}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n      <Control />\n    </>,\n  )\n\n  expect(screen.getByText('commits: 1, count: 0')).toBeInTheDocument()\n  expect(screen.getByText('button commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('commits: 2, count: 1')).toBeInTheDocument()\n  expect(screen.getByText('button commits: 1')).toBeInTheDocument()\n})\n\nit('only re-renders if value has changed', () => {\n  const count1Atom = atom(0)\n  const count2Atom = atom(0)\n  const productAtom = atom((get) => get(count1Atom) * get(count2Atom))\n\n  type Props = { countAtom: typeof count1Atom; name: string }\n  const Counter = ({ countAtom, name }: Props) => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, {name}: {count}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button-{name}</button>\n      </>\n    )\n  }\n\n  const Product = () => {\n    const [product] = useAtom(productAtom)\n    return (\n      <>\n        <div data-testid=\"product\">\n          commits: {useCommitCount()}, product: {product}\n        </div>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter countAtom={count1Atom} name=\"count1\" />\n      <Counter countAtom={count2Atom} name=\"count2\" />\n      <Product />\n    </>,\n  )\n\n  expect(screen.getByText('commits: 1, count1: 0')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1, count2: 0')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1, product: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button-count1'))\n  expect(screen.getByText('commits: 2, count1: 1')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1, count2: 0')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1, product: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button-count2'))\n  expect(screen.getByText('commits: 2, count1: 1')).toBeInTheDocument()\n  expect(screen.getByText('commits: 2, count2: 1')).toBeInTheDocument()\n  expect(screen.getByText('commits: 2, product: 1')).toBeInTheDocument()\n})\n\nit('re-renders a time delayed derived atom with the same initial value (#947)', async () => {\n  const aAtom = atom(false)\n  aAtom.onMount = (set) => {\n    setTimeout(() => {\n      set(true)\n    })\n  }\n\n  const bAtom = atom(1)\n  bAtom.onMount = (set) => {\n    set(2)\n  }\n\n  const cAtom = atom((get) => {\n    if (get(aAtom)) {\n      return get(bAtom)\n    }\n    return 1\n  })\n\n  const App = () => {\n    const [value] = useAtom(cAtom)\n    return <>{value}</>\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('1')).toBeInTheDocument()\n  // Wait for setTimeout to execute\n  await act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('2')).toBeInTheDocument()\n})\n\nit('works with async get', async () => {\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(async (get) => {\n    await sleep(100)\n    return get(countAtom)\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [delayedCount] = useAtom(asyncCountAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, count: {count}, delayedCount:{' '}\n          {delayedCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(\n    screen.getByText('commits: 1, count: 0, delayedCount: 0'),\n  ).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(\n    screen.getByText('commits: 2, count: 1, delayedCount: 1'),\n  ).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(\n    screen.getByText('commits: 3, count: 2, delayedCount: 2'),\n  ).toBeInTheDocument()\n})\n\nit('works with async get without setTimeout', async () => {\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(async (get) => {\n    return get(countAtom)\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [delayedCount] = useAtom(asyncCountAtom)\n    return (\n      <>\n        <div>\n          count: {count}, delayedCount: {delayedCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  // NOTE: loading doesn't appear because async atom resolves immediately (microtask only)\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 0, delayedCount: 0')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1, delayedCount: 1')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2, delayedCount: 2')).toBeInTheDocument()\n})\n\nit('uses atoms with tree dependencies', async () => {\n  const topAtom = atom(0)\n  const leftAtom = atom((get) => get(topAtom))\n  const rightAtom = atom(\n    (get) => get(topAtom),\n    async (get, set, update: (prev: number) => number) => {\n      await sleep(100)\n      batchedUpdates(() => {\n        set(topAtom, update(get(topAtom)))\n      })\n    },\n  )\n\n  const Counter = () => {\n    const [count] = useAtom(leftAtom)\n    const [, setCount] = useAtom(rightAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, count: {count}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('commits: 1, count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('commits: 2, count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('commits: 3, count: 2')).toBeInTheDocument()\n})\n\nit('runs update only once in StrictMode', () => {\n  let updateCount = 0\n  const countAtom = atom(0)\n  const derivedAtom = atom(\n    (get) => get(countAtom),\n    (_get, set, update: number) => {\n      updateCount += 1\n      set(countAtom, update)\n    },\n  )\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount(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  expect(updateCount).toBe(0)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(updateCount).toBe(1)\n})\n\nit('uses an async write-only atom', async () => {\n  const countAtom = atom(0)\n  const asyncCountAtom = atom(\n    null,\n    async (get, set, update: (prev: number) => number) => {\n      await sleep(100)\n      set(countAtom, update(get(countAtom)))\n    },\n  )\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    const [, setCount] = useAtom(asyncCountAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, count: {count}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('commits: 1, count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('commits: 2, count: 1')).toBeInTheDocument()\n})\n\nit('uses a writable atom without read function', async () => {\n  const countAtom = atom(1, async (get, set, v: number) => {\n    await sleep(100)\n    set(countAtom, get(countAtom) + 10 * v)\n  })\n\n  const Counter = () => {\n    const [count, addCount10Times] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => addCount10Times(1)}>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(100))\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n})\n\nit('can write an atom value on useEffect', async () => {\n  const countAtom = atom(0)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    useEffect(() => {\n      setCount((c) => c + 1)\n    }, [setCount])\n    return <div>count: {count}</div>\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('can write an atom value on useEffect in children', async () => {\n  const countAtom = atom(0)\n\n  const Child = ({\n    setCount,\n  }: {\n    setCount: (f: (c: number) => number) => void\n  }) => {\n    useEffect(() => {\n      setCount((c) => c + 1)\n    }, [setCount])\n    return null\n  }\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <div>\n        count: {count}\n        <Child setCount={setCount} />\n        <Child setCount={setCount} />\n      </div>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n})\n\nit('only invoke read function on use atom', () => {\n  const countAtom = atom(0)\n  let readCount = 0\n  const doubledCountAtom = atom((get) => {\n    readCount += 1\n    return get(countAtom) * 2\n  })\n\n  expect(readCount).toBe(0) // do not invoke on atom()\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [doubledCount] = useAtom(doubledCountAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, count: {count}, readCount: {readCount},\n          doubled: {doubledCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(\n    screen.getByText('commits: 1, count: 0, readCount: 1, doubled: 0'),\n  ).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(\n    screen.getByText('commits: 2, count: 1, readCount: 2, doubled: 2'),\n  ).toBeInTheDocument()\n})\n\nit('uses a read-write derived atom with two primitive atoms', () => {\n  const countAAtom = atom(0)\n  const countBAtom = atom(0)\n  const sumAtom = atom(\n    (get) => get(countAAtom) + get(countBAtom),\n    (_get, set) => {\n      set(countAAtom, 0)\n      set(countBAtom, 0)\n    },\n  )\n  const incBothAtom = atom(null, (get, set) => {\n    set(countAAtom, get(countAAtom) + 1)\n    set(countBAtom, get(countBAtom) + 1)\n  })\n\n  const Counter = () => {\n    const [countA, setCountA] = useAtom(countAAtom)\n    const [countB, setCountB] = useAtom(countBAtom)\n    const [sum, reset] = useAtom(sumAtom)\n    const [, incBoth] = useAtom(incBothAtom)\n    return (\n      <>\n        <div>\n          countA: {countA}, countB: {countB}, sum: {sum}\n        </div>\n        <button onClick={() => setCountA((c) => c + 1)}>incA</button>\n        <button onClick={() => setCountB((c) => c + 1)}>incB</button>\n        <button onClick={reset}>reset</button>\n        <button onClick={incBoth}>incBoth</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('countA: 0, countB: 0, sum: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('incA'))\n  expect(screen.getByText('countA: 1, countB: 0, sum: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('incB'))\n  expect(screen.getByText('countA: 1, countB: 1, sum: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('reset'))\n  expect(screen.getByText('countA: 0, countB: 0, sum: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('incBoth'))\n  expect(screen.getByText('countA: 1, countB: 1, sum: 2')).toBeInTheDocument()\n})\n\nit('updates a derived atom in useEffect with two primitive atoms', () => {\n  const countAAtom = atom(0)\n  const countBAtom = atom(1)\n  const sumAtom = atom((get) => get(countAAtom) + get(countBAtom))\n\n  const Counter = () => {\n    const [countA, setCountA] = useAtom(countAAtom)\n    const [countB, setCountB] = useAtom(countBAtom)\n    const [sum] = useAtom(sumAtom)\n    useEffect(() => {\n      setCountA((c) => c + 1)\n    }, [setCountA, countB])\n    return (\n      <>\n        <div>\n          countA: {countA}, countB: {countB}, sum: {sum}\n        </div>\n        <button onClick={() => setCountB((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('countA: 1, countB: 1, sum: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('countA: 2, countB: 2, sum: 4')).toBeInTheDocument()\n})\n\nit('updates two atoms in child useEffect', () => {\n  const countAAtom = atom(0)\n  const countBAtom = atom(10)\n\n  const Child = () => {\n    const [countB, setCountB] = useAtom(countBAtom)\n    useEffect(() => {\n      setCountB((c) => c + 1)\n    }, [setCountB])\n    return <div>countB: {countB}</div>\n  }\n\n  const Counter = () => {\n    const [countA, setCountA] = useAtom(countAAtom)\n    useEffect(() => {\n      setCountA((c) => c + 1)\n    }, [setCountA])\n    return (\n      <>\n        <div>countA: {countA}</div>\n        {countA > 0 && <Child />}\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('countA: 1')).toBeInTheDocument()\n  expect(screen.getByText('countB: 11')).toBeInTheDocument()\n})\n\nit('set atom right after useEffect (#208)', async () => {\n  const countAtom = atom(0)\n  const effectFn = vi.fn()\n\n  const Child = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [, setState] = useState(null)\n    // rAF does not repro, so schedule update intentionally in render\n    if (count === 1) {\n      Promise.resolve().then(() => {\n        setCount(2)\n      })\n    }\n    useEffect(() => {\n      effectFn(count)\n      // eslint-disable-next-line react-hooks/set-state-in-effect\n      setState(null) // this is important to repro (set something stable)\n    }, [count, setState])\n    return <div>count: {count}</div>\n  }\n\n  const Parent = () => {\n    const [, setCount] = useAtom(countAtom)\n    useEffect(() => {\n      setCount(1)\n      // requestAnimationFrame(() => setCount(2))\n    }, [setCount])\n    return <Child />\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(effectFn).toHaveBeenLastCalledWith(2)\n})\n\nit('changes atom from parent (#273, #275)', () => {\n  const atomA = atom({ id: 'a' })\n  const atomB = atom({ id: 'b' })\n\n  const Item = ({ id }: { id: string }) => {\n    const a = useMemo(() => (id === 'a' ? atomA : atomB), [id])\n    const [atomValue] = useAtom(a)\n    return (\n      <div>\n        commits: {useCommitCount()}, id: {atomValue.id}\n      </div>\n    )\n  }\n\n  const App = () => {\n    const [id, setId] = useState('a')\n    return (\n      <div>\n        <button onClick={() => setId('a')}>atom a</button>\n        <button onClick={() => setId('b')}>atom b</button>\n        <Item id={id} />\n      </div>\n    )\n  }\n\n  render(\n    <>\n      <App />\n    </>,\n  )\n\n  expect(screen.getByText('commits: 1, id: a')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('atom a'))\n  expect(screen.getByText('commits: 1, id: a')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('atom b'))\n  expect(screen.getByText('commits: 2, id: b')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('atom a'))\n  expect(screen.getByText('commits: 3, id: a')).toBeInTheDocument()\n})\n\nit('should be able to use a double derived atom twice and useEffect (#373)', () => {\n  const countAtom = atom(0)\n  const doubleAtom = atom((get) => get(countAtom) * 2)\n  const fourfoldAtom = atom((get) => get(doubleAtom) * 2)\n\n  const App = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [fourfold] = useAtom(fourfoldAtom)\n    const [fourfold2] = useAtom(fourfoldAtom)\n\n    useEffect(() => {\n      setCount(count)\n    }, [count, setCount])\n\n    return (\n      <div>\n        count: {count},{fourfold},{fourfold2}\n        <button onClick={() => setCount((c) => c + 1)}>one up</button>\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0,0,0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('one up'))\n  expect(screen.getByText('count: 1,4,4')).toBeInTheDocument()\n})\n\nit('write self atom (undocumented usage)', () => {\n  const countAtom = atom(0, (get, set, _arg) => {\n    set(countAtom, get(countAtom) + 1)\n  })\n\n  const Counter = () => {\n    const [count, inc] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={inc}>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')).toBeInTheDocument()\n})\n\nit('async chain for multiple sync and async atoms (#443)', async () => {\n  const num1Atom = atom(async () => {\n    return 1\n  })\n  const num2Atom = atom(async () => {\n    return 2\n  })\n\n  // \"async\" is required to reproduce the issue\n  const sumAtom = atom(\n    async (get) => (await get(num1Atom)) + (await get(num2Atom)),\n  )\n  const countAtom = atom((get) => get(sumAtom))\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  // FIXME this is not working\n  //screen.getByText('loading')\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n\nit('sync re-renders with useState re-renders (#827)', () => {\n  const atom0 = atom('atom0')\n  const atom1 = atom('atom1')\n  const atom2 = atom('atom2')\n  const atoms = [atom0, atom1, atom2]\n\n  const App = () => {\n    const [currentAtomIndex, setCurrentAtomIndex] = useState(0)\n    const rotateAtoms = () => {\n      setCurrentAtomIndex((prev) => (prev + 1) % atoms.length)\n    }\n    const [atomValue] = useAtom(\n      atoms[currentAtomIndex] as (typeof atoms)[number],\n    )\n\n    return (\n      <>\n        <span>commits: {useCommitCount()}</span>\n        <h1>{atomValue}</h1>\n        <button onClick={rotateAtoms}>rotate</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <App />\n    </>,\n  )\n\n  expect(screen.getByText('commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('rotate'))\n  expect(screen.getByText('commits: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('rotate'))\n  expect(screen.getByText('commits: 3')).toBeInTheDocument()\n})\n\nit('chained derive atom with onMount and useEffect (#897)', () => {\n  const countAtom = atom(0)\n  countAtom.onMount = (set) => {\n    set(1)\n  }\n  const derivedAtom = atom((get) => get(countAtom))\n  const derivedObjectAtom = atom((get) => ({\n    count: get(derivedAtom),\n  }))\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    const [{ count }] = useAtom(derivedObjectAtom)\n    useEffect(() => {\n      setCount(1)\n    }, [setCount])\n    return <div>count: {count}</div>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('onMount is not called when atom value is accessed from writeGetter in derived atom (#942)', () => {\n  const onUnmount = vi.fn()\n  const onMount = vi.fn(() => {\n    return onUnmount\n  })\n\n  const aAtom = atom(false)\n  aAtom.onMount = onMount\n\n  const bAtom = atom(null, (get) => {\n    get(aAtom)\n  })\n\n  const App = () => {\n    const [, action] = useAtom(bAtom)\n    useEffect(() => action(), [action])\n    return null\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(onMount).not.toHaveBeenCalled()\n  expect(onUnmount).not.toHaveBeenCalled()\n})\n\nit('useAtom returns consistent value with input with changing atoms (#1235)', () => {\n  const countAtom = atom(0)\n  const valueAtoms = [atom(0), atom(1)]\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [value] = useAtom(valueAtoms[count] as PrimitiveAtom<number>)\n    if (count !== value) {\n      throw new Error('value mismatch')\n    }\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 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')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/dependency.test.tsx",
    "content": "/* eslint-disable react/no-unescaped-entities */\nimport { StrictMode, Suspense, useEffect, useRef, useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { useAtom, useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport type { Atom, Getter } from 'jotai/vanilla'\nimport { useCommitCount } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('works with 2 level dependencies', () => {\n  const countAtom = atom(1)\n  const doubledAtom = atom((get) => get(countAtom) * 2)\n  const tripledAtom = atom((get) => get(doubledAtom) * 3)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [doubledCount] = useAtom(doubledAtom)\n    const [tripledCount] = useAtom(tripledAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, count: {count}, doubled: {doubledCount},\n          tripled: {tripledCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(\n    screen.getByText('commits: 1, count: 1, doubled: 2, tripled: 6'),\n  ).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(\n    screen.getByText('commits: 2, count: 2, doubled: 4, tripled: 12'),\n  ).toBeInTheDocument()\n})\n\nit('works a primitive atom and a dependent async atom', async () => {\n  const countAtom = atom(1)\n  const doubledAtom = atom(async (get) => {\n    await new Promise<void>((r) => setTimeout(r, 100))\n    return get(countAtom) * 2\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [doubledCount] = useAtom(doubledAtom)\n    return (\n      <>\n        <div>\n          count: {count}, doubled: {doubledCount}\n        </div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('count: 1, doubled: 2')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(1))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(99))\n  expect(screen.getByText('count: 2, doubled: 4')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(1))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(99))\n  expect(screen.getByText('count: 3, doubled: 6')).toBeInTheDocument()\n})\n\nit('should keep an atom value even if unmounted', () => {\n  const countAtom = atom(0)\n  const derivedFn = vi.fn((get: Getter) => get(countAtom))\n  const derivedAtom = atom(derivedFn)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Parent = () => {\n    const [show, setShow] = useState(true)\n    return (\n      <div>\n        <button onClick={() => setShow((x) => !x)}>toggle</button>\n        {show ? (\n          <>\n            <Counter />\n            <DerivedCounter />\n          </>\n        ) : (\n          <div>hidden</div>\n        )}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('derived: 0')).toBeInTheDocument()\n\n  expect(derivedFn).toHaveReturnedTimes(1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('derived: 1')).toBeInTheDocument()\n\n  expect(derivedFn).toHaveReturnedTimes(2)\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('hidden')).toBeInTheDocument()\n\n  expect(derivedFn).toHaveReturnedTimes(2)\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('derived: 1')).toBeInTheDocument()\n\n  expect(derivedFn).toHaveReturnedTimes(2)\n})\n\nit('should keep a dependent atom value even if unmounted', () => {\n  const countAtom = atom(0)\n  const derivedFn = vi.fn((get: Getter) => get(countAtom))\n  const derivedAtom = atom(derivedFn)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Parent = () => {\n    const [showDerived, setShowDerived] = useState(true)\n    return (\n      <div>\n        <button onClick={() => setShowDerived((x) => !x)}>toggle</button>\n        {showDerived ? <DerivedCounter /> : <Counter />}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('derived: 0')).toBeInTheDocument()\n  expect(derivedFn).toHaveReturnedTimes(1)\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(derivedFn).toHaveReturnedTimes(1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(derivedFn).toHaveReturnedTimes(1)\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('derived: 1')).toBeInTheDocument()\n  expect(derivedFn).toHaveReturnedTimes(2)\n})\n\nit('should bail out updating if not changed', () => {\n  const countAtom = atom(0)\n  const derivedFn = vi.fn((get: Getter) => get(countAtom))\n  const derivedAtom = atom(derivedFn)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount(0)}>button</button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n      <DerivedCounter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('derived: 0')).toBeInTheDocument()\n\n  expect(derivedFn).toHaveReturnedTimes(1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('derived: 0')).toBeInTheDocument()\n\n  expect(derivedFn).toHaveReturnedTimes(1)\n})\n\nit('should bail out updating if not changed, 2 level', async () => {\n  const dataAtom = atom({ count: 1, obj: { anotherCount: 10 } })\n  const getDataCountFn = vi.fn((get: Getter) => get(dataAtom).count)\n  const countAtom = atom(getDataCountFn)\n  const getDataObjFn = vi.fn((get: Getter) => get(dataAtom).obj)\n  const objAtom = atom(getDataObjFn)\n  const getAnotherCountFn = vi.fn((get: Getter) => get(objAtom).anotherCount)\n  const anotherCountAtom = atom(getAnotherCountFn)\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    const [, setData] = useAtom(dataAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button\n          onClick={() =>\n            setData((prev) => ({ ...prev, count: prev.count + 1 }))\n          }\n        >\n          button\n        </button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [anotherCount] = useAtom(anotherCountAtom)\n    return <div>anotherCount: {anotherCount}</div>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n      <DerivedCounter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('anotherCount: 10')).toBeInTheDocument()\n\n  expect(getDataCountFn).toHaveReturnedTimes(1)\n  expect(getDataObjFn).toHaveReturnedTimes(1)\n  expect(getAnotherCountFn).toHaveReturnedTimes(1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('anotherCount: 10')).toBeInTheDocument()\n\n  expect(getDataCountFn).toHaveReturnedTimes(2)\n  expect(getDataObjFn).toHaveReturnedTimes(2)\n  expect(getAnotherCountFn).toHaveReturnedTimes(1)\n})\n\nit('derived atom to update base atom in callback', () => {\n  const countAtom = atom(1)\n  const doubledAtom = atom(\n    (get) => get(countAtom) * 2,\n    (_get, _set, callback: () => void) => {\n      callback()\n    },\n  )\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [doubledCount, dispatch] = useAtom(doubledAtom)\n    return (\n      <>\n        <div>\n          commits: {useCommitCount()}, count: {count}, doubled: {doubledCount}\n        </div>\n        <button onClick={() => dispatch(() => setCount((c) => c + 1))}>\n          button\n        </button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(\n    screen.getByText('commits: 1, count: 1, doubled: 2'),\n  ).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(\n    screen.getByText('commits: 2, count: 2, doubled: 4'),\n  ).toBeInTheDocument()\n})\n\nit('can read sync derived atom in write without initializing', () => {\n  const countAtom = atom(1)\n  const doubledAtom = atom((get) => get(countAtom) * 2)\n  const addAtom = atom(null, (get, set, num: number) => {\n    set(countAtom, get(doubledAtom) / 2 + num)\n  })\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    const [, add] = useAtom(addAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => add(1)}>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  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n\nit('can remount atoms with dependency (#490)', () => {\n  const countAtom = atom(0)\n  const derivedAtom = atom((get) => get(countAtom))\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Parent = () => {\n    const [showChildren, setShowChildren] = useState(true)\n    return (\n      <div>\n        <button onClick={() => setShowChildren((x) => !x)}>toggle</button>\n        {showChildren ? (\n          <>\n            <Counter />\n            <DerivedCounter />\n          </>\n        ) : (\n          <div>hidden</div>\n        )}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('derived: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('derived: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('hidden')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('derived: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('derived: 2')).toBeInTheDocument()\n})\n\nit('can remount atoms with intermediate atom', () => {\n  const countAtom = atom(1)\n\n  const resultAtom = atom(0)\n  const intermediateAtom = atom((get) => {\n    const count = get(countAtom)\n    const initAtom = atom(null, (_get, set) => {\n      set(resultAtom, count * 2)\n    })\n    initAtom.onMount = (init) => {\n      init()\n    }\n    return initAtom\n  })\n  const derivedAtom = atom((get) => {\n    const initAtom = get(intermediateAtom)\n    get(initAtom)\n    return get(resultAtom)\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Parent = () => {\n    const [showChildren, setShowChildren] = useState(true)\n    return (\n      <div>\n        <Counter />\n        <button onClick={() => setShowChildren((x) => !x)}>toggle</button>\n        {showChildren ? <DerivedCounter /> : <div>hidden</div>}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('derived: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('derived: 4')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('hidden')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n  expect(screen.getByText('hidden')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('toggle'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n  expect(screen.getByText('derived: 6')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 4')).toBeInTheDocument()\n  expect(screen.getByText('derived: 8')).toBeInTheDocument()\n})\n\nit('can update dependents with useEffect (#512)', () => {\n  const enabledAtom = atom(false)\n  const countAtom = atom(1)\n\n  const derivedAtom = atom((get) => {\n    const enabled = get(enabledAtom)\n    if (!enabled) {\n      return 0\n    }\n    const count = get(countAtom)\n    return count * 2\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Parent = () => {\n    const [, setEnabled] = useAtom(enabledAtom)\n    useEffect(() => {\n      setEnabled(true)\n    }, [setEnabled])\n    return (\n      <div>\n        <Counter />\n        <DerivedCounter />\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('derived: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('derived: 4')).toBeInTheDocument()\n})\n\nit('update unmounted atom with intermediate atom', () => {\n  const enabledAtom = atom(true)\n  const countAtom = atom(1)\n\n  const intermediateAtom = atom((get) => {\n    const count = get(countAtom)\n    const enabled = get(enabledAtom)\n    const tmpAtom = atom(enabled ? count * 2 : -1)\n    return tmpAtom\n  })\n  const derivedAtom = atom((get) => {\n    const tmpAtom = get(intermediateAtom)\n    return get(tmpAtom)\n  })\n\n  const DerivedCounter = () => {\n    const [derived] = useAtom(derivedAtom)\n    return <div>derived: {derived}</div>\n  }\n\n  const Control = () => {\n    const [, setEnabled] = useAtom(enabledAtom)\n    const [, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <button onClick={() => setCount((c) => c + 1)}>increment count</button>\n        <button onClick={() => setEnabled((x) => !x)}>toggle enabled</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <DerivedCounter />\n      <Control />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('derived: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('toggle enabled'))\n  fireEvent.click(screen.getByText('increment count'))\n  expect(screen.getByText('derived: -1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('toggle enabled'))\n  expect(screen.getByText('derived: 4')).toBeInTheDocument()\n})\n\nit('Should bail for derived sync chains (#877)', () => {\n  let syncAtomCount = 0\n  const textAtom = atom('hello')\n\n  const syncAtom = atom((get) => {\n    get(textAtom)\n    syncAtomCount++\n    return 'My very long data'\n  })\n\n  const derivedAtom = atom((get) => {\n    return get(syncAtom)\n  })\n\n  const Input = () => {\n    const [result] = useAtom(derivedAtom)\n    return <div>{result}</div>\n  }\n\n  const ForceValue = () => {\n    const setText = useAtom(textAtom)[1]\n    return (\n      <div>\n        <button onClick={() => setText('hello')}>set value to 'hello'</button>\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Input />\n      <ForceValue />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('My very long data')).toBeInTheDocument()\n\n  expect(syncAtomCount).toBe(1)\n\n  fireEvent.click(screen.getByText(`set value to 'hello'`))\n  expect(screen.getByText('My very long data')).toBeInTheDocument()\n\n  expect(syncAtomCount).toBe(1)\n})\n\nit('Should bail for derived async chains (#877)', async () => {\n  let syncAtomCount = 0\n  const textAtom = atom('hello')\n\n  const asyncAtom = atom(async (get) => {\n    get(textAtom)\n    syncAtomCount++\n    return 'My very long data'\n  })\n\n  const derivedAtom = atom((get) => {\n    return get(asyncAtom)\n  })\n\n  const Input = () => {\n    const [result] = useAtom(derivedAtom)\n    return <div>{result}</div>\n  }\n\n  const ForceValue = () => {\n    const setText = useAtom(textAtom)[1]\n    return (\n      <div>\n        <button onClick={() => setText('hello')}>set value to 'hello'</button>\n      </div>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Input />\n          <ForceValue />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('My very long data')).toBeInTheDocument()\n\n  expect(syncAtomCount).toBe(1)\n\n  fireEvent.click(screen.getByText(\"set value to 'hello'\"))\n  expect(screen.getByText('My very long data')).toBeInTheDocument()\n\n  expect(syncAtomCount).toBe(1)\n})\n\nit('update correctly with async updates (#1250)', async () => {\n  const countAtom = atom(0)\n\n  const countIsGreaterThanOneAtom = atom((get) => get(countAtom) > 1)\n\n  const alsoCountAtom = atom((get) => {\n    const count = get(countAtom)\n    get(countIsGreaterThanOneAtom)\n    return count\n  })\n\n  const App = () => {\n    const setCount = useSetAtom(countAtom)\n    const alsoCount = useAtomValue(alsoCountAtom)\n    const countIsGreaterThanOne = useAtomValue(countIsGreaterThanOneAtom)\n    const incrementCountTwice = () => {\n      setTimeout(() => setCount((count) => count + 1))\n      setTimeout(() => setCount((count) => count + 1))\n    }\n    return (\n      <div>\n        <button onClick={incrementCountTwice}>Increment Count Twice</button>\n        <div>alsoCount: {alsoCount}</div>\n        <div>countIsGreaterThanOne: {countIsGreaterThanOne.toString()}</div>\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('alsoCount: 0')).toBeInTheDocument()\n  expect(screen.getByText('countIsGreaterThanOne: false')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Increment Count Twice'))\n  await act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('alsoCount: 2')).toBeInTheDocument()\n  expect(screen.getByText('countIsGreaterThanOne: true')).toBeInTheDocument()\n})\n\ndescribe('glitch free', () => {\n  it('basic', () => {\n    const baseAtom = atom(0)\n    const derived1Atom = atom((get) => get(baseAtom))\n    const derived2Atom = atom((get) => get(derived1Atom))\n    const computeValue = vi.fn((get: Getter) => {\n      const v0 = get(baseAtom)\n      const v1 = get(derived1Atom)\n      const v2 = get(derived2Atom)\n      return `v0: ${v0}, v1: ${v1}, v2: ${v2}`\n    })\n    const derived3Atom = atom(computeValue)\n\n    const App = () => {\n      const value = useAtomValue(derived3Atom)\n      return <div>value: {value}</div>\n    }\n\n    const Control = () => {\n      const setCount = useSetAtom(baseAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <App />\n        <Control />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('value: v0: 0, v1: 0, v2: 0')).toBeInTheDocument()\n    expect(computeValue).toHaveBeenCalledTimes(1)\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('value: v0: 1, v1: 1, v2: 1')).toBeInTheDocument()\n    expect(computeValue).toHaveBeenCalledTimes(2)\n  })\n\n  it('same value', () => {\n    const baseAtom = atom(0)\n    const derived1Atom = atom((get) => get(baseAtom) * 0)\n    const derived2Atom = atom((get) => get(derived1Atom) * 0)\n    const computeValue = vi.fn((get: Getter) => {\n      const v0 = get(baseAtom)\n      const v1 = get(derived1Atom)\n      const v2 = get(derived2Atom)\n      return v0 + (v1 - v2)\n    })\n    const derived3Atom = atom(computeValue)\n\n    const App = () => {\n      const value = useAtomValue(derived3Atom)\n      return <div>value: {value}</div>\n    }\n\n    const Control = () => {\n      const setCount = useSetAtom(baseAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <App />\n        <Control />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('value: 0')).toBeInTheDocument()\n    expect(computeValue).toHaveBeenCalledTimes(1)\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('value: 1')).toBeInTheDocument()\n    expect(computeValue).toHaveBeenCalledTimes(2)\n  })\n\n  it('double chain', () => {\n    const baseAtom = atom(0)\n    const derived1Atom = atom((get) => get(baseAtom))\n    const derived2Atom = atom((get) => get(derived1Atom))\n    const derived3Atom = atom((get) => get(derived2Atom))\n    const computeValue = vi.fn((get: Getter) => {\n      const v0 = get(baseAtom)\n      const v1 = get(derived1Atom)\n      const v2 = get(derived2Atom)\n      const v3 = get(derived3Atom)\n      return v0 + (v1 - v2) + v3 * 0\n    })\n    const derived4Atom = atom(computeValue)\n\n    const App = () => {\n      const value = useAtomValue(derived4Atom)\n      return <div>value: {value}</div>\n    }\n\n    const Control = () => {\n      const setCount = useSetAtom(baseAtom)\n      return (\n        <>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <App />\n        <Control />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('value: 0')).toBeInTheDocument()\n    expect(computeValue).toHaveBeenCalledTimes(1)\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('value: 1')).toBeInTheDocument()\n    expect(computeValue).toHaveBeenCalledTimes(2)\n  })\n})\n\nit('should not call read function for unmounted atoms in StrictMode (#2076)', () => {\n  const countAtom = atom(1)\n  let firstDerivedFn:\n    | (((get: Getter) => number) & { mockClear: () => void })\n    | undefined\n\n  const Component = () => {\n    const memoizedAtomRef = useRef<Atom<number> | null>(null)\n    if (!memoizedAtomRef.current) {\n      const derivedFn = vi.fn((get: Getter) => get(countAtom))\n      if (!firstDerivedFn) {\n        // eslint-disable-next-line react-hooks/globals\n        firstDerivedFn = derivedFn\n      }\n      memoizedAtomRef.current = atom(derivedFn)\n    }\n    useAtomValue(memoizedAtomRef.current)\n    return null\n  }\n\n  const Main = () => {\n    const [show, setShow] = useState(true)\n    const setCount = useSetAtom(countAtom)\n    return (\n      <>\n        <button onClick={() => setShow(false)}>hide</button>\n        <button\n          onClick={() => {\n            setShow(true)\n            setCount((c) => c + 1)\n          }}\n        >\n          show\n        </button>\n        {show && <Component />}\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Main />\n    </StrictMode>,\n  )\n\n  fireEvent.click(screen.getByText('hide'))\n  expect(firstDerivedFn).toBeCalledTimes(1)\n  firstDerivedFn?.mockClear()\n\n  fireEvent.click(screen.getByText('show'))\n  expect(firstDerivedFn).toBeCalledTimes(0)\n})\n\nit('works with unused hook (#2554)', async () => {\n  const isFooAtom = atom(false)\n  const isBarAtom = atom(false)\n  const isActive1Atom = atom<boolean>((get) => {\n    return get(isFooAtom) && get(isBarAtom)\n  })\n  const isActive2Atom = atom<boolean>((get) => {\n    return get(isFooAtom) && get(isActive1Atom)\n  })\n  const activateAction = atom(undefined, async (_get, set) => {\n    set(isFooAtom, true)\n    set(isBarAtom, true)\n  })\n\n  const App = () => {\n    const activate = useSetAtom(activateAction)\n    useAtomValue(isActive1Atom)\n    const isRunning = useAtomValue(isActive2Atom)\n    return (\n      <div>\n        <button onClick={() => activate()}>Activate</button>\n        {isRunning ? 'running' : 'not running'}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('not running')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Activate'))\n  expect(screen.getByText('running')).toBeInTheDocument()\n})\n\nit('works with async dependencies (#2565)', async () => {\n  const countAtom = atom(0)\n  const countUpAction = atom(null, (_get, set) => {\n    set(countAtom, (prev) => prev + 1)\n  })\n  const totalCountAtom = atom(async (get) => {\n    const base = await Promise.resolve(100)\n    const count = get(countAtom)\n    return base + count\n  })\n\n  const Count = () => {\n    const count = useAtomValue(totalCountAtom)\n    return <p>count: {count}</p>\n  }\n  const App = () => {\n    const up = useSetAtom(countUpAction)\n    return (\n      <div>\n        <button onClick={up}>Count Up</button>\n        <Suspense fallback=\"loading\">\n          <Count />\n        </Suspense>\n      </div>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <App />\n      </StrictMode>,\n    ),\n  )\n\n  // FIXME this is not working\n  // expect(screen.getByText('loading')).toBeInTheDocument()\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 100')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('Count Up')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 101')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('Count Up')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 102')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/error.test.tsx",
    "content": "import {\n  Component,\n  StrictMode,\n  Suspense,\n  version as reactVersion,\n  useEffect,\n  useState,\n} from 'react'\nimport type { ReactNode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { sleep } from '../test-utils'\n\nconst consoleError = console.error\nconst errorMessages: string[] = []\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n  errorMessages.splice(0)\n  console.error = vi.fn((err: string) => {\n    const match = /^(.*?)(\\n|$)/.exec(err)\n    if (match?.[1]) {\n      errorMessages.push(match[1])\n    }\n  })\n})\nafterEach(() => {\n  vi.useRealTimers()\n  console.error = consoleError\n})\n\nclass ErrorBoundary extends Component<\n  { children: ReactNode },\n  { hasError: false } | { hasError: true; error: Error }\n> {\n  constructor(props: { message?: string; children: ReactNode }) {\n    super(props)\n    this.state = { hasError: false }\n  }\n  static getDerivedStateFromError(error: Error) {\n    return { hasError: true, error }\n  }\n  render() {\n    return this.state.hasError ? (\n      <div>\n        Errored: {this.state.error.message}\n        <button onClick={() => this.setState({ hasError: false })}>\n          retry\n        </button>\n      </div>\n    ) : (\n      this.props.children\n    )\n  }\n}\n\nit('can throw an initial error in read function', () => {\n  const errorAtom = atom(() => {\n    throw new Error()\n  })\n\n  const Counter = () => {\n    useAtom(errorAtom)\n    return (\n      <>\n        <div>no error</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Counter />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Errored:')).toBeInTheDocument()\n})\n\nit('can throw an error in read function', () => {\n  const countAtom = atom(0)\n  const errorAtom = atom((get) => {\n    if (get(countAtom) === 0) {\n      return 0\n    }\n    throw new Error()\n  })\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    const [count] = useAtom(errorAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>no error</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Counter />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('Errored:')).toBeInTheDocument()\n})\n\nit('can throw an initial chained error in read function', () => {\n  const errorAtom = atom(() => {\n    throw new Error()\n  })\n  const derivedAtom = atom((get) => get(errorAtom))\n\n  const Counter = () => {\n    useAtom(derivedAtom)\n    return (\n      <>\n        <div>no error</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Counter />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Errored:')).toBeInTheDocument()\n})\n\nit('can throw a chained error in read function', () => {\n  const countAtom = atom(0)\n  const errorAtom = atom((get) => {\n    if (get(countAtom) === 0) {\n      return 0\n    }\n    throw new Error()\n  })\n  const derivedAtom = atom((get) => get(errorAtom))\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    const [count] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>no error</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Counter />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('Errored:')).toBeInTheDocument()\n})\n\nit('can throw an initial error in async read function', async () => {\n  const errorAtom = atom(async () => {\n    await sleep(100)\n    throw new Error()\n  })\n\n  const Counter = () => {\n    useAtom(errorAtom)\n    return (\n      <>\n        <div>no error</div>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <ErrorBoundary>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </ErrorBoundary>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Errored:')).toBeInTheDocument()\n})\n\nit('can throw an error in async read function', async () => {\n  const countAtom = atom(0)\n  const errorAtom = atom(async (get) => {\n    await sleep(100)\n    if (get(countAtom) === 0) {\n      return 0\n    }\n    throw new Error()\n  })\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    const [count] = useAtom(errorAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>no error</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <ErrorBoundary>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </ErrorBoundary>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('no error')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Errored:')).toBeInTheDocument()\n})\n\nit('can throw an error in write function', () => {\n  const countAtom = atom(0)\n  const errorAtom = atom(\n    (get) => get(countAtom),\n    () => {\n      throw new Error('error_in_write_function')\n    },\n  )\n\n  const Counter = () => {\n    const [count, dispatch] = useAtom(errorAtom)\n    const onClick = () => {\n      try {\n        dispatch()\n      } catch (e) {\n        console.error(e)\n      }\n    }\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>no error</div>\n        <button onClick={onClick}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n  expect(errorMessages).not.toContain('Error: error_in_write_function')\n\n  fireEvent.click(screen.getByText('button'))\n  expect(errorMessages).toContain('Error: error_in_write_function')\n})\n\nit('can throw an error in async write function', async () => {\n  const countAtom = atom(0)\n  const errorAtom = atom(\n    (get) => get(countAtom),\n    async () => {\n      throw new Error('error_in_async_write_function')\n    },\n  )\n\n  const Counter = () => {\n    const [count, dispatch] = useAtom(errorAtom)\n    const onClick = async () => {\n      try {\n        await dispatch()\n      } catch (e) {\n        console.error(e)\n      }\n    }\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>no error</div>\n        <button onClick={onClick}>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('no error')).toBeInTheDocument()\n  expect(errorMessages).not.toContain('Error: error_in_async_write_function')\n\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(errorMessages).toContain('Error: error_in_async_write_function')\n})\n\nit('can throw a chained error in write function', () => {\n  const countAtom = atom(0)\n  const errorAtom = atom(\n    (get) => get(countAtom),\n    () => {\n      throw new Error('chained_err_in_write')\n    },\n  )\n  const chainedAtom = atom(\n    (get) => get(errorAtom),\n    (_get, set) => {\n      set(errorAtom)\n    },\n  )\n\n  const Counter = () => {\n    const [count, dispatch] = useAtom(chainedAtom)\n    const onClick = () => {\n      try {\n        dispatch()\n      } catch (e) {\n        console.error(e)\n      }\n    }\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>no error</div>\n        <button onClick={onClick}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n  expect(errorMessages).not.toContain('Error: chained_err_in_write')\n\n  fireEvent.click(screen.getByText('button'))\n  expect(errorMessages).toContain('Error: chained_err_in_write')\n})\n\nit('throws an error while updating in effect', () => {\n  const countAtom = atom(0)\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    useEffect(() => {\n      try {\n        setCount(() => {\n          throw new Error('err_updating_in_effect')\n        })\n      } catch (e) {\n        console.error(e)\n      }\n    }, [setCount])\n    return (\n      <>\n        <div>no error</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <Counter />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('no error')).toBeInTheDocument()\n  expect(errorMessages).toContain('Error: err_updating_in_effect')\n})\n\ndescribe('throws an error while updating in effect cleanup', () => {\n  const countAtom = atom(0)\n\n  let doubleSetCount = false\n\n  const Counter = () => {\n    const [, setCount] = useAtom(countAtom)\n    useEffect(() => {\n      return () => {\n        if (doubleSetCount) {\n          setCount((x) => x + 1)\n        }\n        setCount(() => {\n          throw new Error('err_in_effect_cleanup')\n        })\n      }\n    }, [setCount])\n    return (\n      <>\n        <div>no error</div>\n      </>\n    )\n  }\n\n  const Main = () => {\n    const [hide, setHide] = useState(false)\n    return (\n      <>\n        <button onClick={() => setHide(true)}>close</button>\n        {!hide && <Counter />}\n      </>\n    )\n  }\n\n  it('[DEV-ONLY] single setCount', () => {\n    render(\n      <>\n        <ErrorBoundary>\n          <Main />\n        </ErrorBoundary>\n      </>,\n    )\n\n    expect(screen.getByText('no error')).toBeInTheDocument()\n    expect(errorMessages.some((m) => m.includes('err_in_effect_cleanup'))).toBe(\n      false,\n    )\n\n    fireEvent.click(screen.getByText('close'))\n\n    // NOTE: Conditional expect is required because behavior differs by React version\n    // AND build mode (dev vs prod). Using it.runIf/skipIf causes production build failures.\n    /* eslint-disable vitest/no-conditional-expect */\n    if (reactVersion.startsWith('17.')) {\n      expect(\n        errorMessages.some((m) => m.includes('err_in_effect_cleanup')),\n      ).toBe(true)\n    } else {\n      expect(\n        screen.getByText('Errored: err_in_effect_cleanup'),\n      ).toBeInTheDocument()\n    }\n    /* eslint-enable vitest/no-conditional-expect */\n  })\n\n  it('[DEV-ONLY] double setCount', () => {\n    doubleSetCount = true\n\n    render(\n      <>\n        <ErrorBoundary>\n          <Main />\n        </ErrorBoundary>\n      </>,\n    )\n\n    expect(screen.getByText('no error')).toBeInTheDocument()\n    expect(errorMessages.some((m) => m.includes('err_in_effect_cleanup'))).toBe(\n      false,\n    )\n\n    fireEvent.click(screen.getByText('close'))\n\n    // NOTE: Conditional expect is required because behavior differs by React version\n    // AND build mode (dev vs prod). Using it.runIf/skipIf causes production build failures.\n    /* eslint-disable vitest/no-conditional-expect */\n    if (reactVersion.startsWith('17.')) {\n      expect(\n        errorMessages.some((m) => m.includes('err_in_effect_cleanup')),\n      ).toBe(true)\n    } else {\n      expect(\n        screen.getByText('Errored: err_in_effect_cleanup'),\n      ).toBeInTheDocument()\n    }\n    /* eslint-enable vitest/no-conditional-expect */\n  })\n})\n\ndescribe('error recovery', () => {\n  const createCounter = () => {\n    const counterAtom = atom(0)\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(counterAtom)\n      return <button onClick={() => setCount(count + 1)}>increment</button>\n    }\n\n    return { Counter, counterAtom }\n  }\n\n  it('recovers from sync errors', () => {\n    const { counterAtom, Counter } = createCounter()\n\n    const syncAtom = atom((get) => {\n      const value = get(counterAtom)\n\n      if (value === 0) {\n        throw new Error('An error occurred')\n      }\n\n      return value\n    })\n\n    const Display = () => {\n      return <div>Value: {useAtom(syncAtom)[0]}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n        <ErrorBoundary>\n          <Display />\n        </ErrorBoundary>\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('Errored: An error occurred')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('increment'))\n    fireEvent.click(screen.getByText('retry'))\n    expect(screen.getByText('Value: 1')).toBeInTheDocument()\n  })\n\n  it('recovers from async errors', async () => {\n    const { counterAtom, Counter } = createCounter()\n    const asyncAtom = atom(async (get) => {\n      const value = get(counterAtom)\n      await sleep(100)\n      if (value === 0) {\n        throw new Error('An error occurred')\n      }\n      return value\n    })\n\n    const Display = () => {\n      return <div>Value: {useAtom(asyncAtom)[0]}</div>\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Counter />\n          <ErrorBoundary>\n            <Suspense fallback=\"loading\">\n              <Display />\n            </Suspense>\n          </ErrorBoundary>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('Errored: An error occurred')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('increment')))\n    await act(() => fireEvent.click(screen.getByText('retry')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('Value: 1')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/react/items.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport type { PrimitiveAtom } from 'jotai/vanilla'\n\nit('remove an item, then add another', () => {\n  type Item = {\n    text: string\n    checked: boolean\n  }\n  let itemIndex = 0\n  const itemsAtom = atom<PrimitiveAtom<Item>[]>([])\n\n  const ListItem = ({\n    itemAtom,\n    remove,\n  }: {\n    itemAtom: PrimitiveAtom<Item>\n    remove: () => void\n  }) => {\n    const [item, setItem] = useAtom(itemAtom)\n    const toggle = () =>\n      setItem((prev) => ({ ...prev, checked: !prev.checked }))\n    return (\n      <>\n        <div>\n          {item.text} checked: {item.checked ? 'yes' : 'no'}\n        </div>\n        <button onClick={toggle}>Check {item.text}</button>\n        <button onClick={remove}>Remove {item.text}</button>\n      </>\n    )\n  }\n\n  const List = () => {\n    const [items, setItems] = useAtom(itemsAtom)\n    const addItem = () => {\n      setItems((prev) => [\n        ...prev,\n        atom<Item>({ text: `item${++itemIndex}`, checked: false }),\n      ])\n    }\n    const removeItem = (itemAtom: PrimitiveAtom<Item>) => {\n      setItems((prev) => prev.filter((x) => x !== itemAtom))\n    }\n    return (\n      <ul>\n        {items.map((itemAtom) => (\n          <ListItem\n            key={`${itemAtom}`}\n            itemAtom={itemAtom}\n            remove={() => removeItem(itemAtom)}\n          />\n        ))}\n        <li>\n          <button onClick={addItem}>Add</button>\n        </li>\n      </ul>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <List />\n    </StrictMode>,\n  )\n\n  fireEvent.click(screen.getByText('Add'))\n  expect(screen.getByText('item1 checked: no')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Add'))\n  expect(screen.getByText('item1 checked: no')).toBeInTheDocument()\n  expect(screen.getByText('item2 checked: no')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Check item2'))\n  expect(screen.getByText('item1 checked: no')).toBeInTheDocument()\n  expect(screen.getByText('item2 checked: yes')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Remove item1'))\n  expect(screen.getByText('item2 checked: yes')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Add'))\n  expect(screen.getByText('item2 checked: yes')).toBeInTheDocument()\n  expect(screen.getByText('item3 checked: no')).toBeInTheDocument()\n})\n\nit('add an item with filtered list', () => {\n  type Item = {\n    text: string\n    checked: boolean\n  }\n  type ItemAtoms = PrimitiveAtom<Item>[]\n  type Update = (prev: ItemAtoms) => ItemAtoms\n\n  let itemIndex = 0\n  const itemAtomsAtom = atom<ItemAtoms>([])\n  const setItemsAtom = atom(null, (_get, set, update: Update) =>\n    set(itemAtomsAtom, update),\n  )\n  const filterAtom = atom<'all' | 'checked' | 'not-checked'>('all')\n  const filteredAtom = atom((get) => {\n    const filter = get(filterAtom)\n    const items = get(itemAtomsAtom)\n    if (filter === 'all') {\n      return items\n    }\n    if (filter === 'checked') {\n      return items.filter((atom) => get(atom).checked)\n    }\n    return items.filter((atom) => !get(atom).checked)\n  })\n\n  const ListItem = ({\n    itemAtom,\n    remove,\n  }: {\n    itemAtom: PrimitiveAtom<Item>\n    remove: () => void\n  }) => {\n    const [item, setItem] = useAtom(itemAtom)\n    const toggle = () =>\n      setItem((prev) => ({ ...prev, checked: !prev.checked }))\n    return (\n      <>\n        <div>\n          {item.text} checked: {item.checked ? 'yes' : 'no'}\n        </div>\n        <button onClick={toggle}>Check {item.text}</button>\n        <button onClick={remove}>Remove {item.text}</button>\n      </>\n    )\n  }\n\n  const Filter = () => {\n    const [filter, setFilter] = useAtom(filterAtom)\n    return (\n      <>\n        <div>{filter}</div>\n        <button onClick={() => setFilter('all')}>All</button>\n        <button onClick={() => setFilter('checked')}>Checked</button>\n        <button onClick={() => setFilter('not-checked')}>Not Checked</button>\n      </>\n    )\n  }\n\n  const FilteredList = ({\n    removeItem,\n  }: {\n    removeItem: (itemAtom: PrimitiveAtom<Item>) => void\n  }) => {\n    const [items] = useAtom(filteredAtom)\n    return (\n      <ul>\n        {items.map((itemAtom) => (\n          <ListItem\n            key={`${itemAtom}`}\n            itemAtom={itemAtom}\n            remove={() => removeItem(itemAtom)}\n          />\n        ))}\n      </ul>\n    )\n  }\n\n  const List = () => {\n    const [, setItems] = useAtom(setItemsAtom)\n    const addItem = () => {\n      setItems((prev) => [\n        ...prev,\n        atom<Item>({ text: `item${++itemIndex}`, checked: false }),\n      ])\n    }\n    const removeItem = (itemAtom: PrimitiveAtom<Item>) => {\n      setItems((prev) => prev.filter((x) => x !== itemAtom))\n    }\n    return (\n      <>\n        <Filter />\n        <button onClick={addItem}>Add</button>\n        <FilteredList removeItem={removeItem} />\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <List />\n    </StrictMode>,\n  )\n\n  fireEvent.click(screen.getByText('Checked'))\n  fireEvent.click(screen.getByText('Add'))\n  fireEvent.click(screen.getByText('All'))\n  expect(screen.getByText('item1 checked: no')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/onmount.test.tsx",
    "content": "import { StrictMode, Suspense, useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { sleep } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('one atom, one effect', () => {\n  const countAtom = atom(1)\n  const onMountFn = vi.fn(() => {})\n  countAtom.onMount = onMountFn\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n})\n\nit('two atoms, one each', () => {\n  const countAtom = atom(1)\n  const countAtom2 = atom(1)\n  const onMountFn = vi.fn(() => {})\n  const onMountFn2 = vi.fn(() => {})\n  countAtom.onMount = onMountFn\n  countAtom2.onMount = onMountFn2\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const [count2, setCount2] = useAtom(countAtom2)\n    return (\n      <>\n        <div>count: {count}</div>\n        <div>count2: {count2}</div>\n        <button\n          onClick={() => {\n            setCount((c) => c + 1)\n            setCount2((c) => c + 1)\n          }}\n        >\n          button\n        </button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('count2: 1')).toBeInTheDocument()\n\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onMountFn2).toHaveBeenCalledTimes(1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(screen.getByText('count2: 2')).toBeInTheDocument()\n\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onMountFn2).toHaveBeenCalledTimes(1)\n})\n\nit('one derived atom, one onMount', () => {\n  const countAtom = atom(1)\n  const countAtom2 = atom((get) => get(countAtom))\n  const onMountFn = vi.fn(() => {})\n  countAtom.onMount = onMountFn\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom2)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n})\n\nit('mount/unmount test', () => {\n  const countAtom = atom(1)\n\n  const onUnMountFn = vi.fn()\n  const onMountFn = vi.fn(() => onUnMountFn)\n  countAtom.onMount = onMountFn\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n\n  const Display = () => {\n    const [display, setDisplay] = useState(true)\n    return (\n      <>\n        {display ? <Counter /> : null}\n        <button onClick={() => setDisplay((c) => !c)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Display />\n    </>,\n  )\n\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onUnMountFn).toHaveBeenCalledTimes(0)\n\n  fireEvent.click(screen.getByText('button'))\n\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onUnMountFn).toHaveBeenCalledTimes(1)\n})\n\nit('one derived atom, one onMount for the derived one, and one for the regular atom + onUnMount', () => {\n  const countAtom = atom(1)\n  const derivedAtom = atom(\n    (get) => get(countAtom),\n    (_get, set, update: number) => {\n      set(countAtom, update)\n      set(derivedAtom, update)\n    },\n  )\n  const onUnMountFn = vi.fn()\n  const onMountFn = vi.fn(() => onUnMountFn)\n  countAtom.onMount = onMountFn\n  const derivedOnUnMountFn = vi.fn()\n  const derivedOnMountFn = vi.fn(() => derivedOnUnMountFn)\n  derivedAtom.onMount = derivedOnMountFn\n\n  const Counter = () => {\n    const [count] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n\n  const Display = () => {\n    const [display, setDisplay] = useState(true)\n    return (\n      <>\n        {display ? <Counter /> : null}\n        <button onClick={() => setDisplay((c) => !c)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Display />\n    </>,\n  )\n\n  expect(derivedOnMountFn).toHaveBeenCalledTimes(1)\n  expect(derivedOnUnMountFn).toHaveBeenCalledTimes(0)\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onUnMountFn).toHaveBeenCalledTimes(0)\n\n  fireEvent.click(screen.getByText('button'))\n\n  expect(derivedOnMountFn).toHaveBeenCalledTimes(1)\n  expect(derivedOnUnMountFn).toHaveBeenCalledTimes(1)\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onUnMountFn).toHaveBeenCalledTimes(1)\n})\n\nit('mount/unMount order', () => {\n  const committed: number[] = [0, 0]\n  const countAtom = atom(1)\n  const derivedAtom = atom(\n    (get) => get(countAtom),\n    (_get, set, update: number) => {\n      set(countAtom, update)\n      set(derivedAtom, update)\n    },\n  )\n  const onUnMountFn = vi.fn(() => {\n    committed[0] = 0\n  })\n  const onMountFn = vi.fn(() => {\n    committed[0] = 1\n    return onUnMountFn\n  })\n  countAtom.onMount = onMountFn\n  const derivedOnUnMountFn = vi.fn(() => {\n    committed[1] = 0\n  })\n  const derivedOnMountFn = vi.fn(() => {\n    committed[1] = 1\n    return derivedOnUnMountFn\n  })\n  derivedAtom.onMount = derivedOnMountFn\n\n  const Counter2 = () => {\n    const [count] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    const [display, setDisplay] = useState(false)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setDisplay((c) => !c)}>derived atom</button>\n        {display ? <Counter2 /> : null}\n      </>\n    )\n  }\n\n  const Display = () => {\n    const [display, setDisplay] = useState(false)\n    return (\n      <>\n        {display ? <Counter /> : null}\n        <button onClick={() => setDisplay((c) => !c)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Display />\n    </StrictMode>,\n  )\n\n  expect(committed).toEqual([0, 0])\n\n  fireEvent.click(screen.getByText('button'))\n  expect(committed).toEqual([1, 0])\n\n  fireEvent.click(screen.getByText('derived atom'))\n  expect(committed).toEqual([1, 1])\n\n  fireEvent.click(screen.getByText('derived atom'))\n  expect(committed).toEqual([1, 0])\n\n  fireEvent.click(screen.getByText('button'))\n  expect(committed).toEqual([0, 0])\n})\n\nit('mount/unmount test with async atom', async () => {\n  const countAtom = atom(\n    async () => {\n      await sleep(100)\n      return 0\n    },\n    () => {},\n  )\n\n  const onUnMountFn = vi.fn()\n  const onMountFn = vi.fn(() => onUnMountFn)\n  countAtom.onMount = onMountFn\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n\n  const Display = () => {\n    const [display, setDisplay] = useState(true)\n    return (\n      <>\n        {display ? <Counter /> : null}\n        <button onClick={() => setDisplay((c) => !c)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <>\n        <Suspense fallback=\"loading\">\n          <Display />\n        </Suspense>\n      </>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  await act(() => vi.advanceTimersByTimeAsync(100))\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onUnMountFn).toHaveBeenCalledTimes(0)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n  expect(onUnMountFn).toHaveBeenCalledTimes(1)\n})\n\nit('subscription usage test', () => {\n  const store = {\n    count: 10,\n    listeners: new Set<() => void>(),\n    inc: () => {\n      store.count += 1\n      store.listeners.forEach((listener) => listener())\n    },\n  }\n\n  const countAtom = atom(1)\n  countAtom.onMount = (setCount) => {\n    const callback = () => {\n      setCount(store.count)\n    }\n    store.listeners.add(callback)\n    callback()\n    return () => store.listeners.delete(callback)\n  }\n\n  const Counter = () => {\n    const [count] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n      </>\n    )\n  }\n\n  const Display = () => {\n    const [display, setDisplay] = useState(true)\n    return (\n      <>\n        {display ? <Counter /> : 'N/A'}\n        <button onClick={() => setDisplay((c) => !c)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Display />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n  act(() => store.inc())\n\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('N/A')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('N/A')).toBeInTheDocument()\n\n  act(() => store.inc())\n\n  expect(screen.getByText('N/A')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 12')).toBeInTheDocument()\n})\n\nit('subscription in base atom test', () => {\n  const store = {\n    count: 10,\n    listeners: new Set<() => void>(),\n    add: (n: number) => {\n      store.count += n\n      store.listeners.forEach((listener) => listener())\n    },\n  }\n\n  const countAtom = atom(1)\n  countAtom.onMount = (setCount) => {\n    const callback = () => {\n      setCount(store.count)\n    }\n    store.listeners.add(callback)\n    callback()\n    return () => store.listeners.delete(callback)\n  }\n  const derivedAtom = atom(\n    (get) => get(countAtom),\n    (_get, _set, n: number) => {\n      store.add(n)\n    },\n  )\n\n  const Counter = () => {\n    const [count, add] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => add(1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 12')).toBeInTheDocument()\n})\n\nit('create atom with onMount in async get', async () => {\n  const store = {\n    count: 10,\n    listeners: new Set<() => void>(),\n    add: (n: number) => {\n      store.count += n\n      store.listeners.forEach((listener) => listener())\n    },\n  }\n\n  const holderAtom = atom(async () => {\n    const countAtom = atom(1)\n    countAtom.onMount = (setCount) => {\n      const callback = () => {\n        setCount(store.count)\n      }\n      store.listeners.add(callback)\n      callback()\n      return () => store.listeners.delete(callback)\n    }\n    return countAtom\n  })\n  const derivedAtom = atom(\n    async (get) => get(await get(holderAtom)),\n    (_get, _set, n: number) => {\n      store.add(n)\n    },\n  )\n\n  const Counter = () => {\n    const [count, add] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => add(1)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  // FIXME this is not working\n  // await screen.findByText('count: 1')\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 12')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/optimization.test.tsx",
    "content": "import { StrictMode, useEffect } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\n\nit('only relevant render function called (#156)', () => {\n  const count1Atom = atom(0)\n  const count2Atom = atom(0)\n\n  let viewCount1 = 0\n  let viewCount2 = 0\n\n  const Counter1 = () => {\n    const [count, setCount] = useAtom(count1Atom)\n    ++viewCount1\n    return (\n      <>\n        <div>count1: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button1</button>\n      </>\n    )\n  }\n\n  const Counter2 = () => {\n    const [count, setCount] = useAtom(count2Atom)\n    ++viewCount2\n    return (\n      <>\n        <div>count2: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button2</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter1 />\n      <Counter2 />\n    </>,\n  )\n\n  expect(screen.getByText('count1: 0')).toBeInTheDocument()\n  expect(screen.getByText('count2: 0')).toBeInTheDocument()\n\n  const viewCount1AfterMount = viewCount1\n  const viewCount2AfterMount = viewCount2\n\n  fireEvent.click(screen.getByText('button1'))\n  expect(screen.getByText('count1: 1')).toBeInTheDocument()\n  expect(screen.getByText('count2: 0')).toBeInTheDocument()\n\n  expect(viewCount1).toBe(viewCount1AfterMount + 1)\n  expect(viewCount2).toBe(viewCount2AfterMount + 0)\n\n  fireEvent.click(screen.getByText('button2'))\n  expect(screen.getByText('count1: 1')).toBeInTheDocument()\n  expect(screen.getByText('count2: 1')).toBeInTheDocument()\n\n  expect(viewCount1).toBe(viewCount1AfterMount + 1)\n  expect(viewCount2).toBe(viewCount2AfterMount + 1)\n})\n\nit('only render once using atoms with write-only atom', () => {\n  const count1Atom = atom(0)\n  const count2Atom = atom(0)\n  const incrementAtom = atom(null, (_get, set, _arg) => {\n    set(count1Atom, (c) => c + 1)\n    set(count2Atom, (c) => c + 1)\n  })\n\n  let viewCount = 0\n\n  const Counter = () => {\n    const [count1] = useAtom(count1Atom)\n    const [count2] = useAtom(count2Atom)\n    ++viewCount\n    return (\n      <div>\n        count1: {count1}, count2: {count2}\n      </div>\n    )\n  }\n\n  const Control = () => {\n    const [, increment] = useAtom(incrementAtom)\n    return <button onClick={increment}>button</button>\n  }\n\n  render(\n    <>\n      <Counter />\n      <Control />\n    </>,\n  )\n\n  expect(screen.getByText('count1: 0, count2: 0')).toBeInTheDocument()\n  const viewCountAfterMount = viewCount\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count1: 1, count2: 1')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count1: 2, count2: 2')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 2)\n})\n\nit('useless re-renders with static atoms (#355)', () => {\n  // check out https://codesandbox.io/s/m82r5 to see the expected re-renders\n  const countAtom = atom(0)\n  const unrelatedAtom = atom(0)\n\n  let viewCount = 0\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    useAtom(unrelatedAtom)\n    ++viewCount\n\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  const viewCountAfterMount = viewCount\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 1)\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 2)\n})\n\nit('does not re-render if value is the same (#1158)', () => {\n  const countAtom = atom(0)\n\n  let viewCount = 0\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    ++viewCount\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c)}>noop</button>\n        <button onClick={() => setCount((c) => c + 1)}>inc</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter />\n    </>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  const viewCountAfterMount = viewCount\n\n  fireEvent.click(screen.getByText('noop'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 0)\n\n  fireEvent.click(screen.getByText('inc'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 1)\n\n  fireEvent.click(screen.getByText('noop'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 1)\n\n  fireEvent.click(screen.getByText('inc'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n  expect(viewCount).toBe(viewCountAfterMount + 2)\n})\n\nit('no extra rerenders after commit with derived atoms (#1213)', () => {\n  const baseAtom = atom({ count1: 0, count2: 0 })\n  const count1Atom = atom((get) => get(baseAtom).count1)\n  const count2Atom = atom((get) => get(baseAtom).count2)\n\n  let viewCount1 = 0\n  let viewCount1AfterCommit = 0\n\n  const Counter1 = () => {\n    const [count1] = useAtom(count1Atom)\n    ++viewCount1\n    useEffect(() => {\n      viewCount1AfterCommit = viewCount1\n    })\n    return <div>count1: {count1}</div>\n  }\n\n  let viewCount2 = 0\n  let viewCount2AfterCommit = 0\n\n  const Counter2 = () => {\n    const [count2] = useAtom(count2Atom)\n    ++viewCount2\n    useEffect(() => {\n      viewCount2AfterCommit = viewCount2\n    })\n    return <div>count2: {count2}</div>\n  }\n\n  const Control = () => {\n    const [, setValue] = useAtom(baseAtom)\n    const inc1 = () => {\n      setValue((prev) => ({ ...prev, count1: prev.count1 + 1 }))\n    }\n    const inc2 = () => {\n      setValue((prev) => ({ ...prev, count2: prev.count2 + 1 }))\n    }\n    return (\n      <div>\n        <button onClick={inc1}>inc1</button>\n        <button onClick={inc2}>inc2</button>\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter1 />\n      <Counter2 />\n      <Control />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count1: 0')).toBeInTheDocument()\n  expect(screen.getByText('count2: 0')).toBeInTheDocument()\n\n  expect(viewCount1 > 0).toBeTruthy()\n  expect(viewCount2 > 0).toBeTruthy()\n\n  fireEvent.click(screen.getByText('inc1'))\n  expect(screen.getByText('count1: 1')).toBeInTheDocument()\n  expect(screen.getByText('count2: 0')).toBeInTheDocument()\n\n  expect(viewCount1).toBe(viewCount1AfterCommit)\n\n  fireEvent.click(screen.getByText('inc2'))\n  expect(screen.getByText('count1: 1')).toBeInTheDocument()\n  expect(screen.getByText('count2: 1')).toBeInTheDocument()\n\n  expect(viewCount2).toBe(viewCount2AfterCommit)\n\n  fireEvent.click(screen.getByText('inc1'))\n  expect(screen.getByText('count1: 2')).toBeInTheDocument()\n  expect(screen.getByText('count2: 1')).toBeInTheDocument()\n\n  expect(viewCount1).toBe(viewCount1AfterCommit)\n})\n"
  },
  {
    "path": "tests/react/provider.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { Provider, useAtom } from 'jotai/react'\nimport { atom, createStore } from 'jotai/vanilla'\n\nit('uses initial values from provider', () => {\n  const countAtom = atom(1)\n  const petAtom = atom('cat')\n\n  const Display = () => {\n    const [count] = useAtom(countAtom)\n    const [pet] = useAtom(petAtom)\n\n    return (\n      <>\n        <p>count: {count}</p>\n        <p>pet: {pet}</p>\n      </>\n    )\n  }\n\n  const store = createStore()\n  store.set(countAtom, 0)\n  store.set(petAtom, 'dog')\n\n  render(\n    <StrictMode>\n      <Provider store={store}>\n        <Display />\n      </Provider>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  expect(screen.getByText('pet: dog')).toBeInTheDocument()\n})\n\nit('only uses initial value from provider for specific atom', () => {\n  const countAtom = atom(1)\n  const petAtom = atom('cat')\n\n  const Display = () => {\n    const [count] = useAtom(countAtom)\n    const [pet] = useAtom(petAtom)\n\n    return (\n      <>\n        <p>count: {count}</p>\n        <p>pet: {pet}</p>\n      </>\n    )\n  }\n\n  const store = createStore()\n  store.set(petAtom, 'dog')\n\n  render(\n    <StrictMode>\n      <Provider store={store}>\n        <Display />\n      </Provider>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('pet: dog')).toBeInTheDocument()\n})\n\nit('renders correctly without children', () => {\n  const { container } = render(\n    <StrictMode>\n      <Provider />\n    </StrictMode>,\n  )\n\n  expect(container).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/transition.test.tsx",
    "content": "import ReactExports, { StrictMode, Suspense, useEffect } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { useAtom, useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { sleep } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nconst { useTransition } = ReactExports\n\ndescribe.skipIf(typeof useTransition !== 'function')('useTransition', () => {\n  it('no extra commit with useTransition (#1125)', async () => {\n    const countAtom = atom(0)\n    const delayedAtom = atom(async (get) => {\n      await sleep(100)\n      return get(countAtom)\n    })\n\n    const committed: { pending: boolean; delayed: number }[] = []\n\n    const Counter = () => {\n      const setCount = useSetAtom(countAtom)\n      const delayed = useAtomValue(delayedAtom)\n      const [pending, startTransition] = useTransition()\n      useEffect(() => {\n        committed.push({ pending, delayed })\n      })\n      return (\n        <>\n          <div>delayed: {delayed}</div>\n          <button onClick={() => startTransition(() => setCount((c) => c + 1))}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('delayed: 0')).toBeInTheDocument()\n\n    await act(async () => {\n      fireEvent.click(screen.getByText('button'))\n    })\n    expect(screen.getByText('delayed: 0')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('delayed: 1')).toBeInTheDocument()\n\n    expect(committed).toEqual([\n      { pending: false, delayed: 0 },\n      { pending: true, delayed: 0 },\n      { pending: false, delayed: 1 },\n    ])\n  })\n\n  it('can update normal atom with useTransition (#1151)', async () => {\n    const countAtom = atom(0)\n    const toggleAtom = atom(false)\n    const pendingAtom = atom((get) => {\n      if (get(toggleAtom)) {\n        return new Promise(() => {})\n      }\n      return false\n    })\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(countAtom)\n      const toggle = useSetAtom(toggleAtom)\n      useAtomValue(pendingAtom)\n      const [pending, startTransition] = useTransition()\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount((c) => c + 1)}>increment</button>\n          {pending && 'pending'}\n          <button onClick={() => startTransition(() => toggle((x) => !x))}>\n            toggle\n          </button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('toggle')))\n    expect(screen.getByText('pending')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('increment')))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('increment')))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/react/types.test.tsx",
    "content": "import { expect, expectTypeOf, it } from 'vitest'\nimport { useAtom, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\n\nit('useAtom should return the correct types', () => {\n  function Component() {\n    // primitive atom\n    const primitiveAtom = atom(0)\n    // NOTE: expectTypeOf is not available in TypeScript 4.0.5 and below\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useAtom(primitiveAtom)).toEqualTypeOf<\n      [number, (arg: number | ((prev: number) => number)) => void]\n    >()\n\n    // read-only derived atom\n    const readonlyDerivedAtom = atom((get) => get(primitiveAtom) * 2)\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useAtom(readonlyDerivedAtom)).toEqualTypeOf<[number, never]>()\n\n    // read-write derived atom\n    const readWriteDerivedAtom = atom(\n      (get) => get(primitiveAtom),\n      (get, set, value: number) => {\n        set(primitiveAtom, get(primitiveAtom) + value)\n      },\n    )\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useAtom(readWriteDerivedAtom)).toEqualTypeOf<\n      [number, (arg: number) => void]\n    >()\n\n    // write-only derived atom\n    const writeonlyDerivedAtom = atom(null, (get, set) => {\n      set(primitiveAtom, get(primitiveAtom) - 1)\n    })\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useAtom(writeonlyDerivedAtom)).toEqualTypeOf<\n      [null, () => void]\n    >()\n  }\n  expect(Component).toBeDefined()\n})\n\nit('useAtom should handle inference of atoms (#1831 #1387)', () => {\n  const fieldAtoms = {\n    username: atom(''),\n    age: atom(0),\n    checked: atom(false),\n  }\n  const useField = <T extends keyof typeof fieldAtoms>(prop: T) => {\n    return useAtom(fieldAtoms[prop])\n  }\n  function Component() {\n    const [username, setUsername] = useField('username')\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(username).toEqualTypeOf<string>()\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setUsername).toEqualTypeOf<\n      (arg: string | ((prev: string) => string)) => void\n    >()\n\n    const [age, setAge] = useField('age')\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(age).toEqualTypeOf<number>()\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setAge).toEqualTypeOf<\n      (arg: number | ((prev: number) => number)) => void\n    >()\n\n    const [checked, setChecked] = useField('checked')\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(checked).toEqualTypeOf<boolean>()\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setChecked).toEqualTypeOf<\n      (arg: boolean | ((prev: boolean) => boolean)) => void\n    >()\n  }\n  expect(Component).toBeDefined()\n})\n\nit('useAtom should handle inference of read-only atoms', () => {\n  const fieldAtoms = {\n    username: atom(() => ''),\n    age: atom(() => 0),\n    checked: atom(() => false),\n  }\n  const useField = <T extends keyof typeof fieldAtoms>(prop: T) => {\n    return useAtom(fieldAtoms[prop])\n  }\n  function Component() {\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useField('username')).toEqualTypeOf<[string, never]>()\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useField('age')).toEqualTypeOf<[number, never]>()\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(useField('checked')).toEqualTypeOf<[boolean, never]>()\n  }\n  expect(Component).toBeDefined()\n})\n\nit('useSetAtom should handle inference of atoms', () => {\n  const fieldAtoms = {\n    username: atom(''),\n    age: atom(0),\n    checked: atom(false),\n  }\n  const useSetField = <T extends keyof typeof fieldAtoms>(prop: T) => {\n    return useSetAtom(fieldAtoms[prop])\n  }\n  function Component() {\n    const setUsername = useSetField('username')\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setUsername).toEqualTypeOf<\n      (arg: string | ((prev: string) => string)) => void\n    >()\n\n    const setAge = useSetField('age')\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setAge).toEqualTypeOf<\n      (arg: number | ((prev: number) => number)) => void\n    >()\n\n    const setChecked = useSetField('checked')\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setChecked).toEqualTypeOf<\n      (arg: boolean | ((prev: boolean) => boolean)) => void\n    >()\n  }\n  expect(Component).toBeDefined()\n})\n\nit('useAtom should handle primitive atom with one type argument', () => {\n  const countAtom = atom(0)\n  function Component() {\n    const [count, setCount] = useAtom<number>(countAtom)\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(count).toEqualTypeOf<number>()\n    // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    expectTypeOf(setCount).toEqualTypeOf<\n      (arg: number | ((prev: number) => number)) => void\n    >()\n  }\n  expect(Component).toBeDefined()\n})\n"
  },
  {
    "path": "tests/react/useAtomValue.test.tsx",
    "content": "import { Component, StrictMode, Suspense } from 'react'\nimport type { ReactNode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('useAtomValue basic test', async () => {\n  const countAtom = atom(0)\n\n  const Counter = () => {\n    const count = useAtomValue(countAtom)\n    const setCount = useSetAtom(countAtom)\n\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount(count + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('useAtomValue with async atom (promise)', async () => {\n  const asyncAtom = atom(async () => 42)\n\n  const AsyncComponent = () => {\n    const value = useAtomValue(asyncAtom)\n\n    return <div>value: {value}</div>\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <AsyncComponent />\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('value: 42')).toBeInTheDocument()\n})\n\nclass ErrorBoundary extends Component<\n  { children: ReactNode },\n  { error: Error | null }\n> {\n  constructor(props: { children: ReactNode }) {\n    super(props)\n\n    this.state = { error: null }\n  }\n\n  static getDerivedStateFromError(error: Error) {\n    return { error }\n  }\n\n  render() {\n    if (this.state.error) {\n      return <div>error: {this.state.error.message}</div>\n    }\n\n    return this.props.children\n  }\n}\n\nit('useAtomValue with error throwing atom', async () => {\n  const errorAtom = atom(() => {\n    throw new Error('fail')\n  })\n\n  const ErrorComponent = () => {\n    useAtomValue(errorAtom)\n\n    return <div>no error</div>\n  }\n\n  render(\n    <StrictMode>\n      <ErrorBoundary>\n        <ErrorComponent />\n      </ErrorBoundary>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('error: fail')).toBeInTheDocument()\n})\n\nit('useAtomValue with atom returning object', async () => {\n  const objAtom = atom({ a: 1, b: 2 })\n\n  const ObjComponent = () => {\n    const value = useAtomValue(objAtom)\n\n    return (\n      <div>\n        obj: {value.a},{value.b}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <ObjComponent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('obj: 1,2')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/useSetAtom.test.tsx",
    "content": "import { StrictMode, useEffect } from 'react'\nimport type { PropsWithChildren } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { useCommitCount } from '../test-utils'\n\nit('useSetAtom does not trigger rerender in component', async () => {\n  const countAtom = atom(0)\n\n  const Displayer = () => {\n    const count = useAtomValue(countAtom)\n    const commits = useCommitCount()\n    return (\n      <div>\n        count: {count}, commits: {commits}\n      </div>\n    )\n  }\n\n  const Updater = () => {\n    const setCount = useSetAtom(countAtom)\n    const commits = useCommitCount()\n    return (\n      <>\n        <button onClick={() => setCount((value) => value + 1)}>\n          increment\n        </button>\n        <div>updater commits: {commits}</div>\n      </>\n    )\n  }\n\n  const Parent = () => {\n    return (\n      <>\n        <Displayer />\n        <Updater />\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Parent />\n    </>,\n  )\n\n  expect(screen.getByText('count: 0, commits: 1')).toBeInTheDocument()\n  expect(screen.getByText('updater commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 1, commits: 2')).toBeInTheDocument()\n  expect(screen.getByText('updater commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 2, commits: 3')).toBeInTheDocument()\n  expect(screen.getByText('updater commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 3, commits: 4')).toBeInTheDocument()\n  expect(screen.getByText('updater commits: 1')).toBeInTheDocument()\n})\n\nit('useSetAtom with write without an argument', async () => {\n  const countAtom = atom(0)\n  const incrementCountAtom = atom(null, (get, set) =>\n    set(countAtom, get(countAtom) + 1),\n  )\n\n  const Button = ({ cb, children }: PropsWithChildren<{ cb: () => void }>) => (\n    <button onClick={cb}>{children}</button>\n  )\n\n  const Displayer = () => {\n    const count = useAtomValue(countAtom)\n    return <div>count: {count}</div>\n  }\n\n  const Updater = () => {\n    const setCount = useSetAtom(incrementCountAtom)\n    return <Button cb={setCount}>increment</Button>\n  }\n\n  const Parent = () => {\n    return (\n      <>\n        <Displayer />\n        <Updater />\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('[DEV-ONLY] useSetAtom throws when called with a read-only atom', () => {\n  expect.assertions(1)\n  const countAtom = atom(0)\n  const readOnlyAtom = atom((get) => get(countAtom))\n\n  function TestComponent() {\n    const setAtom = useSetAtom(readOnlyAtom as any)\n\n    useEffect(() => {\n      expect(() => setAtom(1)).toThrow('not writable atom')\n    }, [setAtom])\n\n    return null\n  }\n\n  render(<TestComponent />)\n})\n"
  },
  {
    "path": "tests/react/utils/types.test.tsx",
    "content": "import { expect, it } from 'vitest'\nimport { useHydrateAtoms } from 'jotai/react/utils'\nimport { atom } from 'jotai/vanilla'\n\nit('useHydrateAtoms should not allow invalid atom types when array is passed', () => {\n  function Component() {\n    const countAtom = atom(0)\n    const activeAtom = atom(true)\n    // @ts-expect-error TS2769 [SKIP-TS-3.9.7]\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([\n      [countAtom, 'foo'],\n      [activeAtom, 0],\n    ])\n    // @ts-expect-error TS2769 [SKIP-TS-5.0.4] [SKIP-TS-4.9.5] [SKIP-TS-4.8.4] [SKIP-TS-4.7.4] [SKIP-TS-4.6.4] [SKIP-TS-4.5.5] [SKIP-TS-4.4.4] [SKIP-TS-4.3.5] [SKIP-TS-4.2.3] [SKIP-TS-4.1.5] [SKIP-TS-4.0.5] [SKIP-TS-3.9.7]\n    // [ONLY-TS-5.0.4] [ONLY-TS-4.9.5] [ONLY-TS-4.8.4] [ONLY-TS-4.7.4] [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([\n      [countAtom, 1],\n      [activeAtom, 0],\n    ])\n    // @ts-expect-error TS2769 [SKIP-TS-5.0.4] [SKIP-TS-4.9.5] [SKIP-TS-4.8.4] [SKIP-TS-4.7.4] [SKIP-TS-4.6.4] [SKIP-TS-4.5.5] [SKIP-TS-4.4.4] [SKIP-TS-4.3.5] [SKIP-TS-4.2.3] [SKIP-TS-4.1.5] [SKIP-TS-4.0.5] [SKIP-TS-3.9.7]\n    // [ONLY-TS-5.0.4] [ONLY-TS-4.9.5] [ONLY-TS-4.8.4] [ONLY-TS-4.7.4] [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([\n      [countAtom, true],\n      [activeAtom, false],\n    ])\n    // Valid case\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([\n      [countAtom, 1],\n      [activeAtom, true],\n    ])\n  }\n  expect(Component).toBeDefined()\n})\n"
  },
  {
    "path": "tests/react/utils/useAtomCallback.test.tsx",
    "content": "import { StrictMode, useCallback, useEffect, useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { useAtomCallback } from 'jotai/react/utils'\nimport { atom } from 'jotai/vanilla'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('useAtomCallback with get', async () => {\n  const countAtom = atom(0)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>atom count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  const Monitor = () => {\n    const [count, setCount] = useState(0)\n    const readCount = useAtomCallback(\n      useCallback((get) => {\n        const currentCount = get(countAtom)\n        setCount(currentCount)\n        return currentCount\n      }, []),\n    )\n    useEffect(() => {\n      const timer = setInterval(() => {\n        readCount()\n      }, 100)\n      return () => {\n        clearInterval(timer)\n      }\n    }, [readCount])\n    return (\n      <>\n        <div>state count: {count}</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n      <Monitor />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('atom count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  await act(() => vi.advanceTimersByTime(100))\n  expect(screen.getByText('atom count: 1')).toBeInTheDocument()\n  expect(screen.getByText('state count: 1')).toBeInTheDocument()\n})\n\nit('useAtomCallback with set and update', async () => {\n  const countAtom = atom(0)\n  const changeableAtom = atom(0)\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  const Monitor = () => {\n    const [changeableCount] = useAtom(changeableAtom)\n    const changeCount = useAtomCallback(\n      useCallback((get, set) => {\n        const currentCount = get(countAtom)\n        set(changeableAtom, currentCount)\n        return currentCount\n      }, []),\n    )\n    useEffect(() => {\n      const timer = setInterval(() => {\n        changeCount()\n      }, 100)\n      return () => {\n        clearInterval(timer)\n      }\n    }, [changeCount])\n    return (\n      <>\n        <div>changeable count: {changeableCount}</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n      <Monitor />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  await act(() => vi.advanceTimersByTime(100))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n  expect(screen.getByText('changeable count: 1')).toBeInTheDocument()\n})\n\nit('useAtomCallback with set and update and arg', () => {\n  const countAtom = atom(0)\n\n  const App = () => {\n    const [count] = useAtom(countAtom)\n    const setCount = useAtomCallback(\n      useCallback((_get, set, arg: number) => {\n        set(countAtom, arg)\n        return arg\n      }, []),\n    )\n\n    return (\n      <div>\n        <p>count: {count}</p>\n        <button onClick={() => setCount(42)}>dispatch</button>\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n})\n\nit('useAtomCallback with sync atom (#1100)', () => {\n  const countAtom = atom(0)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    const readCount = useAtomCallback(useCallback((get) => get(countAtom), []))\n    useEffect(() => {\n      const promiseOrValue = readCount()\n      if (typeof promiseOrValue !== 'number') {\n        throw new Error('should return number')\n      }\n    }, [readCount])\n    return (\n      <>\n        <div>atom count: {count}</div>\n        <button onClick={() => setCount((c) => c + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('atom count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('atom count: 1')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/utils/useHydrateAtoms.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it, vi } from 'vitest'\nimport { useAtom, useAtomValue } from 'jotai/react'\nimport { useHydrateAtoms } from 'jotai/react/utils'\nimport type { Atom, PrimitiveAtom, WritableAtom } from 'jotai/vanilla'\nimport { atom } from 'jotai/vanilla'\nimport { useCommitCount } from '../../test-utils'\n\nit('useHydrateAtoms should only hydrate on first render', () => {\n  const countAtom = atom(0)\n  const statusAtom = atom('fulfilled')\n\n  const Counter = ({\n    initialCount,\n    initialStatus,\n  }: {\n    initialCount: number\n    initialStatus: string\n  }) => {\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([\n      [countAtom, initialCount],\n      [statusAtom, initialStatus],\n    ])\n    const [countValue, setCount] = useAtom(countAtom)\n    const [statusValue, setStatus] = useAtom(statusAtom)\n\n    return (\n      <>\n        <div>count: {countValue}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n        <div>status: {statusValue}</div>\n        <button\n          onClick={() =>\n            setStatus((status) =>\n              status === 'fulfilled' ? 'rejected' : 'fulfilled',\n            )\n          }\n        >\n          update\n        </button>\n      </>\n    )\n  }\n  const { rerender } = render(\n    <StrictMode>\n      <Counter initialCount={42} initialStatus=\"rejected\" />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n  expect(screen.getByText('status: rejected')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  fireEvent.click(screen.getByText('update'))\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('status: fulfilled')).toBeInTheDocument()\n\n  rerender(\n    <StrictMode>\n      <Counter initialCount={65} initialStatus=\"rejected\" />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('status: fulfilled')).toBeInTheDocument()\n})\n\nit('useHydrateAtoms should only hydrate on first render using a Map', () => {\n  const countAtom = atom(0)\n  const activeAtom = atom(true)\n\n  const Counter = ({\n    initialActive = false,\n    initialCount,\n  }: {\n    initialActive?: boolean\n    initialCount: number\n  }) => {\n    useHydrateAtoms(\n      new Map<\n        typeof activeAtom | typeof countAtom,\n        typeof initialActive | typeof initialCount\n      >([\n        [activeAtom, initialActive],\n        [countAtom, initialCount],\n      ]),\n    )\n    const activeValue = useAtomValue(activeAtom)\n    const [countValue, setCount] = useAtom(countAtom)\n\n    return (\n      <>\n        <div>is active: {activeValue ? 'yes' : 'no'}</div>\n        <div>count: {countValue}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  const { rerender } = render(\n    <StrictMode>\n      <Counter initialCount={42} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n  expect(screen.getByText('is active: no')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n\n  rerender(\n    <StrictMode>\n      <Counter initialCount={65} initialActive={true} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('is active: no')).toBeInTheDocument()\n})\n\nit('useHydrateAtoms should not trigger unnecessary re-renders', () => {\n  const countAtom = atom(0)\n\n  const Counter = ({ initialCount }: { initialCount: number }) => {\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([[countAtom, initialCount]])\n    const [countValue, setCount] = useAtom(countAtom)\n    const commits = useCommitCount()\n    return (\n      <>\n        <div>commits: {commits}</div>\n        <div>count: {countValue}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Counter initialCount={42} />\n    </>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('commits: 2')).toBeInTheDocument()\n})\n\nit('useHydrateAtoms should work with derived atoms', () => {\n  const countAtom = atom(0)\n  const doubleAtom = atom((get) => get(countAtom) * 2)\n\n  const Counter = ({ initialCount }: { initialCount: number }) => {\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([[countAtom, initialCount]])\n    const [countValue, setCount] = useAtom(countAtom)\n    const [doubleCount] = useAtom(doubleAtom)\n    return (\n      <>\n        <div>count: {countValue}</div>\n        <div>doubleCount: {doubleCount}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter initialCount={42} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n  expect(screen.getByText('doubleCount: 84')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('doubleCount: 86')).toBeInTheDocument()\n})\n\nit('useHydrateAtoms can only restore an atom once', () => {\n  const countAtom = atom(0)\n\n  const Counter = ({ initialCount }: { initialCount: number }) => {\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([[countAtom, initialCount]])\n    const [countValue, setCount] = useAtom(countAtom)\n\n    return (\n      <>\n        <div>count: {countValue}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n      </>\n    )\n  }\n  const Counter2 = ({ count }: { count: number }) => {\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([[countAtom, count]])\n    const [countValue, setCount] = useAtom(countAtom)\n\n    return (\n      <>\n        <div>count: {countValue}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n      </>\n    )\n  }\n\n  const { rerender } = render(\n    <StrictMode>\n      <Counter initialCount={42} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n\n  rerender(\n    <StrictMode>\n      <Counter2 count={65} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 44')).toBeInTheDocument()\n})\n\nit('useHydrateAtoms should respect onMount', () => {\n  const countAtom = atom(0)\n  const onMountFn = vi.fn(() => {})\n  countAtom.onMount = onMountFn\n\n  const Counter = ({ initialCount }: { initialCount: number }) => {\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([[countAtom, initialCount]])\n    const [countValue] = useAtom(countAtom)\n\n    return <div>count: {countValue}</div>\n  }\n\n  render(\n    <>\n      <Counter initialCount={42} />\n    </>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n  expect(onMountFn).toHaveBeenCalledTimes(1)\n})\n\nit('passing dangerouslyForceHydrate to useHydrateAtoms will re-hydrated atoms', () => {\n  const countAtom = atom(0)\n  const statusAtom = atom('fulfilled')\n\n  const Counter = ({\n    initialCount,\n    initialStatus,\n    dangerouslyForceHydrate = false,\n  }: {\n    initialCount: number\n    initialStatus: string\n    dangerouslyForceHydrate?: boolean\n  }) => {\n    useHydrateAtoms(\n      // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n      [\n        [countAtom, initialCount],\n        [statusAtom, initialStatus],\n      ],\n      {\n        dangerouslyForceHydrate,\n      },\n    )\n    const [countValue, setCount] = useAtom(countAtom)\n    const [statusValue, setStatus] = useAtom(statusAtom)\n\n    return (\n      <>\n        <div>count: {countValue}</div>\n        <button onClick={() => setCount((count) => count + 1)}>dispatch</button>\n        <div>status: {statusValue}</div>\n        <button\n          onClick={() =>\n            setStatus((status) =>\n              status === 'fulfilled' ? 'rejected' : 'fulfilled',\n            )\n          }\n        >\n          update\n        </button>\n      </>\n    )\n  }\n\n  const { rerender } = render(\n    <StrictMode>\n      <Counter initialCount={42} initialStatus=\"rejected\" />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 42')).toBeInTheDocument()\n  expect(screen.getByText('status: rejected')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  fireEvent.click(screen.getByText('update'))\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('status: fulfilled')).toBeInTheDocument()\n\n  rerender(\n    <StrictMode>\n      <Counter initialCount={65} initialStatus=\"rejected\" />\n    </StrictMode>,\n  )\n  expect(screen.getByText('count: 43')).toBeInTheDocument()\n  expect(screen.getByText('status: fulfilled')).toBeInTheDocument()\n\n  rerender(\n    <StrictMode>\n      <Counter\n        initialCount={11}\n        initialStatus=\"rejected\"\n        dangerouslyForceHydrate={true}\n      />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n  expect(screen.getByText('status: rejected')).toBeInTheDocument()\n})\n\n// types-only tests\n// eslint-disable-next-line vitest/expect-expect\nit('types: useHydrateAtoms should enforce tuple/value/args types', () => {\n  const numberAtom = {} as PrimitiveAtom<number>\n  const booleanAtom = {} as PrimitiveAtom<boolean>\n  const stringUnionAtom = {} as PrimitiveAtom<'pending' | 'fulfilled'>\n  const readOnlyAtom = {} as Atom<number>\n  const writeOnlySingleNumberAtom = {} as WritableAtom<number, [number], void>\n  const writeOnlyDoubleNumberAtom = {} as WritableAtom<\n    number,\n    [number, number],\n    void\n  >\n\n  // positive cases (should type-check)\n  /* eslint-disable @typescript-eslint/no-unused-expressions */\n  ;() =>\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms([\n      [numberAtom, 1],\n      [booleanAtom, true],\n      [stringUnionAtom, 'fulfilled'],\n    ] as const)\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[writeOnlySingleNumberAtom, 2]])\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[writeOnlyDoubleNumberAtom, 1, 2]])\n  ;() =>\n    // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n    useHydrateAtoms(\n      new Map<\n        typeof numberAtom | typeof stringUnionAtom,\n        number | 'pending' | 'fulfilled'\n      >([\n        [numberAtom, 123],\n        [stringUnionAtom, 'pending'],\n      ]),\n    )\n  type AnyWritableAtom = WritableAtom<unknown, unknown[], unknown>\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([] as (readonly [AnyWritableAtom, unknown])[])\n\n  // negative cases (should fail type-check)\n  // @ts-expect-error wrong value type for primitive atom [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[numberAtom, 'oops']])\n  // @ts-expect-error wrong value type for boolean atom [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[booleanAtom, 0]])\n  // @ts-expect-error read-only atom is not writable [SKIP-TS-4.2.3] [SKIP-TS-4.1.5] [SKIP-TS-4.0.5] [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[readOnlyAtom, 1]])\n  // @ts-expect-error wrong arg type for writable derived atom [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[writeOnlySingleNumberAtom, 'x']])\n  // @ts-expect-error missing one arg for writable derived with two args [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[writeOnlyDoubleNumberAtom, 1]])\n  // @ts-expect-error too many args for writable derived with two args [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms([[writeOnlyDoubleNumberAtom, 1, 2, 3]])\n  // @ts-expect-error map with read-only atom key [SKIP-TS-4.2.3] [SKIP-TS-4.1.5] [SKIP-TS-4.0.5] [SKIP-TS-3.9.7]\n  // [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  ;() => useHydrateAtoms(new Map([[readOnlyAtom, 1]]))\n  /* eslint-enable @typescript-eslint/no-unused-expressions */\n})\n"
  },
  {
    "path": "tests/react/utils/useReducerAtom.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useReducerAtom } from 'jotai/react/utils'\nimport { atom } from 'jotai/vanilla'\n\nlet savedConsoleWarn: any\n\nbeforeEach(() => {\n  savedConsoleWarn = console.warn\n  console.warn = vi.fn()\n})\n\nafterEach(() => {\n  console.warn = savedConsoleWarn\n})\n\nit('useReducerAtom with no action argument', () => {\n  const countAtom = atom(0)\n  const reducer = (state: number) => state + 2\n\n  const Parent = () => {\n    const [count, dispatch] = useReducerAtom(countAtom, reducer)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => dispatch()}>dispatch</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch'))\n  expect(screen.getByText('count: 4')).toBeInTheDocument()\n})\n\nit('useReducerAtom with optional action argument', () => {\n  const countAtom = atom(0)\n  const reducer = (state: number, action?: 'INCREASE' | 'DECREASE') => {\n    switch (action) {\n      case 'INCREASE':\n        return state + 1\n      case 'DECREASE':\n        return state - 1\n      case undefined:\n        return state\n    }\n  }\n\n  const Parent = () => {\n    const [count, dispatch] = useReducerAtom(countAtom, reducer)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => dispatch('INCREASE')}>dispatch INCREASE</button>\n        <button onClick={() => dispatch('DECREASE')}>dispatch DECREASE</button>\n        <button onClick={() => dispatch()}>dispatch empty</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch INCREASE'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch empty'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch DECREASE'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n\nit('useReducerAtom with non-optional action argument', () => {\n  const countAtom = atom(0)\n  const reducer = (state: number, action: 'INCREASE' | 'DECREASE') => {\n    switch (action) {\n      case 'INCREASE':\n        return state + 1\n      case 'DECREASE':\n        return state - 1\n    }\n  }\n\n  const Parent = () => {\n    const [count, dispatch] = useReducerAtom(countAtom, reducer)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => dispatch('INCREASE')}>dispatch INCREASE</button>\n        <button onClick={() => dispatch('DECREASE')}>dispatch DECREASE</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch INCREASE'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch DECREASE'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/utils/useResetAtom.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { useResetAtom } from 'jotai/react/utils'\nimport { atom } from 'jotai/vanilla'\nimport { RESET, atomWithReducer, atomWithReset } from 'jotai/vanilla/utils'\n\nit('atomWithReset resets to its first value', () => {\n  const countAtom = atomWithReset(0)\n\n  const Parent = () => {\n    const [count, setValue] = useAtom(countAtom)\n    const resetAtom = useResetAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={resetAtom}>reset</button>\n        <button onClick={() => setValue((oldValue) => oldValue + 1)}>\n          increment\n        </button>\n        <button onClick={() => setValue(10)}>set to 10</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('reset'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('set to 10'))\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 12')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 13')).toBeInTheDocument()\n})\n\nit('atomWithReset reset based on previous value', () => {\n  const countAtom = atomWithReset(0)\n\n  const Parent = () => {\n    const [count, setValue] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button\n          onClick={() =>\n            setValue((oldValue) =>\n              oldValue === 3 ? (RESET as never) : oldValue + 1,\n            )\n          }\n        >\n          increment till 3, then reset\n        </button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment till 3, then reset'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment till 3, then reset'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment till 3, then reset'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment till 3, then reset'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n\nit('atomWithReset through read-write atom', () => {\n  const primitiveAtom = atomWithReset(0)\n  const countAtom = atom(\n    (get) => get(primitiveAtom),\n    (_get, set, newValue: number | typeof RESET) =>\n      set(primitiveAtom, newValue as never),\n  )\n\n  const Parent = () => {\n    const [count, setValue] = useAtom(countAtom)\n    const resetAtom = useResetAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={resetAtom}>reset</button>\n        <button onClick={() => setValue(10)}>set to 10</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('set to 10'))\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('reset'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n\nit('useResetAtom with custom atom', () => {\n  const reducer = (state: number, action: 'INCREASE' | typeof RESET) => {\n    switch (action) {\n      case 'INCREASE':\n        return state + 1\n      case RESET:\n        return 0\n      default:\n        throw new Error('unknown action')\n    }\n  }\n\n  const countAtom = atomWithReducer(0, reducer)\n\n  const Parent = () => {\n    const [count, dispatch] = useAtom(countAtom)\n    const resetAtom = useResetAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={resetAtom}>reset</button>\n        <button onClick={() => dispatch('INCREASE')}>increment</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('reset'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/atomFamily.test.tsx",
    "content": "import { StrictMode, Suspense, useState } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport type { SetStateAction, WritableAtom } from 'jotai/vanilla'\nimport { atomFamily } from 'jotai/vanilla/utils'\nimport { sleep } from '../../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('new atomFamily impl', () => {\n  const myFamily = atomFamily((param: string) => atom(param))\n\n  const Displayer = ({ index }: { index: string }) => {\n    const [count] = useAtom(myFamily(index))\n    return <div>count: {count}</div>\n  }\n\n  render(\n    <StrictMode>\n      <Displayer index=\"a\" />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: a')).toBeInTheDocument()\n})\n\nit('primitive atomFamily returns same reference for same parameters', () => {\n  const myFamily = atomFamily((num: number) => atom({ num }))\n\n  expect(myFamily(0)).toEqual(myFamily(0))\n  expect(myFamily(0)).not.toEqual(myFamily(1))\n  expect(myFamily(1)).not.toEqual(myFamily(0))\n})\n\nit('read-only derived atomFamily returns same reference for same parameters', () => {\n  const arrayAtom = atom([0])\n  const myFamily = atomFamily((num: number) =>\n    atom((get) => get(arrayAtom)[num] as number),\n  )\n\n  expect(myFamily(0)).toEqual(myFamily(0))\n  expect(myFamily(0)).not.toEqual(myFamily(1))\n  expect(myFamily(1)).not.toEqual(myFamily(0))\n})\n\nit('removed atom creates a new reference', () => {\n  const bigAtom = atom([0])\n  const myFamily = atomFamily((num: number) =>\n    atom((get) => get(bigAtom)[num] as number),\n  )\n\n  const savedReference = myFamily(0)\n\n  expect(savedReference).toEqual(myFamily(0))\n\n  myFamily.remove(0)\n\n  const newReference = myFamily(0)\n\n  expect(savedReference).not.toEqual(newReference)\n\n  myFamily.remove(1337)\n\n  expect(myFamily(0)).toEqual(newReference)\n})\n\nit('primitive atomFamily initialized with props', () => {\n  const myFamily = atomFamily((param: number) => atom(param))\n\n  const Displayer = ({ index }: { index: number }) => {\n    const [count, setCount] = useAtom(myFamily(index))\n    return (\n      <div>\n        count: {count}\n        <button onClick={() => setCount((c) => c + 10)}>button</button>\n      </div>\n    )\n  }\n\n  const Parent = () => {\n    const [index, setIndex] = useState(1)\n\n    return (\n      <div>\n        <button onClick={() => setIndex((i) => i + 1)}>increment</button>\n        <Displayer index={index} />\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 11')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 12')).toBeInTheDocument()\n})\n\nit('derived atomFamily functionality as usual', () => {\n  const arrayAtom = atom([0, 0, 0])\n\n  const myFamily = atomFamily((param: number) =>\n    atom(\n      (get) => get(arrayAtom)[param] as number,\n      (_, set, update) => {\n        set(arrayAtom, (oldArray) => {\n          if (typeof oldArray[param] === 'undefined') return oldArray\n\n          const newValue =\n            typeof update === 'function'\n              ? update(oldArray[param] as number)\n              : update\n\n          const newArray = [\n            ...oldArray.slice(0, param),\n            newValue,\n            ...oldArray.slice(param + 1),\n          ]\n\n          return newArray\n        })\n      },\n    ),\n  )\n\n  const Displayer = ({\n    index,\n    countAtom,\n  }: {\n    index: number\n    countAtom: WritableAtom<number, [SetStateAction<number>], void>\n  }) => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <div>\n        index: {index}, count: {count}\n        <button onClick={() => setCount((oldValue) => oldValue + 1)}>\n          increment #{index}\n        </button>\n      </div>\n    )\n  }\n\n  const indicesAtom = atom((get) => [...new Array(get(arrayAtom).length)])\n\n  const Parent = () => {\n    const [indices] = useAtom(indicesAtom)\n\n    return (\n      <div>\n        {indices.map((_, index) => (\n          <Displayer key={index} index={index} countAtom={myFamily(index)} />\n        ))}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('index: 0, count: 0')).toBeInTheDocument()\n  expect(screen.getByText('index: 1, count: 0')).toBeInTheDocument()\n  expect(screen.getByText('index: 2, count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment #1'))\n  expect(screen.getByText('index: 0, count: 0')).toBeInTheDocument()\n  expect(screen.getByText('index: 1, count: 1')).toBeInTheDocument()\n  expect(screen.getByText('index: 2, count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment #0'))\n  expect(screen.getByText('index: 0, count: 1')).toBeInTheDocument()\n  expect(screen.getByText('index: 1, count: 1')).toBeInTheDocument()\n  expect(screen.getByText('index: 2, count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment #2'))\n  expect(screen.getByText('index: 0, count: 1')).toBeInTheDocument()\n  expect(screen.getByText('index: 1, count: 1')).toBeInTheDocument()\n  expect(screen.getByText('index: 2, count: 1')).toBeInTheDocument()\n})\n\nit('custom equality function work', () => {\n  const bigAtom = atom([0])\n\n  const badFamily = atomFamily((num: { index: number }) =>\n    atom((get) => get(bigAtom)[num.index] as number),\n  )\n\n  const goodFamily = atomFamily(\n    (num: { index: number }) =>\n      atom((get) => get(bigAtom)[num.index] as number),\n    (l, r) => l.index === r.index,\n  )\n\n  expect(badFamily({ index: 0 })).not.toEqual(badFamily({ index: 0 }))\n  expect(badFamily({ index: 0 })).not.toEqual(badFamily({ index: 0 }))\n\n  expect(goodFamily({ index: 0 })).toEqual(goodFamily({ index: 0 }))\n  expect(goodFamily({ index: 0 })).not.toEqual(goodFamily({ index: 1 }))\n})\n\nit('a derived atom from an async atomFamily (#351)', async () => {\n  const countAtom = atom(1)\n  const getAsyncAtom = atomFamily((n: number) =>\n    atom(async () => {\n      await sleep(100)\n      return n + 10\n    }),\n  )\n  const derivedAtom = atom((get) => get(getAsyncAtom(get(countAtom))))\n\n  const Counter = () => {\n    const setCount = useSetAtom(countAtom)\n    const [derived] = useAtom(derivedAtom)\n    return (\n      <>\n        <div>derived: {derived}</div>\n        <button onClick={() => setCount((c) => c + 1)}>button</button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('derived: 11')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('derived: 12')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('derived: 13')).toBeInTheDocument()\n})\n\nit('setShouldRemove with custom equality function', () => {\n  const myFamily = atomFamily(\n    (num: { index: number }) => atom(num),\n    (l, r) => l.index === r.index,\n  )\n  let firstTime = true\n  myFamily.setShouldRemove(() => {\n    if (firstTime) {\n      firstTime = false\n      return true\n    }\n    return false\n  })\n\n  const family1 = myFamily({ index: 0 })\n  const family2 = myFamily({ index: 0 })\n  const family3 = myFamily({ index: 0 })\n\n  expect(family1).not.toBe(family2)\n  expect(family2).toBe(family3)\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/atomWithDefault.test.tsx",
    "content": "import { StrictMode, Suspense } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { RESET, atomWithDefault } from 'jotai/vanilla/utils'\nimport { sleep } from '../../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('simple sync get default', () => {\n  const count1Atom = atom(1)\n  const count2Atom = atomWithDefault((get) => get(count1Atom) * 2)\n\n  const Counter = () => {\n    const [count1, setCount1] = useAtom(count1Atom)\n    const [count2, setCount2] = useAtom(count2Atom)\n    return (\n      <>\n        <div>\n          count1: {count1}, count2: {count2}\n        </div>\n        <button onClick={() => setCount1((c) => c + 1)}>button1</button>\n        <button onClick={() => setCount2((c) => c + 1)}>button2</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count1: 1, count2: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button1'))\n  expect(screen.getByText('count1: 2, count2: 4')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button2'))\n  expect(screen.getByText('count1: 2, count2: 5')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button1'))\n  expect(screen.getByText('count1: 3, count2: 5')).toBeInTheDocument()\n})\n\nit('simple async get default', async () => {\n  const count1Atom = atom(1)\n  const count2Atom = atomWithDefault(async (get) => {\n    await sleep(100)\n    return get(count1Atom) * 2\n  })\n\n  const Counter = () => {\n    const [count1, setCount1] = useAtom(count1Atom)\n    const [count2, setCount2] = useAtom(count2Atom)\n    return (\n      <>\n        <div>\n          count1: {count1}, count2: {count2}\n        </div>\n        <button onClick={() => setCount1((c) => c + 1)}>button1</button>\n        <button onClick={() => setCount2((p) => p.then((c) => c + 1))}>\n          button2\n        </button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('count1: 1, count2: 2')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button1')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 2, count2: 4')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button2')))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 2, count2: 5')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button1')))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 3, count2: 5')).toBeInTheDocument()\n})\n\nit('refresh sync atoms to default values', () => {\n  const count1Atom = atom(1)\n  const count2Atom = atomWithDefault((get) => get(count1Atom) * 2)\n\n  const Counter = () => {\n    const [count1, setCount1] = useAtom(count1Atom)\n    const [count2, setCount2] = useAtom(count2Atom)\n    return (\n      <>\n        <div>\n          count1: {count1}, count2: {count2}\n        </div>\n        <button onClick={() => setCount1((c) => c + 1)}>button1</button>\n        <button onClick={() => setCount2((c) => c + 1)}>button2</button>\n        <button onClick={() => setCount2(RESET)}>Refresh count2</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count1: 1, count2: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button1'))\n  expect(screen.getByText('count1: 2, count2: 4')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button2'))\n  expect(screen.getByText('count1: 2, count2: 5')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button1'))\n  expect(screen.getByText('count1: 3, count2: 5')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Refresh count2'))\n  expect(screen.getByText('count1: 3, count2: 6')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button1'))\n  expect(screen.getByText('count1: 4, count2: 8')).toBeInTheDocument()\n})\n\nit('refresh async atoms to default values', async () => {\n  const count1Atom = atom(1)\n  const count2Atom = atomWithDefault(async (get) => {\n    await new Promise<void>((resolve) => setTimeout(resolve, 100))\n    return get(count1Atom) * 2\n  })\n\n  const Counter = () => {\n    const [count1, setCount1] = useAtom(count1Atom)\n    const [count2, setCount2] = useAtom(count2Atom)\n    return (\n      <>\n        <div>\n          count1: {count1}, count2: {count2}\n        </div>\n        <button onClick={() => setCount1((c) => c + 1)}>button1</button>\n        <button onClick={() => setCount2((p) => p.then((c) => c + 1))}>\n          button2\n        </button>\n        <button onClick={() => setCount2(RESET)}>Refresh count2</button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('count1: 1, count2: 2')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button1')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 2, count2: 4')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button2')))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 2, count2: 5')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button1')))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 3, count2: 5')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('Refresh count2')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 3, count2: 6')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button1')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count1: 4, count2: 8')).toBeInTheDocument()\n})\n\nit('can be set synchronously by passing value', () => {\n  const countAtom = atomWithDefault(() => 1)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount(10)}>Set to 10</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.getByRole('button', { name: 'Set to 10' }))\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/atomWithObservable.test.tsx",
    "content": "import { Component, StrictMode, Suspense, useState } from 'react'\nimport type { ReactElement, ReactNode } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport {\n  BehaviorSubject,\n  Observable,\n  Subject,\n  delay,\n  interval,\n  map,\n  of,\n  switchMap,\n  take,\n} from 'rxjs'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { fromValue, makeSubject, pipe, toObservable } from 'wonka'\nimport { useAtom, useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom, createStore } from 'jotai/vanilla'\nimport { atomWithObservable } from 'jotai/vanilla/utils'\n\nconst consoleError = console.error\nbeforeEach(() => {\n  vi.useFakeTimers()\n  // A workaround for missing performance.mark after using fake timers\n  // https://github.com/pmndrs/jotai/pull/1913#discussion_r1186527192\n  if (!performance.mark) {\n    performance.mark = (() => {}) as any\n    performance.clearMarks = (() => {}) as any\n    performance.clearMeasures = (() => {}) as any\n  }\n  // suppress error log\n  console.error = vi.fn((...args: unknown[]) => {\n    const message = String(args)\n    if (\n      message.includes('at ErrorBoundary') ||\n      message.includes('Test Error')\n    ) {\n      return\n    }\n    return consoleError(...args)\n  })\n})\n\nafterEach(() => {\n  vi.runAllTimers()\n  vi.useRealTimers()\n  console.error = consoleError\n})\n\nclass ErrorBoundary extends Component<\n  { children: ReactNode },\n  { error: string }\n> {\n  state = {\n    error: '',\n  }\n\n  static getDerivedStateFromError(error: Error) {\n    return { error: error.message }\n  }\n\n  render() {\n    if (this.state.error) {\n      return <div>Error: {this.state.error}</div>\n    }\n    return this.props.children\n  }\n}\n\nit('count state', () => {\n  const observableAtom = atomWithObservable(() => of(1))\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Suspense fallback=\"loading\">\n        <Counter />\n      </Suspense>\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('count state with Symbol.observable', () => {\n  const subject = new BehaviorSubject(1)\n  const observable = {\n    [Symbol.observable]: () => ({\n      subscribe: subject.subscribe.bind(subject),\n    }),\n  }\n  const observableAtom = atomWithObservable(() => observable)\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('writable count state', () => {\n  const subject = new BehaviorSubject(1)\n  const observableAtom = atomWithObservable(() => subject)\n\n  const Counter = () => {\n    const [state, dispatch] = useAtom(observableAtom)\n    return (\n      <>\n        count: {state}\n        <button onClick={() => dispatch(9)}>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: 1')).toBeInTheDocument()\n\n  act(() => subject.next(2))\n  act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 9')).toBeInTheDocument()\n  expect(subject.value).toBe(9)\n})\n\nit('writable count state without initial value', async () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => subject)\n\n  const CounterValue = () => {\n    const state = useAtomValue(observableAtom)\n    return <>count: {state}</>\n  }\n\n  const CounterButton = () => {\n    const dispatch = useSetAtom(observableAtom)\n    return <button onClick={() => dispatch(9)}>button</button>\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <CounterValue />\n        </Suspense>\n        <CounterButton />\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 9')).toBeInTheDocument()\n\n  act(() => subject.next(3))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n\nit('writable count state with delayed value', async () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => {\n    const observable = of(1).pipe(delay(10 * 1000))\n    observable.subscribe((n) => subject.next(n))\n    return subject\n  })\n\n  const Counter = () => {\n    const [state, dispatch] = useAtom(observableAtom)\n    return (\n      <>\n        count: {state}\n        <button\n          onClick={() => {\n            dispatch(9)\n          }}\n        >\n          button\n        </button>\n      </>\n    )\n  }\n\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(10000))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 9')).toBeInTheDocument()\n})\n\nit('only subscribe once per atom', async () => {\n  const subject = new Subject<number>()\n  let totalSubscriptions = 0\n  const observable = new Observable<number>((subscriber) => {\n    totalSubscriptions++\n    subject.subscribe(subscriber)\n  })\n  const observableAtom = atomWithObservable(() => observable)\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  let rerender: (ui: ReactNode) => void\n  await act(\n    () =>\n      ({ rerender } = render(\n        <>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </>,\n      )),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  act(() => subject.next(1))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  rerender!(<div />)\n  expect(totalSubscriptions).toEqual(1)\n\n  rerender!(\n    <>\n      <Suspense fallback=\"loading\">\n        <Counter />\n      </Suspense>\n    </>,\n  )\n\n  act(() => subject.next(2))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  expect(totalSubscriptions).toEqual(2)\n})\n\nit('cleanup subscription', async () => {\n  const subject = new Subject<number>()\n  let activeSubscriptions = 0\n  const observable = new Observable<number>((subscriber) => {\n    activeSubscriptions++\n    subject.subscribe(subscriber)\n    return () => {\n      activeSubscriptions--\n    }\n  })\n  const observableAtom = atomWithObservable(() => observable)\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  let rerender: (ui: ReactNode) => void\n  await act(\n    () =>\n      ({ rerender } = render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </StrictMode>,\n      )),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  subject.next(1)\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  expect(activeSubscriptions).toEqual(1)\n  rerender!(<div />)\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(activeSubscriptions).toEqual(0)\n})\n\nit('resubscribe on remount', async () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => subject)\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  const Toggle = ({ children }: { children: ReactElement }) => {\n    const [visible, setVisible] = useState(true)\n    return (\n      <>\n        {visible && children}\n        <button onClick={() => setVisible(!visible)}>Toggle</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Toggle>\n            <Counter />\n          </Toggle>\n        </Suspense>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  act(() => subject.next(1))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('Toggle'))\n  fireEvent.click(screen.getByText('Toggle'))\n  act(() => subject.next(2))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n})\n\nit(\"count state with initialValue doesn't suspend\", () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => subject, { initialValue: 5 })\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 5')).toBeInTheDocument()\n\n  act(() => subject.next(10))\n  act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n})\n\nit('writable count state with initialValue', () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => subject, { initialValue: 5 })\n\n  const Counter = () => {\n    const [state, dispatch] = useAtom(observableAtom)\n    return (\n      <>\n        count: {state}\n        <button onClick={() => dispatch(9)}>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: 5')).toBeInTheDocument()\n  act(() => subject.next(1))\n  act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  act(() => vi.advanceTimersByTime(0))\n  expect(screen.getByText('count: 9')).toBeInTheDocument()\n})\n\nit('writable count state with error', async () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => subject)\n\n  const Counter = () => {\n    const [state, dispatch] = useAtom(observableAtom)\n    return (\n      <>\n        count: {state}\n        <button onClick={() => dispatch(9)}>button</button>\n      </>\n    )\n  }\n\n  await act(() =>\n    render(\n      <StrictMode>\n        <ErrorBoundary>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </ErrorBoundary>\n      </StrictMode>,\n    ),\n  )\n\n  expect(screen.getByText('loading')).toBeInTheDocument()\n\n  subject.error(new Error('Test Error'))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('Error: Test Error')).toBeInTheDocument()\n})\n\nit('synchronous subscription with initial value', () => {\n  const observableAtom = atomWithObservable(() => of(1), { initialValue: 5 })\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('synchronous subscription with BehaviorSubject', () => {\n  const observableAtom = atomWithObservable(() => new BehaviorSubject(1))\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('synchronous subscription with already emitted value', () => {\n  const observableAtom = atomWithObservable(() => of(1))\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit('with falsy initial value', () => {\n  const observableAtom = atomWithObservable(() => new Subject<number>(), {\n    initialValue: 0,\n  })\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state}</>\n  }\n\n  render(\n    <StrictMode>\n      <Counter />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n\nit('with initially emitted undefined value', async () => {\n  const subject = new Subject<number | undefined | null>()\n  const observableAtom = atomWithObservable(() => subject)\n\n  const Counter = () => {\n    const [state] = useAtom(observableAtom)\n    return <>count: {state === undefined ? '-' : state}</>\n  }\n\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\n  act(() => subject.next(undefined))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: -')).toBeInTheDocument()\n\n  act(() => subject.next(1))\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n})\n\nit(\"don't omit values emitted between init and mount\", async () => {\n  const subject = new Subject<number>()\n  const observableAtom = atomWithObservable(() => subject)\n\n  const Counter = () => {\n    const [state, dispatch] = useAtom(observableAtom)\n    return (\n      <>\n        count: {state}\n        <button\n          onClick={() => {\n            dispatch(9)\n          }}\n        >\n          button\n        </button>\n      </>\n    )\n  }\n\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\n  subject.next(1)\n  subject.next(2)\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 9')).toBeInTheDocument()\n})\n\ndescribe('error handling', () => {\n  class ErrorBoundary extends Component<\n    { message?: string; retry?: () => void; children: ReactNode },\n    { hasError: boolean }\n  > {\n    constructor(props: { message?: string; children: ReactNode }) {\n      super(props)\n      this.state = { hasError: false }\n    }\n    static getDerivedStateFromError() {\n      return { hasError: true }\n    }\n    render() {\n      return this.state.hasError ? (\n        <div>\n          {this.props.message || 'errored'}\n          {this.props.retry && (\n            <button\n              onClick={() => {\n                this.props.retry?.()\n                this.setState({ hasError: false })\n              }}\n            >\n              retry\n            </button>\n          )}\n        </div>\n      ) : (\n        this.props.children\n      )\n    }\n  }\n\n  it('can catch error in error boundary', async () => {\n    const subject = new Subject<number>()\n    const countAtom = atomWithObservable(() => subject)\n\n    const Counter = () => {\n      const [count] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <ErrorBoundary>\n            <Suspense fallback=\"loading\">\n              <Counter />\n            </Suspense>\n          </ErrorBoundary>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    subject.error(new Error('Test Error'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('errored')).toBeInTheDocument()\n  })\n\n  it('can recover from error with dependency', async () => {\n    const baseAtom = atom(0)\n    const countAtom = atomWithObservable((get) => {\n      const base = get(baseAtom)\n      if (base % 2 === 0) {\n        const subject = new Subject<number>()\n        const observable = of(1).pipe(delay(10 * 1000))\n        observable.subscribe(() => subject.error(new Error('Test Error')))\n        return subject\n      }\n      const observable = of(base).pipe(delay(10 * 1000))\n      return observable\n    })\n\n    const Counter = () => {\n      const [count] = useAtom(countAtom)\n      const setBase = useSetAtom(baseAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setBase((v) => v + 1)}>next</button>\n        </>\n      )\n    }\n\n    const App = () => {\n      const setBase = useSetAtom(baseAtom)\n      const retry = () => {\n        setBase((c) => c + 1)\n      }\n      return (\n        <ErrorBoundary retry={retry}>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </ErrorBoundary>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <App />\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('errored')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('retry')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('next')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('errored')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('retry')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n  })\n\n  it('can recover with intermediate atom', async () => {\n    let count = -1\n    let willThrowError = false\n    const refreshAtom = atom(0)\n    const countObservableAtom = atom((get) => {\n      get(refreshAtom)\n      const observableAtom = atomWithObservable(() => {\n        willThrowError = !willThrowError\n        ++count\n        const subject = new Subject<{ data: number } | { error: Error }>()\n        setTimeout(() => {\n          if (willThrowError) {\n            subject.next({ error: new Error('Test Error') })\n          } else {\n            subject.next({ data: count })\n          }\n        }, 10 * 1000)\n        return subject\n      })\n      return observableAtom\n    })\n    const derivedAtom = atom((get) => {\n      const observableAtom = get(countObservableAtom)\n      const result = get(observableAtom)\n      if (result instanceof Promise) {\n        return result.then((result) => {\n          if ('error' in result) {\n            throw result.error\n          }\n          return result.data\n        })\n      }\n      if ('error' in result) {\n        throw result.error\n      }\n      return result.data\n    })\n\n    const Counter = () => {\n      const [count] = useAtom(derivedAtom)\n      const refresh = useSetAtom(refreshAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => refresh((c) => c + 1)}>refresh</button>\n        </>\n      )\n    }\n\n    const App = () => {\n      const refresh = useSetAtom(refreshAtom)\n      const retry = () => {\n        refresh((c) => c + 1)\n      }\n      return (\n        <ErrorBoundary retry={retry}>\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </ErrorBoundary>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <App />\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('errored')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('retry')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('refresh')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('errored')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('retry')))\n    expect(screen.getByText('loading')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(10000))\n    expect(screen.getByText('count: 3')).toBeInTheDocument()\n  })\n})\n\nit('should throw error when writing to non-subject observable', () => {\n  const observable = of(1)\n  const observableAtom = atomWithObservable(() => observable)\n\n  const Counter = () => {\n    const [state, dispatch] = useAtom(observableAtom as any)\n    return (\n      <>\n        count: {state}\n        <button\n          onClick={() => {\n            expect(() => dispatch(2)).toThrow('observable is not subject')\n          }}\n        >\n          button\n        </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})\n\ndescribe('wonka', () => {\n  it('count state', () => {\n    const source = fromValue(1)\n    const observable = pipe(source, toObservable)\n    const observableAtom = atomWithObservable(() => observable)\n\n    const Counter = () => {\n      const [count] = useAtom(observableAtom)\n      return <>count: {count}</>\n    }\n\n    render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <Counter />\n        </Suspense>\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n\n  it('make subject', async () => {\n    const subject = makeSubject<number>()\n    const observable = pipe(subject.source, toObservable)\n    const observableAtom = atomWithObservable(() => observable)\n    const countAtom = atom(\n      (get) => get(observableAtom),\n      (_get, _set, nextValue: number) => {\n        subject.next(nextValue)\n      },\n    )\n\n    const Counter = () => {\n      const [count] = useAtom(countAtom)\n      return <>count: {count}</>\n    }\n\n    const Controls = () => {\n      const setCount = useSetAtom(countAtom)\n      return <button onClick={() => setCount(1)}>button</button>\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Controls />\n          <Suspense fallback=\"loading\">\n            <Counter />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('loading')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n})\n\ndescribe('atomWithObservable vanilla tests', () => {\n  it('can propagate updates with async atom chains', async () => {\n    const store = createStore()\n\n    const subject = new BehaviorSubject(1)\n    const countAtom = atomWithObservable(() => subject)\n    const asyncAtom = atom(async (get) => get(countAtom))\n    const async2Atom = atom((get) => get(asyncAtom))\n\n    const unsub = store.sub(async2Atom, () => {})\n\n    await expect(store.get(async2Atom)).resolves.toBe(1)\n\n    subject.next(2)\n    await expect(store.get(async2Atom)).resolves.toBe(2)\n\n    subject.next(3)\n    await expect(store.get(async2Atom)).resolves.toBe(3)\n\n    unsub()\n  })\n\n  it('can propagate updates with rxjs chains', () => {\n    const store = createStore()\n\n    const single$ = new Subject<number>()\n    const double$ = single$.pipe(map((n) => n * 2))\n\n    const singleAtom = atomWithObservable(() => single$)\n    const doubleAtom = atomWithObservable(() => double$)\n\n    const unsubs = [\n      store.sub(singleAtom, () => {}),\n      store.sub(doubleAtom, () => {}),\n    ]\n\n    single$.next(1)\n    expect(store.get(singleAtom)).toBe(1)\n    expect(store.get(doubleAtom)).toBe(2)\n\n    single$.next(2)\n    expect(store.get(singleAtom)).toBe(2)\n    expect(store.get(doubleAtom)).toBe(4)\n\n    single$.next(3)\n    expect(store.get(singleAtom)).toBe(3)\n    expect(store.get(doubleAtom)).toBe(6)\n\n    unsubs.forEach((unsub) => unsub())\n  })\n\n  it('should throw error when writing to non-subject observable', () => {\n    const store = createStore()\n    const observable = of(1)\n    const observableAtom = atomWithObservable(() => observable)\n\n    store.sub(observableAtom, () => {})\n\n    expect(() => store.set(observableAtom as any, 2)).toThrow(\n      'observable is not subject',\n    )\n  })\n})\n\nit('should update continuous values in React 19', async () => {\n  const counterSubject = interval(100).pipe(\n    take(4),\n    switchMap(async (i) => i),\n  )\n\n  const counterAtom = atomWithObservable(() => counterSubject, {\n    unstable_timeout: 1000,\n  })\n\n  const countAtom = atom(async (get) => get(counterAtom))\n\n  const Counter = () => {\n    const count = useAtomValue(countAtom)\n    return <div>count: {count}</div>\n  }\n\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(400))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/atomWithReducer.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atomWithReducer } from 'jotai/vanilla/utils'\n\nit('atomWithReducer with optional action argument', () => {\n  const reducer = (state: number, action?: 'INCREASE' | 'DECREASE') => {\n    switch (action) {\n      case 'INCREASE':\n        return state + 1\n      case 'DECREASE':\n        return state - 1\n      case undefined:\n        return state\n    }\n  }\n  const countAtom = atomWithReducer(0, reducer)\n\n  const Parent = () => {\n    const [count, dispatch] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => dispatch('INCREASE')}>dispatch INCREASE</button>\n        <button onClick={() => dispatch('DECREASE')}>dispatch DECREASE</button>\n        <button onClick={() => dispatch()}>dispatch empty</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch INCREASE'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch empty'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch DECREASE'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n\nit('atomWithReducer with non-optional action argument', () => {\n  const reducer = (state: number, action: 'INCREASE' | 'DECREASE') => {\n    switch (action) {\n      case 'INCREASE':\n        return state + 1\n      case 'DECREASE':\n        return state - 1\n    }\n  }\n  const countAtom = atomWithReducer(0, reducer)\n\n  const Parent = () => {\n    const [count, dispatch] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => dispatch('INCREASE')}>dispatch INCREASE</button>\n        <button onClick={() => dispatch('DECREASE')}>dispatch DECREASE</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch INCREASE'))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('dispatch DECREASE'))\n  expect(screen.getByText('count: 0')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/atomWithRefresh.test.tsx",
    "content": "import { StrictMode, Suspense } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atomWithRefresh } from 'jotai/vanilla/utils'\nimport { sleep } from '../../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('sync counter', () => {\n  let counter = 0\n  const countAtom = atomWithRefresh(() => ++counter)\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount()}>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  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  expect(counter).toBe(3)\n})\n\nit('async counter', async () => {\n  let counter = 0\n  const countAtom = atomWithRefresh(async () => {\n    await sleep(100)\n    return ++counter\n  })\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount()}>button</button>\n      </>\n    )\n  }\n\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(100))\n  expect(screen.getByText('count: 1')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  await act(() => fireEvent.click(screen.getByText('button')))\n  expect(screen.getByText('loading')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  expect(counter).toBe(3)\n})\n\nit('writable counter', () => {\n  let counter = 0\n  const countAtom = atomWithRefresh(\n    () => ++counter,\n    (_get, _set, newValue: number) => {\n      counter = newValue\n    },\n  )\n\n  const Counter = () => {\n    const [count, setCount] = useAtom(countAtom)\n    return (\n      <>\n        <div>count: {count}</div>\n        <button onClick={() => setCount()}>button</button>\n        <button onClick={() => setCount(9)}>set9</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  expect(screen.getByText('count: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('set9'))\n  expect(screen.getByText('count: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('count: 10')).toBeInTheDocument()\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/atomWithStorage.test.tsx",
    "content": "import { StrictMode, Suspense } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport {\n  afterAll,\n  afterEach,\n  beforeAll,\n  beforeEach,\n  describe,\n  expect,\n  it,\n  vi,\n} from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom, createStore } from 'jotai/vanilla'\nimport {\n  RESET,\n  atomWithStorage,\n  createJSONStorage,\n  unstable_withStorageValidator as withStorageValidator,\n} from 'jotai/vanilla/utils'\nimport type { SyncStringStorage } from 'jotai/vanilla/utils/atomWithStorage'\nimport { sleep } from '../../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\ndescribe('atomWithStorage (sync)', () => {\n  const storageData: Record<string, number> = {\n    count: 10,\n  }\n  const dummyStorage = {\n    getItem: (key: string, initialValue: number) => {\n      if (!(key in storageData)) {\n        return initialValue\n      }\n      return storageData[key] as number\n    },\n    setItem: (key: string, newValue: number) => {\n      storageData[key] = newValue\n    },\n    removeItem: (key: string) => {\n      delete storageData[key]\n    },\n    listeners: new Set<(key: string, value: number | null) => void>(),\n    emitStorageEvent: (key: string, newValue: number | null) => {\n      dummyStorage.listeners.forEach((listener) => {\n        listener(key, newValue)\n      })\n    },\n    subscribe: (\n      key: string,\n      callback: (value: number) => void,\n      initialValue: number,\n    ) => {\n      const listener = (k: string, value: number | null) => {\n        if (k === key) {\n          callback(value ?? initialValue)\n        }\n      }\n      dummyStorage.listeners.add(listener)\n      return () => dummyStorage.listeners.delete(listener)\n    },\n  }\n\n  it('simple count', () => {\n    const countAtom = atomWithStorage('count', 1, dummyStorage)\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n          <button onClick={() => setCount(RESET)}>reset</button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('count: 11')).toBeInTheDocument()\n    expect(storageData.count).toBe(11)\n\n    fireEvent.click(screen.getByText('reset'))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(storageData.count).toBeUndefined()\n  })\n\n  it('storage updates before mount (#1079)', () => {\n    dummyStorage.setItem('count', 10)\n    const countAtom = atomWithStorage('count', 1, dummyStorage)\n\n    const Counter = () => {\n      const [count] = useAtom(countAtom)\n      // emulating updating before mount\n      if (dummyStorage.getItem('count', 1) !== 9) {\n        dummyStorage.emitStorageEvent('count', 9)\n      }\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 9')).toBeInTheDocument()\n  })\n\n  it('should get stored value on init with getOnInit option', () => {\n    const store = createStore()\n    const countAtom = atomWithStorage('count', 0, dummyStorage, {\n      getOnInit: true,\n    })\n\n    expect(store.get(countAtom)).toBe(10)\n  })\n})\n\ndescribe('with sync string storage', () => {\n  const storageData: Record<string, string> = {\n    count: '10',\n  }\n  const stringStorage = {\n    getItem: (key: string) => {\n      return storageData[key] || null\n    },\n    setItem: (key: string, newValue: string) => {\n      storageData[key] = newValue\n    },\n    removeItem: (key: string) => {\n      delete storageData[key]\n    },\n    listeners: new Set<(key: string, value: string | null) => void>(),\n    emitStorageEvent: (key: string, newValue: string | null) => {\n      stringStorage.listeners.forEach((listener) => {\n        listener(key, newValue)\n      })\n    },\n  }\n  const dummyStorage = createJSONStorage<number>(() => stringStorage)\n  dummyStorage.subscribe = (key, callback, initialValue) => {\n    const listener = (k: string, value: string | null) => {\n      if (k === key) {\n        let newValue: number\n        try {\n          newValue = JSON.parse(value ?? '')\n        } catch {\n          newValue = initialValue\n        }\n        callback(newValue)\n      }\n    }\n    stringStorage.listeners.add(listener)\n    return () => stringStorage.listeners.delete(listener)\n  }\n\n  it('simple count', () => {\n    const countAtom = atomWithStorage('count', 1, dummyStorage)\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount((c) => c + 1)}>button</button>\n          <button onClick={() => setCount(RESET)}>reset</button>\n          <button onClick={() => setCount((c) => (c === 2 ? RESET : c + 1))}>\n            conditional reset\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('count: 11')).toBeInTheDocument()\n    expect(storageData.count).toBe('11')\n\n    fireEvent.click(screen.getByText('reset'))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(storageData.count).toBeUndefined()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n    expect(storageData.count).toBe('2')\n\n    fireEvent.click(screen.getByText('conditional reset'))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(storageData.count).toBeUndefined()\n  })\n\n  it('no entry (#1086)', () => {\n    const noentryAtom = atomWithStorage('noentry', -1, dummyStorage)\n\n    const Counter = () => {\n      const [noentry] = useAtom(noentryAtom)\n      return <div>noentry: {noentry}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('noentry: -1')).toBeInTheDocument()\n  })\n})\n\ndescribe('atomWithStorage (async)', () => {\n  const asyncStorageData: Record<string, number> = {\n    count: 10,\n  }\n  const asyncDummyStorage = {\n    getItem: async (key: string, initialValue: number) => {\n      await sleep(100)\n      if (!(key in asyncStorageData)) {\n        return initialValue\n      }\n      return asyncStorageData[key] as number\n    },\n    setItem: async (key: string, newValue: number) => {\n      await sleep(100)\n      asyncStorageData[key] = newValue\n    },\n    removeItem: async (key: string) => {\n      await sleep(100)\n      delete asyncStorageData[key]\n    },\n  }\n\n  it('async count', async () => {\n    const countAtom = atomWithStorage('count', 1, asyncDummyStorage)\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount(async (c) => (await c) + 1)}>\n            button\n          </button>\n          <button onClick={() => setCount(RESET)}>reset</button>\n        </>\n      )\n    }\n\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(100))\n    expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n    await act(() => fireEvent.click(screen.getByText('button')))\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 11')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(asyncStorageData.count).toBe(11)\n\n    await act(() => fireEvent.click(screen.getByText('reset')))\n    await act(() => vi.advanceTimersByTimeAsync(100))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    expect(asyncStorageData.count).toBeUndefined()\n  })\n\n  it('async new count', async () => {\n    const countAtom = atomWithStorage('count2', 20, asyncDummyStorage)\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(countAtom)\n\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount(async (c) => (await c) + 1)}>\n            button\n          </button>\n        </>\n      )\n    }\n\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(100))\n    expect(screen.getByText('count: 20')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('button'))\n    expect(screen.getByText('count: 20')).toBeInTheDocument()\n    await act(() => vi.advanceTimersByTimeAsync(300))\n    expect(asyncStorageData.count2).toBe(21)\n  })\n\n  it('createJSONStorage with async string storage', async () => {\n    const asyncStringStorage = {\n      getItem: async (key: string) => {\n        await sleep(100)\n        if (key === 'count') {\n          return '10'\n        }\n        return null\n      },\n      setItem: async () => {},\n      removeItem: async () => {},\n    }\n\n    const countAtom = atomWithStorage(\n      'count',\n      0,\n      createJSONStorage<number>(() => asyncStringStorage),\n    )\n\n    const Counter = () => {\n      const [count] = useAtom(countAtom)\n      return <div>count: {count}</div>\n    }\n\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(100))\n    expect(screen.getByText('count: 10')).toBeInTheDocument()\n  })\n})\n\ndescribe('atomWithStorage (without localStorage) (#949)', () => {\n  it('createJSONStorage without localStorage', () => {\n    const countAtom = atomWithStorage(\n      'count',\n      1,\n      createJSONStorage(() => undefined as any),\n    )\n\n    const Counter = () => {\n      const [count, setCount] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount(async (c) => (await c) + 1)}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n  })\n})\n\ndescribe('atomWithStorage (in non-browser environment)', () => {\n  const asyncStorageData: Record<string, string> = {\n    count: '10',\n  }\n  const asyncDummyStorage = {\n    getItem: async (key: string) => {\n      await sleep(100)\n      return asyncStorageData[key] as string\n    },\n    setItem: async (key: string, newValue: string) => {\n      await sleep(100)\n      asyncStorageData[key] = newValue\n    },\n    removeItem: async (key: string) => {\n      await sleep(100)\n      delete asyncStorageData[key]\n    },\n  }\n\n  const addEventListener = window.addEventListener\n  const localStorage = window.localStorage\n  const sessionStorage = window.sessionStorage\n  const consoleWarn = window.console.warn\n\n  beforeAll(() => {\n    ;(window as any).addEventListener = undefined\n    // patch console.warn to prevent logging along test results\n    Object.defineProperty(window.console, 'warn', {\n      value: () => {},\n    })\n    Object.defineProperties(window, {\n      localStorage: {\n        get() {\n          throw new Error('localStorage is not available.')\n        },\n      },\n      sessionStorage: {\n        get() {\n          throw new Error('sessionStorage is not available.')\n        },\n      },\n    })\n  })\n\n  afterAll(() => {\n    window.addEventListener = addEventListener\n    Object.defineProperty(window.console, 'warn', {\n      value: consoleWarn,\n    })\n    Object.defineProperties(window, {\n      localStorage: {\n        get() {\n          return localStorage\n        },\n      },\n      sessionStorage: {\n        get() {\n          return sessionStorage\n        },\n      },\n    })\n  })\n\n  it('createJSONStorage with undefined window.addEventListener', () => {\n    const storage = createJSONStorage(() => asyncDummyStorage)\n    expect(storage.subscribe).toBeUndefined()\n  })\n\n  it('createJSONStorage with localStorage', () => {\n    expect(() => createJSONStorage()).not.toThrow()\n    expect(() => createJSONStorage(() => window.localStorage)).not.toThrow()\n  })\n\n  it('createJSONStorage with sessionStorage', () => {\n    expect(() => createJSONStorage(() => window.sessionStorage)).not.toThrow()\n  })\n})\n\ndescribe('atomWithStorage (with browser storage)', () => {\n  const addEventListener = window.addEventListener\n  const mockAddEventListener = vi.fn()\n\n  beforeAll(() => {\n    ;(window as any).addEventListener = mockAddEventListener\n  })\n\n  afterAll(() => {\n    window.addEventListener = addEventListener\n  })\n\n  it('createJSONStorage subscribes to specific window storage events', () => {\n    const store = createStore()\n    const mockNativeStorage = Object.create(window.Storage.prototype)\n    mockNativeStorage.setItem = vi.fn() as Storage['setItem']\n    mockNativeStorage.getItem = vi.fn(() => null) as Storage['getItem']\n    mockNativeStorage.removeItem = vi.fn() as Storage['removeItem']\n\n    const dummyAtom = atomWithStorage<number>(\n      'dummy',\n      1,\n      createJSONStorage<number>(() => mockNativeStorage),\n    )\n\n    const DummyComponent = () => {\n      const [value] = useAtom(dummyAtom, { store })\n      return (\n        <>\n          <div>{value}</div>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <DummyComponent />\n      </StrictMode>,\n    )\n\n    expect(mockAddEventListener).toHaveBeenCalledWith(\n      'storage',\n      expect.any(Function),\n    )\n\n    const storageEventHandler = mockAddEventListener.mock.calls\n      .filter(([eventName]: any) => eventName === 'storage')\n      .pop()?.[1] as (e: StorageEvent) => void\n\n    expect(store.get(dummyAtom)).toBe(1)\n\n    act(() => {\n      storageEventHandler?.({\n        key: 'dummy',\n        newValue: '2',\n        storageArea: {},\n      } as StorageEvent)\n    })\n\n    expect(store.get(dummyAtom)).toBe(1)\n\n    act(() => {\n      storageEventHandler?.({\n        key: 'dummy',\n        newValue: '2',\n        storageArea: mockNativeStorage,\n      } as StorageEvent)\n    })\n\n    expect(store.get(dummyAtom)).toBe(2)\n\n    // simulate removeItem() from another window\n    act(() => {\n      storageEventHandler?.({\n        key: 'dummy',\n        newValue: null,\n        storageArea: mockNativeStorage,\n      } as StorageEvent)\n    })\n\n    expect(store.get(dummyAtom)).toBe(1)\n  })\n\n  it(\"should recompute dependents' state after onMount (#2098)\", () => {\n    const store = createStore()\n    let currentValue: string | null = 'true'\n    const mockNativeStorage = {\n      setItem: vi.fn((_key: string, value: string) => (currentValue = value)),\n      getItem: vi.fn(() => currentValue),\n      removeItem: vi.fn(() => (currentValue = null)),\n    }\n\n    const isLoggedAtom = atom(false)\n    const isDevModeStorageAtom = atomWithStorage(\n      'isDevModeStorageAtom',\n      false,\n      createJSONStorage<boolean>(() => mockNativeStorage),\n    )\n    const isDevModeState = atom(\n      (get) => {\n        if (!get(isLoggedAtom)) return false\n        return get(isDevModeStorageAtom)\n      },\n      (_get, set, value: boolean) => {\n        set(isDevModeStorageAtom, value)\n      },\n    )\n\n    const DummyComponent = () => {\n      const [isLogged] = useAtom(isLoggedAtom, { store })\n      const [value, setValue] = useAtom(isDevModeState, { store })\n      return isLogged ? (\n        <input\n          type=\"checkbox\"\n          checked={value}\n          onChange={() => setValue(!value)}\n        />\n      ) : null\n    }\n\n    render(\n      <StrictMode>\n        <DummyComponent />\n      </StrictMode>,\n    )\n\n    act(() => store.set(isLoggedAtom, true))\n\n    const checkbox = screen.getByRole('checkbox') as HTMLInputElement\n\n    expect(store.get(isLoggedAtom)).toBeTruthy()\n    expect(store.get(isDevModeStorageAtom)).toBeTruthy()\n\n    expect(checkbox).toBeChecked()\n    fireEvent.click(checkbox)\n    expect(checkbox).not.toBeChecked()\n  })\n})\n\ndescribe('atomWithStorage (with disabled browser storage)', () => {\n  const savedLocalStorage = window.localStorage\n\n  beforeAll(() => {\n    // Firefox and chromium based browser throw DOMException when cookies are disabled\n    Object.defineProperty(window, 'localStorage', {\n      get() {\n        throw new DOMException('The operation is insecure.')\n      },\n    })\n  })\n\n  afterAll(() => {\n    // TS < 4.5 causes type error without `as any`\n    ;(window as any).localStorage = savedLocalStorage\n  })\n\n  it('initial value of atomWithStorage can be used when cookies are disabled', () => {\n    const countAtom = atomWithStorage<number>('counter', 4)\n\n    const Counter = () => {\n      const [value] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {value}</div>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 4')).toBeInTheDocument()\n  })\n})\n\ndescribe('atomWithStorage (with non-browser storage)', () => {\n  const addEventListener = window.addEventListener\n  const mockAddEventListener = vi.fn()\n\n  beforeAll(() => {\n    ;(window as any).addEventListener = mockAddEventListener\n  })\n\n  afterAll(() => {\n    window.addEventListener = addEventListener\n  })\n\n  it('createJSONStorage avoids attaching event handler for non-browser storage', () => {\n    const store = createStore()\n    const mockNonNativeStorage = {\n      setItem: vi.fn() as Storage['setItem'],\n      getItem: vi.fn(() => null) as Storage['getItem'],\n      removeItem: vi.fn() as Storage['removeItem'],\n    }\n\n    const dummyAtom = atomWithStorage<number>(\n      'dummy',\n      1,\n      createJSONStorage<number>(() => mockNonNativeStorage),\n    )\n\n    const DummyComponent = () => {\n      const [value] = useAtom(dummyAtom, { store })\n      return (\n        <>\n          <div>{value}</div>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <DummyComponent />\n      </StrictMode>,\n    )\n\n    expect(mockAddEventListener).not.toHaveBeenCalledWith(\n      'storage',\n      expect.any(Function),\n    )\n  })\n})\n\ndescribe('withStorageValidator', () => {\n  const isNumber = (v: unknown): v is number => typeof v === 'number'\n\n  it('should use withStorageValidator with isNumber', () => {\n    const store = createStore()\n    const storage = createJSONStorage()\n    const numAtom = atomWithStorage(\n      'my-number',\n      0,\n      withStorageValidator(isNumber)(storage),\n    )\n\n    expect(store.get(numAtom)).toBe(0)\n  })\n\n  it('should return initialValue when validator fails', () => {\n    const store = createStore()\n    const stringStorage: SyncStringStorage = {\n      getItem: () => JSON.stringify('not-a-number'),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n    const storage = createJSONStorage(() => stringStorage)\n    const numAtom = atomWithStorage(\n      'my-number',\n      42,\n      withStorageValidator(isNumber)(storage),\n    )\n\n    expect(store.get(numAtom)).toBe(42)\n  })\n\n  it('should return initialValue when sync validator fails with render', () => {\n    const stringStorage: SyncStringStorage = {\n      getItem: () => JSON.stringify('not-a-number'),\n      setItem: () => {},\n      removeItem: () => {},\n    }\n    const storage = createJSONStorage(() => stringStorage)\n    const numAtom = atomWithStorage(\n      'my-number',\n      42,\n      withStorageValidator(isNumber)(storage),\n    )\n\n    const Counter = () => {\n      const [count] = useAtom(numAtom)\n      return <div>count: {count}</div>\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 42')).toBeInTheDocument()\n  })\n\n  it('should return stored value when async validator succeeds', async () => {\n    const asyncStringStorage = {\n      getItem: async () => {\n        await sleep(100)\n        return JSON.stringify(99)\n      },\n      setItem: async () => {},\n      removeItem: async () => {},\n    }\n    const storage = createJSONStorage(() => asyncStringStorage)\n    const numAtom = atomWithStorage(\n      'my-number',\n      42,\n      withStorageValidator(isNumber)(storage),\n    )\n\n    const Counter = () => {\n      const [count] = useAtom(numAtom)\n      return <div>count: {count}</div>\n    }\n\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(100))\n    expect(screen.getByText('count: 99')).toBeInTheDocument()\n  })\n\n  it('should return initialValue when async validator fails', async () => {\n    const asyncStringStorage = {\n      getItem: async () => {\n        await sleep(100)\n        return JSON.stringify('invalid')\n      },\n      setItem: async () => {},\n      removeItem: async () => {},\n    }\n    const storage = createJSONStorage(() => asyncStringStorage)\n    const numAtom = atomWithStorage(\n      'my-number',\n      42,\n      withStorageValidator(isNumber)(storage),\n    )\n\n    const Counter = () => {\n      const [count] = useAtom(numAtom)\n      return <div>count: {count}</div>\n    }\n\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(100))\n    expect(screen.getByText('count: 42')).toBeInTheDocument()\n  })\n})\n\ndescribe('with subscribe method in string storage', () => {\n  it('createJSONStorage subscriber is called correctly', () => {\n    const store = createStore()\n\n    const subscribe = vi.fn()\n    const stringStorage = {\n      getItem: () => {\n        return null\n      },\n      setItem: () => {},\n      removeItem: () => {},\n      subscribe,\n    }\n\n    const dummyStorage = createJSONStorage<number>(() => stringStorage)\n\n    const dummyAtom = atomWithStorage<number>('dummy', 1, dummyStorage)\n\n    const DummyComponent = () => {\n      const [value] = useAtom(dummyAtom, { store })\n      return (\n        <>\n          <div>{value}</div>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <DummyComponent />\n      </StrictMode>,\n    )\n\n    expect(subscribe).toHaveBeenCalledWith('dummy', expect.any(Function))\n  })\n\n  it('createJSONStorage subscriber responds to events correctly', () => {\n    const storageData: Record<string, string> = {\n      count: '10',\n    }\n\n    const stringStorage = {\n      getItem: (key: string) => {\n        return storageData[key] || null\n      },\n      setItem: (key: string, newValue: string) => {\n        storageData[key] = newValue\n      },\n      removeItem: (key: string) => {\n        delete storageData[key]\n      },\n      subscribe(_key, callback) {\n        function handler(event: CustomEvent<string>) {\n          callback(event.detail)\n        }\n\n        window.addEventListener('dummystoragechange', handler as EventListener)\n        return () =>\n          window.removeEventListener(\n            'dummystoragechange',\n            handler as EventListener,\n          )\n      },\n    } as SyncStringStorage\n\n    const dummyStorage = createJSONStorage<number>(() => stringStorage)\n\n    const countAtom = atomWithStorage('count', 1, dummyStorage)\n\n    const Counter = () => {\n      const [count] = useAtom(countAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Counter />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('count: 10')).toBeInTheDocument()\n\n    storageData.count = '12'\n    fireEvent(\n      window,\n      new CustomEvent('dummystoragechange', {\n        detail: '12',\n      }),\n    )\n    expect(screen.getByText('count: 12')).toBeInTheDocument()\n    // expect(storageData.count).toBe('11')\n  })\n})\n\ndescribe('with custom async storage', () => {\n  it('does not infinite loop (#2931)', async () => {\n    let storedValue = 0\n    let cachedPromise:\n      | [typeof storedValue, Promise<typeof storedValue>]\n      | null = null\n    const counterAtom = atomWithStorage('counter', 0, {\n      getItem(_key: string, _initialValue: number) {\n        if (cachedPromise && cachedPromise[0] === storedValue) {\n          return cachedPromise[1]\n        }\n        const promise = Promise.resolve(storedValue)\n        cachedPromise = [storedValue, promise]\n        return promise\n      },\n      async setItem(_key, newValue) {\n        storedValue = await new Promise((resolve) => resolve(newValue))\n      },\n      async removeItem() {},\n    })\n    const Component = () => {\n      const [count, setCount] = useAtom(counterAtom)\n      return (\n        <>\n          <div>count: {count}</div>\n          <button onClick={() => setCount(async (c) => (await c) + 1)}>\n            button\n          </button>\n        </>\n      )\n    }\n\n    await act(() =>\n      render(\n        <StrictMode>\n          <Suspense fallback=\"loading\">\n            <Component />\n          </Suspense>\n        </StrictMode>,\n      ),\n    )\n\n    expect(screen.getByText('count: 0')).toBeInTheDocument()\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 1')).toBeInTheDocument()\n    fireEvent.click(screen.getByText('button'))\n    await act(() => vi.advanceTimersByTimeAsync(0))\n    expect(screen.getByText('count: 2')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/freezeAtom.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { useAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { freezeAtom, freezeAtomCreator } from 'jotai/vanilla/utils'\n\nit('freezeAtom basic test', () => {\n  const objAtom = atom({ deep: { count: 0 } })\n\n  const Component = () => {\n    const [obj, setObj] = useAtom(freezeAtom(objAtom))\n    return (\n      <>\n        <button onClick={() => setObj({ deep: { count: 1 } })}>change</button>\n        <div>\n          count: {obj.deep.count}, isFrozen:{' '}\n          {`${Object.isFrozen(obj) && Object.isFrozen(obj.deep)}`}\n        </div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Component />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('count: 0, isFrozen: true')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('change'))\n  expect(screen.getByText('count: 1, isFrozen: true')).toBeInTheDocument()\n})\n\nit('freezeAtom handles null correctly', () => {\n  const nullAtom = atom(null)\n\n  const Component = () => {\n    const [value, setValue] = useAtom(freezeAtom(nullAtom))\n    return (\n      <>\n        <button onClick={() => setValue(null)}>set null</button>\n        <div>value is null: {`${value === null}`}</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Component />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('value is null: true')).toBeInTheDocument()\n})\n\nit('freezeAtom handles primitive correctly', () => {\n  const numberAtom = atom(123)\n\n  const Component = () => {\n    const [value, setValue] = useAtom(freezeAtom(numberAtom))\n    return (\n      <>\n        <button onClick={() => setValue(456)}>set number</button>\n        <div>value: {value}</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Component />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('value: 123')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('set number'))\n  expect(screen.getByText('value: 456')).toBeInTheDocument()\n})\n\ndescribe('freezeAtomCreator', () => {\n  let savedConsoleWarn: any\n  beforeEach(() => {\n    savedConsoleWarn = console.warn\n    console.warn = vi.fn()\n  })\n  afterEach(() => {\n    console.warn = savedConsoleWarn\n  })\n\n  it('freezeAtomCreator basic test', () => {\n    const createFrozenAtom = freezeAtomCreator(atom)\n    const objAtom = createFrozenAtom({ deep: {} }, (_get, set, _ignored?) => {\n      set(objAtom, { deep: {} })\n    })\n\n    const Component = () => {\n      const [obj, setObj] = useAtom(objAtom)\n      return (\n        <>\n          <button onClick={setObj}>change</button>\n          <div>\n            isFrozen: {`${Object.isFrozen(obj) && Object.isFrozen(obj.deep)}`}\n          </div>\n        </>\n      )\n    }\n\n    render(\n      <StrictMode>\n        <Component />\n      </StrictMode>,\n    )\n\n    expect(screen.getByText('isFrozen: true')).toBeInTheDocument()\n\n    fireEvent.click(screen.getByText('change'))\n    expect(screen.getByText('isFrozen: true')).toBeInTheDocument()\n  })\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/loadable.test.tsx",
    "content": "import { StrictMode, Suspense, useEffect } from 'react'\nimport { act, fireEvent, render, screen } from '@testing-library/react'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport type { Atom } from 'jotai/vanilla'\nimport { loadable } from 'jotai/vanilla/utils'\nimport { sleep } from '../../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('loadable turns suspense into values', async () => {\n  const asyncAtom = atom(async () => {\n    await sleep(100)\n    return 5\n  })\n\n  render(\n    <StrictMode>\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Data: 5')).toBeInTheDocument()\n})\n\nit('loadable turns errors into values', async () => {\n  const asyncAtom = atom(async () => {\n    await sleep(100)\n    throw new Error('An error occurred')\n  })\n\n  render(\n    <StrictMode>\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Error: An error occurred')).toBeInTheDocument()\n})\n\nit('loadable turns primitive throws into values', async () => {\n  const asyncAtom = atom(async () => {\n    await sleep(100)\n    throw 'An error occurred'\n  })\n\n  render(\n    <StrictMode>\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('An error occurred')).toBeInTheDocument()\n})\n\nit('loadable goes back to loading after re-fetch', async () => {\n  const refreshAtom = atom(0)\n  const asyncAtom = atom(async (get) => {\n    const count = get(refreshAtom)\n    await sleep(100)\n    return count === 0 ? 5 : 6\n  })\n\n  const Refresh = () => {\n    const setRefresh = useSetAtom(refreshAtom)\n    return (\n      <>\n        <button onClick={() => setRefresh((value) => value + 1)}>\n          refresh\n        </button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Refresh />\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Data: 5')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('refresh'))\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Data: 6')).toBeInTheDocument()\n})\n\nit('loadable can recover from error', async () => {\n  const refreshAtom = atom(0)\n  const asyncAtom = atom(async (get) => {\n    const count = get(refreshAtom)\n    await sleep(100)\n    if (count === 0) {\n      throw new Error('An error occurred')\n    }\n    return 6\n  })\n\n  const Refresh = () => {\n    const setRefresh = useSetAtom(refreshAtom)\n    return (\n      <>\n        <button onClick={() => setRefresh((value) => value + 1)}>\n          refresh\n        </button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Refresh />\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Error: An error occurred')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('refresh'))\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Data: 6')).toBeInTheDocument()\n})\n\nit('loadable immediately resolves sync values', () => {\n  const syncAtom = atom(5)\n  const effectCallback = vi.fn()\n\n  render(\n    <StrictMode>\n      <LoadableComponent effectCallback={effectCallback} asyncAtom={syncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Data: 5')).toBeInTheDocument()\n  expect(effectCallback.mock.calls).not.toContain(\n    expect.objectContaining({ state: 'loading' }),\n  )\n  expect(effectCallback).toHaveBeenLastCalledWith({ state: 'hasData', data: 5 })\n})\n\nit('loadable can use resolved promises synchronously', async () => {\n  const asyncAtom = atom(Promise.resolve(5))\n  const effectCallback = vi.fn()\n\n  const ResolveAtomComponent = () => {\n    useAtomValue(asyncAtom)\n\n    return <div>Ready</div>\n  }\n\n  let result: ReturnType<typeof render>\n  await act(async () => {\n    result = render(\n      <StrictMode>\n        <Suspense fallback=\"loading\">\n          <ResolveAtomComponent />\n        </Suspense>\n      </StrictMode>,\n    )\n  })\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  // FIXME React 18/19 Suspense behavior is non-deterministic\n  expect(\n    screen.queryByText('loading') ?? screen.queryByText('Ready'),\n  ).toBeInTheDocument()\n\n  result!.rerender(\n    <StrictMode>\n      <LoadableComponent\n        effectCallback={effectCallback}\n        asyncAtom={asyncAtom}\n      />\n    </StrictMode>,\n  )\n\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('Data: 5')).toBeInTheDocument()\n\n  expect(effectCallback.mock.calls).not.toContain(\n    expect.objectContaining({ state: 'loading' }),\n  )\n  expect(effectCallback).toHaveBeenLastCalledWith({ state: 'hasData', data: 5 })\n})\n\nit('loadable of a derived async atom does not trigger infinite loop (#1114)', async () => {\n  const baseAtom = atom(0)\n  const asyncAtom = atom(async (get) => {\n    get(baseAtom)\n    await sleep(100)\n    return 5\n  })\n\n  const Trigger = () => {\n    const trigger = useSetAtom(baseAtom)\n    return (\n      <>\n        <button onClick={() => trigger((value) => value)}>trigger</button>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Trigger />\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('trigger'))\n  await act(() => vi.advanceTimersByTimeAsync(100))\n  expect(screen.getByText('Data: 5')).toBeInTheDocument()\n})\n\nit('loadable of a derived async atom with error does not trigger infinite loop (#1330)', async () => {\n  const baseAtom = atom(() => {\n    throw new Error('thrown in baseAtom')\n  })\n  const asyncAtom = atom(async (get) => {\n    get(baseAtom)\n    return ''\n  })\n\n  render(\n    <StrictMode>\n      <LoadableComponent asyncAtom={asyncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Loading...')).toBeInTheDocument()\n  await act(() => vi.advanceTimersByTimeAsync(0))\n  expect(screen.getByText('Error: thrown in baseAtom')).toBeInTheDocument()\n})\n\nit('does not repeatedly attempt to get the value of an unresolved promise atom wrapped in a loadable (#1481)', async () => {\n  const baseAtom = atom(new Promise<number>(() => {}))\n\n  let callsToGetBaseAtom = 0\n  const derivedAtom = atom((get) => {\n    callsToGetBaseAtom++\n    return get(baseAtom)\n  })\n\n  render(\n    <StrictMode>\n      <LoadableComponent asyncAtom={derivedAtom} />\n    </StrictMode>,\n  )\n\n  // we need a small delay to reproduce the issue\n  await act(() => vi.advanceTimersByTimeAsync(10))\n  // depending on provider-less mode or versioned-write mode, there will be\n  // either 2 or 3 calls.\n  expect(callsToGetBaseAtom).toBeLessThanOrEqual(3)\n})\n\nit('should handle sync error (#1843)', () => {\n  const syncAtom = atom(() => {\n    throw new Error('thrown in syncAtom')\n  })\n\n  render(\n    <StrictMode>\n      <LoadableComponent asyncAtom={syncAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('Error: thrown in syncAtom')).toBeInTheDocument()\n})\n\ntype LoadableComponentProps = {\n  asyncAtom: Atom<Promise<number> | Promise<string> | string | number>\n  effectCallback?: (loadableValue: any) => void\n}\n\nconst LoadableComponent = ({\n  asyncAtom,\n  effectCallback,\n}: LoadableComponentProps) => {\n  const value = useAtomValue(loadable(asyncAtom))\n\n  useEffect(() => {\n    if (effectCallback) {\n      effectCallback(value)\n    }\n  }, [value, effectCallback])\n\n  if (value.state === 'loading') {\n    return <>Loading...</>\n  }\n\n  if (value.state === 'hasError') {\n    return <>{String(value.error)}</>\n  }\n\n  // this is to ensure correct typing\n  const data: number | string = value.data\n\n  return <>Data: {data}</>\n}\n"
  },
  {
    "path": "tests/react/vanilla-utils/selectAtom.test.tsx",
    "content": "import { StrictMode } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom } from 'jotai/vanilla'\nimport { selectAtom } from 'jotai/vanilla/utils'\nimport { useCommitCount } from '../../test-utils'\n\nit('selectAtom works as expected', () => {\n  const bigAtom = atom({ a: 0, b: 'othervalue' })\n  const littleAtom = selectAtom(bigAtom, (v) => v.a)\n\n  const Parent = () => {\n    const setValue = useSetAtom(bigAtom)\n    return (\n      <>\n        <button\n          onClick={() =>\n            setValue((oldValue) => ({ ...oldValue, a: oldValue.a + 1 }))\n          }\n        >\n          increment\n        </button>\n      </>\n    )\n  }\n\n  const Selector = () => {\n    const a = useAtomValue(littleAtom)\n    return (\n      <>\n        <div>a: {a}</div>\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Parent />\n      <Selector />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('a: 0')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('a: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('a: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('a: 3')).toBeInTheDocument()\n})\n\nit('do not update unless equality function says value has changed', () => {\n  const bigAtom = atom({ a: 0 })\n  const littleAtom = selectAtom(\n    bigAtom,\n    (value) => value,\n    (left, right) => JSON.stringify(left) === JSON.stringify(right),\n  )\n\n  const Parent = () => {\n    const setValue = useSetAtom(bigAtom)\n    return (\n      <>\n        <button\n          onClick={() =>\n            setValue((oldValue) => ({ ...oldValue, a: oldValue.a + 1 }))\n          }\n        >\n          increment\n        </button>\n        <button onClick={() => setValue((oldValue) => ({ ...oldValue }))}>\n          copy\n        </button>\n      </>\n    )\n  }\n\n  const Selector = () => {\n    const value = useAtomValue(littleAtom)\n    const commits = useCommitCount()\n    return (\n      <>\n        <div>value: {JSON.stringify(value)}</div>\n        <div>commits: {commits}</div>\n      </>\n    )\n  }\n\n  render(\n    <>\n      <Parent />\n      <Selector />\n    </>,\n  )\n\n  expect(screen.getByText('value: {\"a\":0}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('copy'))\n  expect(screen.getByText('value: {\"a\":0}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 1')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('value: {\"a\":1}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('copy'))\n  expect(screen.getByText('value: {\"a\":1}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 2')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('value: {\"a\":2}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('copy'))\n  expect(screen.getByText('value: {\"a\":2}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('increment'))\n  expect(screen.getByText('value: {\"a\":3}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 4')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('copy'))\n  expect(screen.getByText('value: {\"a\":3}')).toBeInTheDocument()\n  expect(screen.getByText('commits: 4')).toBeInTheDocument()\n})\n\nit('creates fresh cache path when deps differ (memo3)', () => {\n  const baseAtom = atom({ a: 0, b: 1 })\n\n  const derivedAtom1 = selectAtom(baseAtom, (v) => v)\n  const derivedAtom2 = selectAtom(baseAtom, (v) => v)\n\n  expect(derivedAtom1).not.toBe(derivedAtom2)\n\n  const selector = (v: { a: number; b: number }) => v.a\n  const derivedAtom3 = selectAtom(baseAtom, selector)\n  const derivedAtom4 = selectAtom(baseAtom, selector)\n\n  expect(derivedAtom3).toBe(derivedAtom4)\n})\n"
  },
  {
    "path": "tests/react/vanilla-utils/splitAtom.test.tsx",
    "content": "import { StrictMode, useEffect, useRef } from 'react'\nimport { fireEvent, render, screen } from '@testing-library/react'\nimport { expect, it } from 'vitest'\nimport { useAtom, useAtomValue, useSetAtom } from 'jotai/react'\nimport { atom, createStore } from 'jotai/vanilla'\nimport type { Atom, PrimitiveAtom } from 'jotai/vanilla'\nimport { splitAtom } from 'jotai/vanilla/utils'\nimport { useCommitCount } from '../../test-utils'\n\ntype TodoItem = { task: string; checked?: boolean }\n\nit('no unnecessary updates when updating atoms', () => {\n  const todosAtom = atom<TodoItem[]>([\n    { task: 'get cat food', checked: false },\n    { task: 'get dragon food', checked: false },\n  ])\n\n  const TaskList = ({ listAtom }: { listAtom: typeof todosAtom }) => {\n    const [atoms, dispatch] = useAtom(splitAtom(listAtom))\n    return (\n      <>\n        TaskListUpdates: {useCommitCount()}\n        {atoms.map((anAtom) => (\n          <TaskItem\n            key={`${anAtom}`}\n            onRemove={() => dispatch({ type: 'remove', atom: anAtom })}\n            itemAtom={anAtom}\n          />\n        ))}\n      </>\n    )\n  }\n\n  const TaskItem = ({\n    itemAtom,\n  }: {\n    itemAtom: PrimitiveAtom<TodoItem>\n    onRemove: () => void\n  }) => {\n    const [value, onChange] = useAtom(itemAtom)\n    const toggle = () =>\n      onChange((value) => ({ ...value, checked: !value.checked }))\n    return (\n      <li>\n        {value.task} commits: {useCommitCount()}\n        <input\n          data-testid={`${value.task}-checkbox`}\n          type=\"checkbox\"\n          checked={value.checked || false}\n          onChange={toggle}\n        />\n      </li>\n    )\n  }\n\n  render(\n    <>\n      <TaskList listAtom={todosAtom} />\n    </>,\n  )\n\n  expect(screen.getByText('TaskListUpdates: 1')).toBeInTheDocument()\n  expect(screen.getByText('get cat food commits: 1')).toBeInTheDocument()\n  expect(screen.getByText('get dragon food commits: 1')).toBeInTheDocument()\n\n  const catBox = screen.getByTestId('get cat food-checkbox') as HTMLInputElement\n  const dragonBox = screen.getByTestId(\n    'get dragon food-checkbox',\n  ) as HTMLInputElement\n\n  expect(catBox).not.toBeChecked()\n  expect(dragonBox).not.toBeChecked()\n\n  fireEvent.click(catBox)\n  expect(screen.getByText('TaskListUpdates: 1')).toBeInTheDocument()\n  expect(screen.getByText('get cat food commits: 2')).toBeInTheDocument()\n  expect(screen.getByText('get dragon food commits: 1')).toBeInTheDocument()\n\n  expect(catBox).toBeChecked()\n  expect(dragonBox).not.toBeChecked()\n\n  fireEvent.click(dragonBox)\n  expect(screen.getByText('TaskListUpdates: 1')).toBeInTheDocument()\n  expect(screen.getByText('get cat food commits: 2')).toBeInTheDocument()\n  expect(screen.getByText('get dragon food commits: 2')).toBeInTheDocument()\n\n  expect(catBox).toBeChecked()\n  expect(dragonBox).toBeChecked()\n})\n\nit('removing atoms', () => {\n  const todosAtom = atom<TodoItem[]>([\n    { task: 'get cat food', checked: false },\n    { task: 'get dragon food', checked: false },\n    { task: 'help nana', checked: false },\n  ])\n\n  const TaskList = ({ listAtom }: { listAtom: typeof todosAtom }) => {\n    const [atoms, dispatch] = useAtom(splitAtom(listAtom))\n    return (\n      <>\n        {atoms.map((anAtom) => (\n          <TaskItem\n            key={`${anAtom}`}\n            onRemove={() => dispatch({ type: 'remove', atom: anAtom })}\n            itemAtom={anAtom}\n          />\n        ))}\n      </>\n    )\n  }\n\n  const TaskItem = ({\n    itemAtom,\n    onRemove,\n  }: {\n    itemAtom: PrimitiveAtom<TodoItem>\n    onRemove: () => void\n  }) => {\n    const [value] = useAtom(itemAtom)\n    return (\n      <li>\n        <div>{value.task}</div>\n        <button data-testid={`${value.task}-removebutton`} onClick={onRemove}>\n          X\n        </button>\n      </li>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <TaskList listAtom={todosAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('get cat food')).toBeInTheDocument()\n  expect(screen.getByText('get dragon food')).toBeInTheDocument()\n  expect(screen.getByText('help nana')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByTestId('get cat food-removebutton'))\n  expect(screen.queryByText('get cat food')).not.toBeInTheDocument()\n  expect(screen.getByText('get dragon food')).toBeInTheDocument()\n  expect(screen.getByText('help nana')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByTestId('get dragon food-removebutton'))\n  expect(screen.queryByText('get cat food')).not.toBeInTheDocument()\n  expect(screen.queryByText('get dragon food')).not.toBeInTheDocument()\n  expect(screen.getByText('help nana')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByTestId('help nana-removebutton'))\n  expect(screen.queryByText('get cat food')).not.toBeInTheDocument()\n  expect(screen.queryByText('get dragon food')).not.toBeInTheDocument()\n  expect(screen.queryByText('help nana')).not.toBeInTheDocument()\n})\n\nit('inserting atoms', () => {\n  const todosAtom = atom<TodoItem[]>([\n    { task: 'get cat food' },\n    { task: 'get dragon food' },\n    { task: 'help nana' },\n  ])\n\n  const TaskList = ({ listAtom }: { listAtom: typeof todosAtom }) => {\n    const [atoms, dispatch] = useAtom(splitAtom(listAtom))\n    return (\n      <>\n        <ul data-testid=\"list\">\n          {atoms.map((anAtom) => (\n            <TaskItem\n              key={`${anAtom}`}\n              onInsert={(newValue) =>\n                dispatch({\n                  type: 'insert',\n                  value: newValue,\n                  before: anAtom,\n                })\n              }\n              itemAtom={anAtom}\n            />\n          ))}\n        </ul>\n        <button\n          data-testid=\"addtaskbutton\"\n          onClick={() =>\n            dispatch({\n              type: 'insert',\n              value: { task: 'end' },\n            })\n          }\n        >\n          add task\n        </button>\n      </>\n    )\n  }\n\n  let taskCount = 1\n  const TaskItem = ({\n    itemAtom,\n    onInsert,\n  }: {\n    itemAtom: PrimitiveAtom<TodoItem>\n    onInsert: (newValue: TodoItem) => void\n  }) => {\n    const [value] = useAtom(itemAtom)\n    return (\n      <li>\n        <div>{value.task}</div>\n        <button\n          data-testid={`${value.task}-insertbutton`}\n          onClick={() => onInsert({ task: 'new task' + taskCount++ })}\n        >\n          +\n        </button>\n      </li>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <TaskList listAtom={todosAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'get cat food+get dragon food+help nana+',\n  )\n\n  fireEvent.click(screen.getByTestId('help nana-insertbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'get cat food+get dragon food+new task1+help nana+',\n  )\n\n  fireEvent.click(screen.getByTestId('get cat food-insertbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'new task2+get cat food+get dragon food+new task1+help nana+',\n  )\n\n  fireEvent.click(screen.getByTestId('addtaskbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'new task2+get cat food+get dragon food+new task1+help nana+end+',\n  )\n})\n\nit('moving atoms', () => {\n  const todosAtom = atom<TodoItem[]>([\n    { task: 'get cat food' },\n    { task: 'get dragon food' },\n    { task: 'help nana' },\n  ])\n\n  const TaskList = ({ listAtom }: { listAtom: typeof todosAtom }) => {\n    const [atoms, dispatch] = useAtom(splitAtom(listAtom))\n    return (\n      <ul data-testid=\"list\">\n        {atoms.map((anAtom, index) => (\n          <TaskItem\n            key={`${anAtom}`}\n            onMoveLeft={() => {\n              if (index === 0) {\n                dispatch({\n                  type: 'move',\n                  atom: anAtom,\n                })\n              } else if (index > 0) {\n                dispatch({\n                  type: 'move',\n                  atom: anAtom,\n                  before: atoms[index - 1] as PrimitiveAtom<TodoItem>,\n                })\n              }\n            }}\n            onMoveRight={() => {\n              if (index === atoms.length - 1) {\n                dispatch({\n                  type: 'move',\n                  atom: anAtom,\n                })\n              } else if (index < atoms.length - 1) {\n                dispatch({\n                  type: 'move',\n                  atom: anAtom,\n                  before: atoms[index + 2] as PrimitiveAtom<TodoItem>,\n                })\n              }\n            }}\n            itemAtom={anAtom}\n          />\n        ))}\n      </ul>\n    )\n  }\n\n  const TaskItem = ({\n    itemAtom,\n    onMoveLeft,\n    onMoveRight,\n  }: {\n    itemAtom: PrimitiveAtom<TodoItem>\n    onMoveLeft: () => void\n    onMoveRight: () => void\n  }) => {\n    const [value] = useAtom(itemAtom)\n    return (\n      <li>\n        <div>{value.task}</div>\n        <button data-testid={`${value.task}-leftbutton`} onClick={onMoveLeft}>\n          &lt;\n        </button>\n        <button data-testid={`${value.task}-rightbutton`} onClick={onMoveRight}>\n          &gt;\n        </button>\n      </li>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <TaskList listAtom={todosAtom} />\n    </StrictMode>,\n  )\n\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'get cat food<>get dragon food<>help nana<>',\n  )\n\n  fireEvent.click(screen.getByTestId('help nana-leftbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'get cat food<>help nana<>get dragon food<>',\n  )\n\n  fireEvent.click(screen.getByTestId('get cat food-rightbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'help nana<>get cat food<>get dragon food<>',\n  )\n\n  fireEvent.click(screen.getByTestId('get cat food-rightbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'help nana<>get dragon food<>get cat food<>',\n  )\n\n  fireEvent.click(screen.getByTestId('help nana-leftbutton'))\n  expect(screen.getByTestId('list')).toHaveTextContent(\n    'get dragon food<>get cat food<>help nana<>',\n  )\n})\n\nit('read-only array atom', () => {\n  const todosAtom = atom<TodoItem[]>(() => [\n    { task: 'get cat food', checked: false },\n    { task: 'get dragon food', checked: false },\n  ])\n\n  const TaskList = ({ listAtom }: { listAtom: typeof todosAtom }) => {\n    const [atoms] = useAtom(splitAtom(listAtom))\n    return (\n      <>\n        {atoms.map((anAtom) => (\n          <TaskItem key={`${anAtom}`} itemAtom={anAtom} />\n        ))}\n      </>\n    )\n  }\n\n  const TaskItem = ({ itemAtom }: { itemAtom: Atom<TodoItem> }) => {\n    const [value] = useAtom(itemAtom)\n    return (\n      <li>\n        <input\n          data-testid={`${value.task}-checkbox`}\n          type=\"checkbox\"\n          checked={value.checked || false}\n          readOnly\n        />\n      </li>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <TaskList listAtom={todosAtom} />\n    </StrictMode>,\n  )\n\n  const catBox = screen.getByTestId('get cat food-checkbox') as HTMLInputElement\n  const dragonBox = screen.getByTestId(\n    'get dragon food-checkbox',\n  ) as HTMLInputElement\n\n  expect(catBox).not.toBeChecked()\n  expect(dragonBox).not.toBeChecked()\n})\n\nit('no error with cached atoms (fix 510)', () => {\n  const filterAtom = atom('all')\n  const numsAtom = atom<number[]>([0, 1, 2, 3, 4])\n  const filteredAtom = atom<number[]>((get) => {\n    const filter = get(filterAtom)\n    const nums = get(numsAtom)\n    if (filter === 'even') {\n      return nums.filter((num) => num % 2 === 0)\n    }\n    return nums\n  })\n  const filteredAtomsAtom = splitAtom(filteredAtom, (num) => num)\n\n  function useCachedAtoms<T>(atoms: T[]) {\n    const prevAtoms = useRef<T[]>(atoms)\n    // eslint-disable-next-line react-hooks/refs\n    return prevAtoms.current\n  }\n\n  type NumItemProps = { atom: Atom<number> }\n\n  const NumItem = ({ atom }: NumItemProps) => {\n    const [readOnlyItem] = useAtom(atom)\n    if (typeof readOnlyItem !== 'number') {\n      throw new Error('expecting a number')\n    }\n    return <>{readOnlyItem}</>\n  }\n\n  function Filter() {\n    const [, setFilter] = useAtom(filterAtom)\n    return <button onClick={() => setFilter('even')}>button</button>\n  }\n\n  const Filtered = () => {\n    const [todos] = useAtom(filteredAtomsAtom)\n    const cachedAtoms = useCachedAtoms(todos)\n\n    return (\n      <>\n        {cachedAtoms.map((atom) => (\n          <NumItem key={`${atom}`} atom={atom} />\n        ))}\n      </>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <Filter />\n      <Filtered />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('01234')).toBeInTheDocument()\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('01234')).toBeInTheDocument()\n})\n\nit('variable sized split atom', () => {\n  const lengthAtom = atom(3)\n  const collectionAtom = atom<number[]>([])\n  const collectionAtomsAtom = splitAtom(collectionAtom)\n  const derivativeAtom = atom((get) =>\n    get(collectionAtomsAtom).map((ca) => get(ca)),\n  )\n\n  function App() {\n    const [length, setLength] = useAtom(lengthAtom)\n    const setCollection = useSetAtom(collectionAtom)\n    const [derivative] = useAtom(derivativeAtom)\n    useEffect(() => {\n      setCollection([1, 2, 3].splice(0, length))\n    }, [length, setCollection])\n    return (\n      <div>\n        <button onClick={() => setLength(2)}>button</button>\n        numbers: {derivative.join(',')}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('numbers: 1,2,3')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('numbers: 1,2')).toBeInTheDocument()\n})\n\nit('should not update split atom when single item is set to identical value', () => {\n  const initialCollection = [1, 2, 3]\n  const collectionAtom = atom<number[]>(initialCollection)\n  const collectionAtomsAtom = splitAtom(collectionAtom)\n\n  function App() {\n    const collectionAtoms = useAtomValue(collectionAtomsAtom)\n    const setItem2 = useSetAtom(collectionAtoms[1]!)\n    const currentCollection = useAtomValue(collectionAtom)\n    return (\n      <div>\n        <button onClick={() => setItem2(2)}>button</button>\n        changed: {(!Object.is(currentCollection, initialCollection)).toString()}\n      </div>\n    )\n  }\n\n  render(\n    <StrictMode>\n      <App />\n    </StrictMode>,\n  )\n\n  expect(screen.getByText('changed: false')).toBeInTheDocument()\n\n  fireEvent.click(screen.getByText('button'))\n  expect(screen.getByText('changed: false')).toBeInTheDocument()\n})\n\nit('should throw error when writing to a removed item atom', () => {\n  const store = createStore()\n  const arrAtom = atom([1, 2, 3])\n  const splittedAtom = splitAtom(arrAtom)\n\n  const atomList = store.get(splittedAtom)\n  const secondAtom = atomList[1]!\n\n  store.set(arrAtom, [])\n\n  expect(() => store.set(secondAtom, 10)).toThrow(\n    'splitAtom: index out of bounds for write',\n  )\n})\n"
  },
  {
    "path": "tests/setup.ts",
    "content": "import '@testing-library/jest-dom/vitest'\n"
  },
  {
    "path": "tests/test-utils.ts",
    "content": "import { useEffect, useRef } from 'react'\n\nexport function sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function useCommitCount(): number {\n  const commitCountRef = useRef(1)\n  useEffect(() => {\n    commitCountRef.current += 1\n  })\n  // eslint-disable-next-line react-hooks/refs\n  return commitCountRef.current\n}\n"
  },
  {
    "path": "tests/vanilla/basic.test.tsx",
    "content": "import { expect, it } from 'vitest'\nimport { atom } from 'jotai/vanilla'\n\nit('creates atoms', () => {\n  // primitive atom\n  const countAtom = atom(0)\n  const anotherCountAtom = atom(1)\n  // read-only derived atom\n  const doubledCountAtom = atom((get) => get(countAtom) * 2)\n  // read-write derived atom\n  const sumCountAtom = atom(\n    (get) => get(countAtom) + get(anotherCountAtom),\n    (get, set, value: number) => {\n      set(countAtom, get(countAtom) + value / 2)\n      set(anotherCountAtom, get(anotherCountAtom) + value / 2)\n    },\n  )\n  // write-only derived atom\n  const decrementCountAtom = atom(null, (get, set) => {\n    set(countAtom, get(countAtom) - 1)\n  })\n  delete countAtom.debugLabel\n  delete doubledCountAtom.debugLabel\n  delete sumCountAtom.debugLabel\n  delete decrementCountAtom.debugLabel\n  expect({\n    countAtom,\n    doubledCountAtom,\n    sumCountAtom,\n    decrementCountAtom,\n  }).toMatchInlineSnapshot(`\n    {\n      \"countAtom\": {\n        \"init\": 0,\n        \"read\": [Function],\n        \"toString\": [Function],\n        \"write\": [Function],\n      },\n      \"decrementCountAtom\": {\n        \"init\": null,\n        \"read\": [Function],\n        \"toString\": [Function],\n        \"write\": [Function],\n      },\n      \"doubledCountAtom\": {\n        \"read\": [Function],\n        \"toString\": [Function],\n      },\n      \"sumCountAtom\": {\n        \"read\": [Function],\n        \"toString\": [Function],\n        \"write\": [Function],\n      },\n    }\n  `)\n})\n\nit('[DEV-ONLY] should include debugLabel in toString output', () => {\n  const countAtom = atom(0)\n  countAtom.debugLabel = 'count'\n  expect(countAtom.toString()).toContain(':count')\n})\n\nit('should let users mark atoms as private', () => {\n  const internalAtom = atom(0)\n  internalAtom.debugPrivate = true\n  delete internalAtom.debugLabel\n\n  expect(internalAtom).toMatchInlineSnapshot(`\n    {\n      \"debugPrivate\": true,\n      \"init\": 0,\n      \"read\": [Function],\n      \"toString\": [Function],\n      \"write\": [Function],\n    }\n  `)\n})\n"
  },
  {
    "path": "tests/vanilla/dependency.test.tsx",
    "content": "import { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport { sleep } from '../test-utils'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\nit('can propagate updates with async atom chains', async () => {\n  const store = createStore()\n\n  const countAtom = atom(1)\n  const asyncAtom = atom(async (get) => {\n    const count = get(countAtom)\n    await sleep(100)\n    return count\n  })\n  const async2Atom = atom((get) => get(asyncAtom))\n  const async3Atom = atom((get) => get(async2Atom))\n\n  expect(store.get(async3Atom) instanceof Promise).toBeTruthy()\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(async3Atom)).resolves.toBe(1)\n\n  store.set(countAtom, (c) => c + 1)\n  expect(store.get(async3Atom) instanceof Promise).toBeTruthy()\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(async3Atom)).resolves.toBe(2)\n\n  store.set(countAtom, (c) => c + 1)\n  expect(store.get(async3Atom) instanceof Promise).toBeTruthy()\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(async3Atom)).resolves.toBe(3)\n})\n\nit('can get async atom with deps more than once before resolving (#1668)', async () => {\n  const countAtom = atom(0)\n  const asyncAtom = atom(async (get) => {\n    const count = get(countAtom)\n    await sleep(100)\n    return count\n  })\n  const store = createStore()\n\n  store.set(countAtom, (c) => c + 1)\n  store.get(asyncAtom)\n  store.set(countAtom, (c) => c + 1)\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(asyncAtom)).resolves.toBe(2)\n})\n\nit('correctly updates async derived atom after get/set update', async () => {\n  const baseAtom = atom(0)\n  const derivedAsyncAtom = atom(\n    async (get) => get(baseAtom) + 1,\n    async (_get, set, val) => set(baseAtom, val as number),\n  )\n\n  const store = createStore()\n\n  // NOTE: Have to .set() straight after await on .get(), so that it executes\n  // in the same JS event loop cycle!\n  let derived = await store.get(derivedAsyncAtom)\n  await store.set(derivedAsyncAtom, 2)\n\n  expect(derived).toBe(1)\n  expect(store.get(baseAtom)).toBe(2)\n\n  derived = await store.get(derivedAsyncAtom)\n  expect(derived).toBe(3)\n})\n\nit('correctly handles the same promise being returned twice from an atom getter (#2151)', async () => {\n  const asyncDataAtom = atom(async () => {\n    return 'Asynchronous Data'\n  })\n\n  const counterAtom = atom(0)\n\n  const derivedAtom = atom((get) => {\n    get(counterAtom) // depending on sync data\n    return get(asyncDataAtom) // returning a promise from another atom\n  })\n\n  const store = createStore()\n\n  store.get(derivedAtom)\n  // setting the `counterAtom` dependency on the same JS event loop cycle, before\n  // the `derivedAtom` promise resolves.\n  store.set(counterAtom, 1)\n  await expect(store.get(derivedAtom)).resolves.toBe('Asynchronous Data')\n})\n\nit('keeps atoms mounted between recalculations', async () => {\n  const metrics1 = {\n    mounted: 0,\n    unmounted: 0,\n  }\n  const atom1 = atom(0)\n  atom1.onMount = () => {\n    ++metrics1.mounted\n    return () => {\n      ++metrics1.unmounted\n    }\n  }\n\n  const metrics2 = {\n    mounted: 0,\n    unmounted: 0,\n  }\n  const atom2 = atom(0)\n  atom2.onMount = () => {\n    ++metrics2.mounted\n    return () => {\n      ++metrics2.unmounted\n    }\n  }\n\n  const derivedAtom = atom(async (get) => {\n    get(atom1)\n    await sleep(100)\n    get(atom2)\n  })\n\n  const unrelatedAtom = atom(0)\n\n  const store = createStore()\n\n  store.sub(derivedAtom, () => {})\n  await vi.advanceTimersByTimeAsync(100)\n  store.set(unrelatedAtom, (c) => c + 1)\n  expect(metrics1).toEqual({\n    mounted: 1,\n    unmounted: 0,\n  })\n  expect(metrics2).toEqual({\n    mounted: 1,\n    unmounted: 0,\n  })\n\n  store.set(atom1, (c) => c + 1)\n  await vi.advanceTimersByTimeAsync(100)\n  expect(metrics1).toEqual({\n    mounted: 1,\n    unmounted: 0,\n  })\n  expect(metrics2).toEqual({\n    mounted: 1,\n    unmounted: 0,\n  })\n})\n\nit('should not provide stale values to conditional dependents', () => {\n  const dataAtom = atom<number[]>([100])\n  const hasFilterAtom = atom(false)\n  const filteredAtom = atom((get) => {\n    const data = get(dataAtom)\n    const hasFilter = get(hasFilterAtom)\n    if (hasFilter) {\n      return []\n    } else {\n      return data\n    }\n  })\n  const stageAtom = atom((get) => {\n    const hasFilter = get(hasFilterAtom)\n    if (hasFilter) {\n      const filtered = get(filteredAtom)\n      return filtered.length === 0 ? 'is-empty' : 'has-data'\n    } else {\n      return 'no-filter'\n    }\n  })\n\n  const store = createStore()\n  store.sub(filteredAtom, () => undefined)\n  store.sub(stageAtom, () => undefined)\n\n  expect(store.get(stageAtom), 'should start without filter').toBe('no-filter')\n  store.set(hasFilterAtom, true)\n  expect(store.get(stageAtom), 'should update').toBe('is-empty')\n})\n\nit('settles never resolving async derivations with deps picked up sync', async () => {\n  const resolve: ((value: number) => void)[] = []\n\n  const syncAtom = atom({\n    promise: new Promise<number>((r) => resolve.push(r)),\n  })\n\n  const asyncAtom = atom(async (get) => {\n    return await get(syncAtom).promise\n  })\n\n  const store = createStore()\n\n  let sub = 0\n  const values: unknown[] = []\n  store.get(asyncAtom).then((value) => values.push(value))\n  store.sub(asyncAtom, () => {\n    sub++\n    store.get(asyncAtom).then((value) => values.push(value))\n  })\n\n  store.set(syncAtom, {\n    promise: new Promise<number>((r) => resolve.push(r)),\n  })\n  resolve[1]?.(1)\n\n  await vi.advanceTimersByTimeAsync(0)\n  expect(values).toEqual([1])\n  expect(sub).toBe(1)\n})\n\nit('settles never resolving async derivations with deps picked up async', async () => {\n  const resolve: ((value: number) => void)[] = []\n\n  const syncAtom = atom({\n    promise: new Promise<number>((r) => resolve.push(r)),\n  })\n\n  const asyncAtom = atom(async (get) => {\n    // we want to pick up `syncAtom` as an async dep\n    await sleep(100)\n    return await get(syncAtom).promise\n  })\n\n  const store = createStore()\n\n  let sub = 0\n  const values: unknown[] = []\n  store.get(asyncAtom).then((value) => values.push(value))\n  store.sub(asyncAtom, () => {\n    sub++\n    store.get(asyncAtom).then((value) => values.push(value))\n  })\n\n  await vi.advanceTimersByTimeAsync(100)\n  store.set(syncAtom, {\n    promise: new Promise<number>((r) => resolve.push(r)),\n  })\n  resolve[1]?.(1)\n\n  await vi.advanceTimersByTimeAsync(100)\n  expect(values).toEqual([1])\n  expect(sub).toBe(1)\n})\n\nit('refreshes deps for each async read', async () => {\n  const countAtom = atom(0)\n  const depAtom = atom(false)\n  const values: number[] = []\n  const asyncAtom = atom(async (get) => {\n    const count = get(countAtom)\n    values.push(count)\n    if (count === 0) {\n      get(depAtom)\n    }\n    await sleep(100)\n    return count\n  })\n  const store = createStore()\n\n  store.get(asyncAtom)\n  store.set(countAtom, (c) => c + 1)\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(asyncAtom)).resolves.toBe(1)\n\n  store.set(depAtom, true)\n  store.get(asyncAtom)\n  await vi.advanceTimersByTimeAsync(0)\n  expect(values).toEqual([0, 1])\n})\n\nit('should not re-evaluate stable derived atom values in situations where dependencies are re-ordered (#2738)', () => {\n  const callCounter = vi.fn()\n  const countAtom = atom(0)\n  const rootAtom = atom(false)\n  const stableDep = atom((get) => {\n    get(rootAtom)\n    return 1\n  })\n  const stableDepDep = atom((get) => {\n    get(stableDep)\n    callCounter()\n    return 2 + get(countAtom)\n  })\n\n  const newAtom = atom((get) => {\n    if (get(rootAtom) || get(countAtom) > 0) {\n      return get(stableDepDep)\n    }\n\n    return get(stableDep)\n  })\n\n  const store = createStore()\n  store.sub(stableDepDep, () => {})\n  store.sub(newAtom, () => {})\n  expect(store.get(stableDepDep)).toBe(2)\n  expect(callCounter).toHaveBeenCalledTimes(1)\n\n  store.set(rootAtom, true)\n  expect(store.get(newAtom)).toBe(2)\n  expect(callCounter).toHaveBeenCalledTimes(1)\n\n  store.set(rootAtom, false)\n  store.set(countAtom, 1)\n  expect(store.get(newAtom)).toBe(3)\n  expect(callCounter).toHaveBeenCalledTimes(2)\n})\n\nit('handles complex dependency chains', async () => {\n  const baseAtom = atom(1)\n  const derived1 = atom((get) => get(baseAtom) * 2)\n  const derived2 = atom((get) => get(derived1) + 1)\n  const asyncDerived = atom(async (get) => {\n    const value = get(derived2)\n    await sleep(100)\n    return value * 2\n  })\n  const store = createStore()\n\n  store.get(asyncDerived)\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(asyncDerived)).resolves.toBe(6)\n\n  store.set(baseAtom, 2)\n  store.get(asyncDerived)\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(asyncDerived)).resolves.toBe(10)\n})\n\nit('can read sync derived atom in write without initializing', () => {\n  const store = createStore()\n  const a = atom(0)\n  const b = atom((get) => get(a) + 1)\n  const c = atom(null, (get, set) => set(a, get(b)))\n  store.set(c)\n  expect(store.get(a)).toBe(1)\n  store.set(c)\n  // note: this is why write get needs to update deps\n  expect(store.get(a)).toBe(2)\n})\n\nit('can read in write function without changing dependencies', () => {\n  // https://github.com/pmndrs/jotai/discussions/2789\n  const a = atom(0)\n  let bReadCount = 0\n  const b = atom(\n    (get) => {\n      ++bReadCount\n      return get(a)\n    },\n    () => {},\n  )\n  let bIsMounted = false\n  b.onMount = () => {\n    bIsMounted = true\n  }\n  const c = atom((get) => get(a))\n  const w = atom(null, (get, set) => {\n    expect(bReadCount).toBe(0)\n    const bValue = get(b)\n    expect(bReadCount).toBe(1)\n    set(a, bValue + 1)\n    expect(bReadCount).toBe(1)\n  })\n\n  const store = createStore()\n  store.sub(c, () => {}) // mounts c,a\n  store.set(w)\n  expect(bIsMounted).toBe(false)\n})\n\nit('can cache reading an atom in write function (without mounting)', () => {\n  let aReadCount = 0\n  const a = atom(() => {\n    ++aReadCount\n    return 'a'\n  })\n  const w = atom(null, (get) => get(a))\n  const store = createStore()\n  store.set(w)\n  expect(aReadCount).toBe(1)\n  store.set(w)\n  expect(aReadCount).toBe(1)\n})\n\nit('can cache reading an atom in write function (with mounting)', () => {\n  let aReadCount = 0\n  const a = atom(() => {\n    ++aReadCount\n    return 'a'\n  })\n  const w = atom(null, (get) => get(a))\n  const store = createStore()\n  store.sub(a, () => {}) // mounts a\n  store.set(w)\n  expect(aReadCount).toBe(1)\n  store.set(w)\n  expect(aReadCount).toBe(1)\n})\n\nit('batches sync writes', () => {\n  const a = atom(0)\n  const b = atom((get) => get(a))\n  const fetch = vi.fn()\n  const c = atom((get) => fetch(get(a)))\n  const w = atom(null, (get, set) => {\n    set(a, 1)\n    expect(get(b)).toBe(1)\n    expect(fetch).toHaveBeenCalledTimes(0)\n  })\n  const store = createStore()\n  store.sub(b, () => {})\n  store.sub(c, () => {})\n  fetch.mockClear()\n  store.set(w)\n  expect(fetch).toHaveBeenCalledOnce()\n  expect(fetch).toBeCalledWith(1)\n  expect(store.get(a)).toBe(1)\n})\n"
  },
  {
    "path": "tests/vanilla/derive.test.tsx",
    "content": "import { describe, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport type { Atom } from 'jotai/vanilla'\nimport {\n  INTERNAL_buildStoreRev2 as INTERNAL_buildStore,\n  INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks,\n} from 'jotai/vanilla/internals'\n\ntype AtomStateMapType = ReturnType<typeof INTERNAL_getBuildingBlocks>[0]\n\nconst deriveStore = (\n  store: ReturnType<typeof createStore>,\n  enhanceAtomStateMap: (atomStateMap: AtomStateMapType) => AtomStateMapType,\n): ReturnType<typeof createStore> => {\n  const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n  const atomStateMap = buildingBlocks[0]\n  const derivedStore = INTERNAL_buildStore(enhanceAtomStateMap(atomStateMap))\n  return derivedStore\n}\n\ndescribe('deriveStore for scoping atoms', () => {\n  /**\n   * a\n   * S1[a]: a1\n   */\n  it('primitive atom', () => {\n    const a = atom('a')\n    a.onMount = (setSelf) => setSelf((v) => v + ':mounted')\n    const scopedAtoms = new Set<Atom<unknown>>([a])\n\n    const store = createStore()\n    const derivedStore = deriveStore(store, (atomStateMap) => {\n      const scopedAtomStateMap = new WeakMap()\n      return {\n        get: (atom) => {\n          if (scopedAtoms.has(atom)) {\n            return scopedAtomStateMap.get(atom)\n          }\n          return atomStateMap.get(atom)\n        },\n        set: (atom, atomState) => {\n          if (scopedAtoms.has(atom)) {\n            scopedAtomStateMap.set(atom, atomState)\n          } else {\n            atomStateMap.set(atom, atomState)\n          }\n        },\n        has: (atom) => {\n          if (scopedAtoms.has(atom)) {\n            return scopedAtomStateMap.has(atom)\n          }\n          return atomStateMap.has(atom)\n        },\n        delete: (atom) => {\n          if (scopedAtoms.has(atom)) {\n            return scopedAtomStateMap.delete(atom)\n          }\n          return atomStateMap.delete(atom)\n        },\n      }\n    })\n\n    expect(store.get(a)).toBe('a')\n    expect(derivedStore.get(a)).toBe('a')\n\n    derivedStore.sub(a, vi.fn())\n    expect(store.get(a)).toBe('a')\n    expect(derivedStore.get(a)).toBe('a:mounted')\n\n    derivedStore.set(a, (v) => v + ':updated')\n    expect(store.get(a)).toBe('a')\n    expect(derivedStore.get(a)).toBe('a:mounted:updated')\n  })\n\n  /**\n   * a, b, c(a + b)\n   * S1[a]: a1, b0, c0(a1 + b0)\n   */\n  it('derived atom (scoping primitive)', () => {\n    const a = atom('a')\n    const b = atom('b')\n    const c = atom((get) => get(a) + get(b))\n    const scopedAtoms = new Set<Atom<unknown>>([a])\n\n    const store = createStore()\n    const derivedStore = deriveStore(store, (atomStateMap) => {\n      const scopedAtomStateMap = new WeakMap()\n      return {\n        get: (atom) => {\n          if (scopedAtoms.has(atom)) {\n            return scopedAtomStateMap.get(atom)\n          }\n          return atomStateMap.get(atom)\n        },\n        set: (atom, atomState) => {\n          if (scopedAtoms.has(atom)) {\n            scopedAtomStateMap.set(atom, atomState)\n          } else {\n            atomStateMap.set(atom, atomState)\n          }\n        },\n        has: (atom) => {\n          if (scopedAtoms.has(atom)) {\n            return scopedAtomStateMap.has(atom)\n          }\n          return atomStateMap.has(atom)\n        },\n        delete: (atom) => {\n          if (scopedAtoms.has(atom)) {\n            return scopedAtomStateMap.delete(atom)\n          }\n          return atomStateMap.delete(atom)\n        },\n      }\n    })\n\n    expect(store.get(c)).toBe('ab')\n    expect(derivedStore.get(c)).toBe('ab')\n\n    derivedStore.set(a, 'a2')\n    expect(store.get(c)).toBe('ab')\n    expect(derivedStore.get(c)).toBe('a2b')\n  })\n\n  /**\n   * a, b(a)\n   * S1[a]: a1, b0(a1)\n   */\n  it('derived atom with subscribe', () => {\n    const a = atom('a')\n    const b = atom(\n      (get) => get(a),\n      (_get, set, v: string) => set(a, v),\n    )\n    const scopedAtoms = new Set<Atom<unknown>>([a])\n\n    function makeStores() {\n      const store = createStore()\n      const derivedStore = deriveStore(store, (atomStateMap) => {\n        const scopedAtomStateMap = new WeakMap()\n        return {\n          get: (atom) => {\n            if (scopedAtoms.has(atom)) {\n              return scopedAtomStateMap.get(atom)\n            }\n            return atomStateMap.get(atom)\n          },\n          set: (atom, atomState) => {\n            if (scopedAtoms.has(atom)) {\n              scopedAtomStateMap.set(atom, atomState)\n            } else {\n              atomStateMap.set(atom, atomState)\n            }\n          },\n          has: (atom) => {\n            if (scopedAtoms.has(atom)) {\n              return scopedAtomStateMap.has(atom)\n            }\n            return atomStateMap.has(atom)\n          },\n          delete: (atom) => {\n            if (scopedAtoms.has(atom)) {\n              return scopedAtomStateMap.delete(atom)\n            }\n            return atomStateMap.delete(atom)\n          },\n        }\n      })\n      expect(store.get(b)).toBe('a')\n      expect(derivedStore.get(b)).toBe('a')\n      return { store, derivedStore }\n    }\n\n    /**\n     * Ba[ ]: a0, b0(a0)\n     * S1[a]: a1, b0(a1)\n     */\n    {\n      const { store, derivedStore } = makeStores()\n      store.set(b, '*')\n      expect(store.get(b)).toBe('*')\n      expect(derivedStore.get(b)).toBe('a')\n    }\n    {\n      const { store, derivedStore } = makeStores()\n      derivedStore.set(b, '*')\n      expect(store.get(b)).toBe('a')\n      expect(derivedStore.get(b)).toBe('*')\n    }\n    {\n      const { store, derivedStore } = makeStores()\n      const storeCallback = vi.fn()\n      const derivedCallback = vi.fn()\n      store.sub(b, storeCallback)\n      derivedStore.sub(b, derivedCallback)\n      store.set(b, '*')\n      expect(store.get(b)).toBe('*')\n      //expect(derivedStore.get(b)).toBe('a') // FIXME: received '*'\n      expect(storeCallback).toHaveBeenCalledTimes(1)\n      //expect(derivedCallback).toHaveBeenCalledTimes(0) // FIXME: received 1\n    }\n    {\n      const { store, derivedStore } = makeStores()\n      const storeCallback = vi.fn()\n      const derivedCallback = vi.fn()\n      store.sub(b, storeCallback)\n      derivedStore.sub(b, derivedCallback)\n      derivedStore.set(b, '*')\n      //expect(store.get(b)).toBe('a') // FIXME: received '*'\n      expect(derivedStore.get(b)).toBe('*')\n      expect(storeCallback).toHaveBeenCalledTimes(0)\n      expect(derivedCallback).toHaveBeenCalledTimes(1)\n    }\n  })\n})\n\nit('should pass the correct store instance to the atom initializer', () => {\n  expect.assertions(2)\n  const baseStore = createStore()\n  const derivedStore = deriveStore(baseStore, (atomStateMap) => {\n    const initializedAtoms = new WeakSet()\n    return {\n      get: (atom) => {\n        if (!initializedAtoms.has(atom)) {\n          return undefined\n        }\n        return atomStateMap.get(atom)\n      },\n      set: (atom, atomState) => {\n        initializedAtoms.add(atom)\n        atomStateMap.set(atom, atomState)\n      },\n      has: (atom) => {\n        if (!initializedAtoms.has(atom)) {\n          return false\n        }\n        return atomStateMap.has(atom)\n      },\n      delete: (atom) => {\n        initializedAtoms.delete(atom)\n        return atomStateMap.delete(atom)\n      },\n    }\n  })\n  const a = atom(null)\n  a.INTERNAL_onInit = (store) => {\n    expect(store).toBe(baseStore)\n  }\n  baseStore.get(a)\n  a.INTERNAL_onInit = (store) => {\n    expect(store).toBe(derivedStore)\n  }\n  derivedStore.get(a)\n})\n"
  },
  {
    "path": "tests/vanilla/effect.test.ts",
    "content": "import { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport type { Atom, Getter, Setter, WritableAtom } from 'jotai/vanilla'\nimport { atom, createStore } from 'jotai/vanilla'\nimport {\n  INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks,\n  INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks,\n} from 'jotai/vanilla/internals'\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n})\n\ntype Cleanup = () => void\ntype Effect = (get: Getter, set: Setter) => Cleanup | void\ntype Ref = {\n  get?: Getter\n  inProgress: number\n  epoch: number\n  cleanup?: Cleanup | undefined\n}\n\nfunction syncEffect(effect: Effect): Atom<void> {\n  const refAtom = atom<Ref>(() => ({ inProgress: 0, epoch: 0 }))\n  const refreshAtom = atom(0)\n  const internalAtom = atom(\n    (get) => {\n      get(refreshAtom)\n      const ref = get(refAtom)\n      if (ref.inProgress) {\n        return ref.epoch\n      }\n      ref.get = get\n      return ++ref.epoch\n    },\n    () => {},\n  )\n  internalAtom.onMount = () => {\n    return () => {}\n  }\n  internalAtom.INTERNAL_onInit = (store) => {\n    const ref = store.get(refAtom)\n    const runEffect = () => {\n      const deps = new Set<Atom<unknown>>()\n      try {\n        ref.cleanup?.()\n        ref.cleanup =\n          effect(\n            (a) => {\n              deps.add(a)\n              return store.get(a)\n            },\n            (a, ...args) => {\n              try {\n                ++ref.inProgress\n                return store.set(a, ...args)\n              } finally {\n                --ref.inProgress\n              }\n            },\n          ) || undefined\n      } finally {\n        deps.forEach(ref.get!)\n      }\n    }\n    const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n    const storeHooks = INTERNAL_initializeStoreHooks(buildingBlocks[6])\n    const syncEffectChannel = ensureSyncEffectChannel(store)\n    storeHooks.m.add(internalAtom, () => {\n      // mount\n      store.set(refreshAtom, (v) => v + 1)\n    })\n    storeHooks.u.add(internalAtom, () => {\n      // unmount\n      syncEffectChannel.add(() => {\n        ref.cleanup?.()\n        delete ref.cleanup\n      })\n    })\n    storeHooks.c.add(internalAtom, () => {\n      // update\n      syncEffectChannel.add(runEffect)\n    })\n  }\n  return atom((get) => {\n    get(internalAtom)\n  })\n}\n\nconst syncEffectChannelSymbol = Symbol()\n\nfunction ensureSyncEffectChannel(store: any) {\n  if (!store[syncEffectChannelSymbol]) {\n    store[syncEffectChannelSymbol] = new Set<() => void>()\n    const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n    const storeHooks = INTERNAL_initializeStoreHooks(buildingBlocks[6])\n    storeHooks.f.add(() => {\n      const syncEffectChannel = store[syncEffectChannelSymbol] as Set<\n        () => void\n      >\n      const fns = Array.from(syncEffectChannel)\n      syncEffectChannel.clear()\n      fns.forEach((fn: () => void) => fn())\n    })\n  }\n  return store[syncEffectChannelSymbol] as Set<() => void>\n}\n\nit('fires after recomputeDependents and before atom listeners', async function test() {\n  const store = createStore()\n  const a = atom({} as { v?: number })\n  let r\n  const e = syncEffect(function effect(get) {\n    r = get(a).v\n  })\n  const b = atom(function bAtomRead(get) {\n    const aValue = get(a)\n    get(e)\n    // sets property `v` inside recomputeDependents\n    aValue.v = 1\n    return aValue\n  })\n  store.sub(b, function bAtomListener() {\n    // sets property `v` inside atom listener\n    store.get(a).v = 2\n  })\n  store.set(a, { v: 0 })\n  expect(r).toBe(1)\n})\n\nit('responds to changes to atoms when subscribed', () => {\n  const store = createStore()\n  const a = atom(1)\n  const b = atom(1)\n  const w = atom(null, (_get, set, value: number) => {\n    set(a, value)\n    set(b, value)\n  })\n  const results: number[] = []\n  const cleanup = vi.fn()\n  const effect = vi.fn((get: Getter) => {\n    results.push(get(a) * 10 + get(b))\n    return cleanup\n  })\n  const e = syncEffect(effect)\n  const unsub = store.sub(e, () => {}) // mount syncEffect\n  expect(effect).toBeCalledTimes(1)\n  expect(results).toStrictEqual([11]) // initial values at time of effect mount\n  store.set(a, 2)\n  expect(results).toStrictEqual([11, 21])\n  store.set(b, 2)\n  expect(results).toStrictEqual([11, 21, 22])\n  store.set(w, 3)\n  // intermediate state of '32' should not be recorded since the effect runs _after_ graph has been computed\n  expect(results).toStrictEqual([11, 21, 22, 33])\n  expect(cleanup).toBeCalledTimes(3)\n  expect(effect).toBeCalledTimes(4)\n  unsub()\n  expect(cleanup).toBeCalledTimes(4)\n  expect(effect).toBeCalledTimes(4)\n  store.set(a, 4)\n  // the effect is unmounted so no more updates\n  expect(results).toStrictEqual([11, 21, 22, 33])\n  expect(effect).toBeCalledTimes(4)\n})\n\nit('responds to changes to atoms when mounted with get', () => {\n  const store = createStore()\n  const a = atom(1)\n  const b = atom(1)\n  const w = atom(null, (_get, set, value: number) => {\n    set(a, value)\n    set(b, value)\n  })\n  const results: number[] = []\n  const cleanup = vi.fn()\n  const effect = vi.fn((get: Getter) => {\n    results.push(get(a) * 10 + get(b))\n    return cleanup\n  })\n  const e = syncEffect(effect)\n  const d = atom((get) => get(e))\n  const unsub = store.sub(d, () => {}) // mount syncEffect\n  expect(effect).toBeCalledTimes(1)\n  expect(results).toStrictEqual([11]) // initial values at time of effect mount\n  store.set(a, 2)\n  expect(results).toStrictEqual([11, 21])\n  store.set(b, 2)\n  expect(results).toStrictEqual([11, 21, 22])\n  store.set(w, 3)\n  // intermediate state of '32' should not be recorded since the effect runs _after_ graph has been computed\n  expect(results).toStrictEqual([11, 21, 22, 33])\n  expect(cleanup).toBeCalledTimes(3)\n  expect(effect).toBeCalledTimes(4)\n  unsub()\n  expect(cleanup).toBeCalledTimes(4)\n  expect(effect).toBeCalledTimes(4)\n})\n\nit('sets values to atoms without causing infinite loop', () => {\n  const store = createStore()\n  const a = atom(1)\n  const effect = vi.fn((get: Getter, set: Setter) => {\n    set(a, get(a) + 1)\n  })\n  const e = syncEffect(effect)\n  const unsub = store.sub(e, () => {}) // mount syncEffect\n  expect(effect).toBeCalledTimes(1)\n  expect(store.get(a)).toBe(2) // initial values at time of effect mount\n  store.set(a, (v) => ++v)\n  expect(store.get(a)).toBe(4)\n  expect(effect).toBeCalledTimes(2)\n  unsub()\n  expect(effect).toBeCalledTimes(2)\n})\n\n// TODO: consider removing this after we provide a new syncEffect implementation\nit('supports recursive setting synchronous in read', async () => {\n  const store = createStore()\n  const a = atom(0)\n  const refreshAtom = atom(0)\n  type Ref = {\n    isMounted?: true\n    recursing: number\n    set: Setter\n  }\n  const refAtom = atom(\n    () => ({ recursing: 0 }) as Ref,\n    (get, set) => {\n      const ref = get(refAtom)\n      ref.isMounted = true\n      ref.set = set\n      set(refreshAtom, (v) => v + 1)\n    },\n  )\n  refAtom.onMount = (mount) => mount()\n  const effectAtom = atom((get) => {\n    get(refreshAtom)\n    const ref = get(refAtom)\n    if (!ref.isMounted) {\n      return\n    }\n    const recurse = <Value, Args extends unknown[], Result>(\n      a: WritableAtom<Value, Args, Result>,\n      ...args: Args\n    ): Result => {\n      ++ref.recursing\n      const value = ref.set(a, ...args)\n      return value as Result\n    }\n    function runEffect() {\n      const v = get(a)\n      if (v < 5) {\n        recurse(a, (v) => v + 1)\n      }\n    }\n    if (ref.recursing) {\n      let prevRecursing = ref.recursing\n      do {\n        prevRecursing = ref.recursing\n        runEffect()\n      } while (prevRecursing !== ref.recursing)\n      ref.recursing = 0\n      return Promise.resolve()\n    }\n    return Promise.resolve().then(runEffect)\n  })\n  store.sub(effectAtom, () => {})\n  await vi.advanceTimersByTimeAsync(0)\n  expect(store.get(a)).toBe(5)\n})\n"
  },
  {
    "path": "tests/vanilla/internals.test.tsx",
    "content": "import { describe, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai'\nimport type {\n  INTERNAL_AtomState,\n  INTERNAL_AtomStateMap,\n  INTERNAL_BuildingBlocks,\n  INTERNAL_InvalidatedAtoms,\n} from 'jotai/vanilla/internals'\nimport {\n  INTERNAL_buildStoreRev2 as INTERNAL_buildStore,\n  INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks,\n  INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks,\n} from 'jotai/vanilla/internals'\n\nconst buildingBlockLength = 28\n\ndescribe('internals', () => {\n  it('should not return a sparse building blocks array', () => {\n    {\n      const store = createStore()\n      const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n      expect(isBuildingBlocks(buildingBlocks)).toBe(true)\n    }\n    {\n      const store = INTERNAL_buildStore()\n      const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n      expect(isBuildingBlocks(buildingBlocks)).toBe(true)\n    }\n  })\n\n  it('internals should not hold stale references', () => {\n    const createMockAtomStateMap = () => {\n      return {\n        get: vi.fn(() => {\n          return {\n            d: new Map(),\n            p: new Set(),\n            n: 0,\n            v: 0,\n          } as INTERNAL_AtomState\n        }),\n        set: vi.fn(),\n        has: vi.fn(() => true),\n        delete: vi.fn(() => true),\n      } as INTERNAL_AtomStateMap\n    }\n    const mockAtomStateMap1 = createMockAtomStateMap()\n    const buildingBlocks1: Partial<INTERNAL_BuildingBlocks> = [\n      mockAtomStateMap1,\n    ]\n    const store1 = INTERNAL_buildStore(...buildingBlocks1)\n    const buildingBlocks2 = [\n      ...INTERNAL_getBuildingBlocks(store1),\n    ] as INTERNAL_BuildingBlocks\n    const mockAtomStateMap2 = createMockAtomStateMap()\n    buildingBlocks2[0] = mockAtomStateMap2\n    const store2 = INTERNAL_buildStore(...buildingBlocks2)\n    store2.get(atom(0))\n    expect(mockAtomStateMap1.get).not.toBeCalled()\n    expect(mockAtomStateMap2.get).toBeCalled()\n  })\n\n  it('should transform external building blocks differently from internal ones', () => {\n    const didRun = {\n      internal: vi.fn(),\n      external: vi.fn(),\n    }\n    const bb0 = [] as Partial<INTERNAL_BuildingBlocks>\n    bb0[21] = function storeGet1() {\n      didRun.internal()\n    } as INTERNAL_BuildingBlocks[21]\n    let bbInternal: Readonly<INTERNAL_BuildingBlocks> | undefined\n    function storeGet() {\n      didRun.external()\n    }\n    bb0[24] = (bbi) => {\n      bbInternal = bbi\n      const bb1 = [...bbi] as INTERNAL_BuildingBlocks\n      bb1[21] = storeGet as INTERNAL_BuildingBlocks[21]\n      return bb1\n    }\n    const store1 = INTERNAL_buildStore(...bb0)\n    const bb1 = INTERNAL_getBuildingBlocks(store1)\n    expect(isBuildingBlocks(bb1)).toBe(true)\n    expect(isBuildingBlocks(bbInternal)).toBe(true)\n    const store2 = INTERNAL_buildStore(...bb1)\n    const bb2 = INTERNAL_getBuildingBlocks(store2)\n    expect(isBuildingBlocks(bb2)).toBe(true)\n    expect(isBuildingBlocks(bbInternal)).toBe(true)\n    expect(bb0[21]).not.toBe(bb1[21])\n    expect(bb1[21]).toBe(bb2[21])\n    store1.get(atom(0))\n    expect(didRun.internal).toBeCalledTimes(1)\n    expect(didRun.external).toBeCalledTimes(0)\n    vi.clearAllMocks()\n    store2.get(atom(0))\n    expect(didRun.internal).toBeCalledTimes(0)\n    expect(didRun.external).toBeCalledTimes(1)\n  })\n\n  it('invalidateDependents should not invalidate the same dependent twice via multiple paths', () => {\n    const invalidatedAtoms = (() => {\n      const map = new WeakMap()\n      return {\n        get: (key) => map.get(key),\n        set: (key, value) => {\n          const prev = map.get(key)\n          if (prev === value) {\n            throw new Error('duplicate invalidation')\n          }\n          map.set(key, value)\n        },\n        has: (key) => map.has(key),\n        delete: (key) => map.delete(key),\n      } as INTERNAL_InvalidatedAtoms\n    })()\n\n    const buildingBlocks: Partial<INTERNAL_BuildingBlocks> = []\n    buildingBlocks[2] = invalidatedAtoms\n    const store = INTERNAL_buildStore(...buildingBlocks)\n\n    const baseAtom = atom(0)\n    const midAtom1 = atom((get) => get(baseAtom))\n    const midAtom2 = atom((get) => get(baseAtom))\n    const leafAtom = atom((get) => get(midAtom1) + get(midAtom2))\n\n    const unsub = store.sub(leafAtom, () => {})\n    const invalidateDependents = INTERNAL_getBuildingBlocks(store)[15]\n    expect(() => invalidateDependents(store, baseAtom)).not.toThrow()\n    unsub()\n  })\n})\n\ndescribe('store hooks', () => {\n  // Helper function to create store with hooks\n  const createStoreWithHooks = () => {\n    const storeHooks = INTERNAL_initializeStoreHooks({})\n    const buildingBlocks = [] as Partial<INTERNAL_BuildingBlocks>\n    buildingBlocks[6] = storeHooks\n    const store = INTERNAL_buildStore(...buildingBlocks)\n    return { store, storeHooks }\n  }\n\n  describe('init hook (i)', () => {\n    it('should call init hook when atom state is initialized', () => {\n      const { store, storeHooks } = createStoreWithHooks()\n      const baseAtom = atom(0)\n      const initCallback = vi.fn()\n      storeHooks.i.add(baseAtom, initCallback)\n      store.get(baseAtom)\n      expect(initCallback).toHaveBeenCalledTimes(1)\n    })\n  })\n\n  describe('read hook (r)', () => {\n    it('should call read hook when atom is read', () => {\n      const { store, storeHooks } = createStoreWithHooks()\n      const baseAtom = atom(0)\n      const derivedAtom = atom((get) => get(baseAtom))\n      const readCallback = vi.fn()\n\n      storeHooks.r.add(derivedAtom, readCallback)\n      store.get(derivedAtom)\n      expect(readCallback).toHaveBeenCalledTimes(1)\n      readCallback.mockClear()\n      store.get(derivedAtom)\n      expect(readCallback).toHaveBeenCalledTimes(0)\n      store.set(baseAtom, 1)\n      store.get(derivedAtom)\n      expect(readCallback).toHaveBeenCalledTimes(1)\n    })\n  })\n\n  describe('mount hook (m)', () => {\n    it('should call mount hook when atom is mounted', () => {\n      const { store, storeHooks } = createStoreWithHooks()\n      const countAtom = atom(0)\n      const mountCallback = vi.fn()\n\n      storeHooks.m.add(countAtom, mountCallback)\n      const unsub = store.sub(countAtom, () => {})\n\n      expect(mountCallback).toHaveBeenCalledTimes(1)\n      unsub()\n    })\n  })\n\n  describe('unmount hook (u)', () => {\n    it('should call unmount hook when atom is unmounted', () => {\n      const { store, storeHooks } = createStoreWithHooks()\n      const countAtom = atom(0)\n      const unmountCallback = vi.fn()\n\n      storeHooks.u.add(countAtom, unmountCallback)\n      const unsub = store.sub(countAtom, () => {})\n      unsub()\n\n      expect(unmountCallback).toHaveBeenCalledTimes(1)\n    })\n  })\n\n  describe('change hook (c)', () => {\n    it('should call change hook when atom value changes', () => {\n      const { store, storeHooks } = createStoreWithHooks()\n      const countAtom = atom(0)\n      const changeCallback = vi.fn()\n\n      storeHooks.c.add(countAtom, changeCallback)\n      const unsub = store.sub(countAtom, () => {})\n      store.set(countAtom, 1)\n\n      expect(changeCallback).toHaveBeenCalledTimes(1)\n      changeCallback.mockClear()\n      store.set(countAtom, 1)\n      expect(changeCallback).toHaveBeenCalledTimes(0)\n      unsub()\n    })\n  })\n\n  describe('flush hook (f)', () => {\n    it('should call flush hook when callbacks are flushed', () => {\n      const { store, storeHooks } = createStoreWithHooks()\n      const countAtom = atom(0)\n      const flushCallback = vi.fn()\n\n      storeHooks.f.add(flushCallback)\n      const unsub = store.sub(countAtom, () => {})\n      expect(flushCallback).toHaveBeenCalledTimes(1)\n      flushCallback.mockClear()\n      store.set(countAtom, 1)\n      expect(flushCallback).toHaveBeenCalledTimes(1)\n      flushCallback.mockClear()\n      unsub()\n      expect(flushCallback).toHaveBeenCalledTimes(1)\n    })\n  })\n})\n\nfunction isSparse(arr: ReadonlyArray<unknown>) {\n  return arr.some((_, i) => !Object.prototype.hasOwnProperty.call(arr, i))\n}\n\nfunction isBuildingBlocks(blocks: ReadonlyArray<unknown> | undefined) {\n  return (\n    blocks !== undefined &&\n    blocks.length === buildingBlockLength &&\n    isSparse(blocks) === false\n  )\n}\n"
  },
  {
    "path": "tests/vanilla/memoryleaks.test.ts",
    "content": "import LeakDetector from 'jest-leak-detector'\nimport { describe, expect, it } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport type { Atom } from 'jotai/vanilla'\n\ndescribe('memory leaks (get & set only)', () => {\n  it('one atom', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector = new LeakDetector(store.get(objAtom))\n    objAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('two atoms', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector1 = new LeakDetector(store.get(objAtom))\n    let derivedAtom: Atom<object> | undefined = atom((get) => ({\n      obj: objAtom && get(objAtom),\n    }))\n    const detector2 = new LeakDetector(store.get(derivedAtom))\n    objAtom = undefined\n    derivedAtom = undefined\n    await Promise.resolve()\n    expect(await detector1.isLeaking()).toBe(false)\n    expect(await detector2.isLeaking()).toBe(false)\n  })\n\n  it('should not hold onto dependent atoms that are not mounted', async () => {\n    const store = createStore()\n    const objAtom = atom({})\n    let depAtom: Atom<unknown> | undefined = atom((get) => get(objAtom))\n    const detector = new LeakDetector(depAtom)\n    store.get(depAtom)\n    depAtom = undefined\n    await Promise.resolve()\n    await expect(detector.isLeaking()).resolves.toBe(false)\n  })\n\n  it('with a long-lived base atom', async () => {\n    const store = createStore()\n    const objAtom = atom({})\n    let derivedAtom: Atom<object> | undefined = atom((get) => ({\n      obj: get(objAtom),\n    }))\n    const detector = new LeakDetector(store.get(derivedAtom))\n    derivedAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n})\n\ndescribe('memory leaks (with subscribe)', () => {\n  it('one atom', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector = new LeakDetector(store.get(objAtom))\n    let unsub: (() => void) | undefined = store.sub(objAtom, () => {})\n    unsub()\n    unsub = undefined\n    objAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('two atoms', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector1 = new LeakDetector(store.get(objAtom))\n    let derivedAtom: Atom<object> | undefined = atom((get) => ({\n      obj: objAtom && get(objAtom),\n    }))\n    const detector2 = new LeakDetector(store.get(derivedAtom))\n    let unsub: (() => void) | undefined = store.sub(objAtom, () => {})\n    unsub()\n    unsub = undefined\n    objAtom = undefined\n    derivedAtom = undefined\n    await Promise.resolve()\n    expect(await detector1.isLeaking()).toBe(false)\n    expect(await detector2.isLeaking()).toBe(false)\n  })\n\n  it('with a long-lived base atom', async () => {\n    const store = createStore()\n    const objAtom = atom({})\n    let derivedAtom: Atom<object> | undefined = atom((get) => ({\n      obj: get(objAtom),\n    }))\n    const detector = new LeakDetector(store.get(derivedAtom))\n    let unsub: (() => void) | undefined = store.sub(objAtom, () => {})\n    unsub()\n    unsub = undefined\n    derivedAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n})\n\ndescribe('memory leaks (with dependencies)', () => {\n  it('sync dependency', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector = new LeakDetector(store.get(objAtom))\n    const atom1 = atom(0)\n    const atom2 = atom((get) => get(atom1) || (objAtom && get(objAtom)))\n    store.sub(atom2, () => {})\n    store.set(atom1, 1)\n    objAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('async dependency', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector = new LeakDetector(store.get(objAtom))\n    const atom1 = atom(0)\n    const atom2 = atom(async (get) => get(atom1) || (objAtom && get(objAtom)))\n    store.sub(atom2, () => {})\n    store.set(atom1, 1)\n    objAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n\n  it('async await dependency', async () => {\n    const store = createStore()\n    let objAtom: Atom<object> | undefined = atom({})\n    const detector = new LeakDetector(store.get(objAtom))\n    const atom1 = atom(0)\n    const atom2 = atom(async (get) => {\n      await Promise.resolve()\n      return get(atom1) || (objAtom && get(objAtom))\n    })\n    store.sub(atom2, () => {})\n    store.set(atom1, 1)\n    objAtom = undefined\n    await Promise.resolve()\n    expect(await detector.isLeaking()).toBe(false)\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/store.test.tsx",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport type { Atom, Getter, PrimitiveAtom } from 'jotai/vanilla'\nimport {\n  INTERNAL_buildStoreRev2 as INTERNAL_buildStore,\n  INTERNAL_getBuildingBlocksRev2 as INTERNAL_getBuildingBlocks,\n  INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks,\n} from 'jotai/vanilla/internals'\nimport type { INTERNAL_Store } from 'jotai/vanilla/internals'\nimport { sleep } from '../test-utils'\n\nlet savedConsoleWarn: any\n\nbeforeEach(() => {\n  vi.useFakeTimers()\n  savedConsoleWarn = console.warn\n  console.warn = vi.fn()\n})\n\nafterEach(() => {\n  vi.useRealTimers()\n  console.warn = savedConsoleWarn\n})\n\ntype DevStore = {\n  get_mounted_atoms: () => Set<Atom<unknown>>\n}\n\nconst createDevStore = (): INTERNAL_Store & DevStore => {\n  const storeHooks = INTERNAL_initializeStoreHooks({})\n  const store = INTERNAL_buildStore(\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n    storeHooks,\n  )\n  const debugMountedAtoms = new Set<Atom<unknown>>()\n  storeHooks.m.add(undefined, (atom) => {\n    debugMountedAtoms.add(atom)\n  })\n  storeHooks.u.add(undefined, (atom) => {\n    debugMountedAtoms.delete(atom)\n  })\n  const devStore: DevStore = {\n    get_mounted_atoms: () => debugMountedAtoms,\n  }\n  return Object.assign(store, devStore)\n}\n\ntype AtomStateMapType = ReturnType<typeof INTERNAL_getBuildingBlocks>[0]\n\nconst deriveStore = (\n  store: ReturnType<typeof createStore>,\n  enhanceAtomStateMap: (atomStateMap: AtomStateMapType) => AtomStateMapType,\n): ReturnType<typeof createStore> => {\n  const buildingBlocks = INTERNAL_getBuildingBlocks(store)\n  const atomStateMap = buildingBlocks[0]\n  const derivedStore = INTERNAL_buildStore(enhanceAtomStateMap(atomStateMap))\n  return derivedStore\n}\n\nit('should not fire on subscribe', async () => {\n  const store = createStore()\n  const countAtom = atom(0)\n  const callback1 = vi.fn()\n  const callback2 = vi.fn()\n\n  store.sub(countAtom, callback1)\n  store.sub(countAtom, callback2)\n  expect(callback1).not.toHaveBeenCalled()\n  expect(callback2).not.toHaveBeenCalled()\n})\n\nit('should not fire subscription if primitive atom value is the same', () => {\n  const store = createStore()\n  const countAtom = atom(0)\n  const callback = vi.fn()\n\n  store.sub(countAtom, callback)\n  const calledTimes = callback.mock.calls.length\n  store.set(countAtom, 0)\n  expect(callback).toHaveBeenCalledTimes(calledTimes)\n})\n\nit('should not fire subscription if derived atom value is the same', () => {\n  const store = createStore()\n  const countAtom = atom(0)\n  const derivedAtom = atom((get) => get(countAtom) * 0)\n  const callback = vi.fn()\n\n  store.sub(derivedAtom, callback)\n  const calledTimes = callback.mock.calls.length\n  store.set(countAtom, 1)\n  expect(callback).toHaveBeenCalledTimes(calledTimes)\n})\n\nit('should unmount with store.get', () => {\n  const store = createDevStore()\n  const countAtom = atom(0)\n  const callback = vi.fn()\n  const unsub = store.sub(countAtom, callback)\n\n  store.get(countAtom)\n  unsub()\n  const result = Array.from(store.get_mounted_atoms())\n  expect(result).toEqual([])\n})\n\nit('should unmount dependencies with store.get', () => {\n  const store = createDevStore()\n  const countAtom = atom(0)\n  const derivedAtom = atom((get) => get(countAtom) * 2)\n  const callback = vi.fn()\n  const unsub = store.sub(derivedAtom, callback)\n\n  store.get(derivedAtom)\n  unsub()\n  const result = Array.from(store.get_mounted_atoms())\n  expect(result).toEqual([])\n})\n\nit('should update async atom with delay (#1813)', async () => {\n  const countAtom = atom(0)\n\n  const delayedAtom = atom(async (get) => {\n    const count = get(countAtom)\n    await sleep(100)\n    return count\n  })\n\n  const store = createStore()\n  store.get(delayedAtom)\n  store.set(countAtom, 1)\n  await vi.advanceTimersByTimeAsync(100)\n  await expect(store.get(delayedAtom)).resolves.toBe(1)\n})\n\nit('should override a promise by setting', async () => {\n  const store = createStore()\n  const countAtom = atom(Promise.resolve(0))\n  const infinitePending = new Promise<never>(() => {})\n\n  store.set(countAtom, infinitePending)\n  const promise1 = store.get(countAtom)\n  expect(promise1).toBe(infinitePending)\n\n  store.set(countAtom, Promise.resolve(1))\n  const promise2 = store.get(countAtom)\n  expect(await promise2).toBe(1)\n})\n\nit('should update async atom with deps after await (#1905)', async () => {\n  const countAtom = atom(0)\n  const delayedAtom = atom(async (get) => {\n    await sleep(100)\n    const count = get(countAtom)\n    return count\n  })\n  const derivedAtom = atom(async (get) => {\n    const count = await get(delayedAtom)\n    return count\n  })\n\n  const store = createStore()\n  let lastValue = store.get(derivedAtom)\n  const unsub = store.sub(derivedAtom, () => {\n    lastValue = store.get(derivedAtom)\n  })\n\n  store.set(countAtom, 1)\n  await vi.advanceTimersByTimeAsync(100)\n  expect(await lastValue).toBe(1)\n\n  store.set(countAtom, 2)\n  await vi.advanceTimersByTimeAsync(100)\n  expect(await lastValue).toBe(2)\n\n  store.set(countAtom, 3)\n  await vi.advanceTimersByTimeAsync(100)\n  expect(await lastValue).toBe(3)\n\n  unsub()\n})\n\nit('should not fire subscription when async atom promise is the same', async () => {\n  const promise = Promise.resolve()\n  const promiseAtom = atom(promise)\n  const derivedGetter = vi.fn((get: Getter) => get(promiseAtom))\n  const derivedAtom = atom(derivedGetter)\n\n  const store = createStore()\n\n  expect(derivedGetter).not.toHaveBeenCalled()\n\n  const promiseListener = vi.fn()\n  const promiseUnsub = store.sub(promiseAtom, promiseListener)\n  const derivedListener = vi.fn()\n  const derivedUnsub = store.sub(derivedAtom, derivedListener)\n\n  expect(derivedGetter).toHaveBeenCalledOnce()\n  expect(promiseListener).not.toHaveBeenCalled()\n  expect(derivedListener).not.toHaveBeenCalled()\n\n  store.get(promiseAtom)\n  store.get(derivedAtom)\n\n  expect(derivedGetter).toHaveBeenCalledOnce()\n  expect(promiseListener).not.toHaveBeenCalled()\n  expect(derivedListener).not.toHaveBeenCalled()\n\n  store.set(promiseAtom, promise)\n\n  expect(derivedGetter).toHaveBeenCalledOnce()\n  expect(promiseListener).not.toHaveBeenCalled()\n  expect(derivedListener).not.toHaveBeenCalled()\n\n  store.set(promiseAtom, promise)\n\n  expect(derivedGetter).toHaveBeenCalledOnce()\n  expect(promiseListener).not.toHaveBeenCalled()\n  expect(derivedListener).not.toHaveBeenCalled()\n\n  promiseUnsub()\n  derivedUnsub()\n})\n\nit('should notify subscription with tree dependencies (#1956)', () => {\n  const valueAtom = atom(1)\n  const dep1Atom = atom((get) => get(valueAtom) * 2)\n  const dep2Atom = atom((get) => get(valueAtom) + get(dep1Atom))\n  const dep3Atom = atom((get) => get(dep1Atom))\n\n  const cb = vi.fn()\n  const store = createStore()\n  store.sub(dep2Atom, vi.fn()) // this will cause the bug\n  store.sub(dep3Atom, cb)\n\n  expect(cb).toBeCalledTimes(0)\n  expect(store.get(dep3Atom)).toBe(2)\n  store.set(valueAtom, (c) => c + 1)\n  expect(cb).toBeCalledTimes(1)\n  expect(store.get(dep3Atom)).toBe(4)\n})\n\nit('should notify subscription with tree dependencies with bail-out', () => {\n  const valueAtom = atom(1)\n  const dep1Atom = atom((get) => get(valueAtom) * 2)\n  const dep2Atom = atom((get) => get(valueAtom) * 0)\n  const dep3Atom = atom((get) => get(dep1Atom) + get(dep2Atom))\n\n  const cb = vi.fn()\n  const store = createStore()\n  store.sub(dep1Atom, vi.fn())\n  store.sub(dep3Atom, cb)\n\n  expect(cb).toBeCalledTimes(0)\n  expect(store.get(dep3Atom)).toBe(2)\n  store.set(valueAtom, (c) => c + 1)\n  expect(cb).toBeCalledTimes(1)\n  expect(store.get(dep3Atom)).toBe(4)\n})\n\nit('should bail out with the same value with chained dependency (#2014)', () => {\n  const store = createStore()\n  const objAtom = atom({ count: 1 })\n  const countAtom = atom((get) => get(objAtom).count)\n  const deriveFn = vi.fn((get: Getter) => get(countAtom))\n  const derivedAtom = atom(deriveFn)\n  const deriveFurtherFn = vi.fn((get: Getter) => {\n    get(objAtom) // intentional extra dependency\n    return get(derivedAtom)\n  })\n  const derivedFurtherAtom = atom(deriveFurtherFn)\n  const callback = vi.fn()\n\n  store.sub(derivedFurtherAtom, callback)\n\n  expect(store.get(derivedAtom)).toBe(1)\n  expect(store.get(derivedFurtherAtom)).toBe(1)\n  expect(callback).toHaveBeenCalledTimes(0)\n  expect(deriveFn).toHaveBeenCalledTimes(1)\n  expect(deriveFurtherFn).toHaveBeenCalledTimes(1)\n\n  store.set(objAtom, (obj) => ({ ...obj }))\n  expect(callback).toHaveBeenCalledTimes(0)\n  expect(deriveFn).toHaveBeenCalledTimes(1)\n  expect(deriveFurtherFn).toHaveBeenCalledTimes(2)\n})\n\nit('should not call read function for unmounted atoms (#2076)', () => {\n  const store = createStore()\n  const countAtom = atom(1)\n  const derive1Fn = vi.fn((get: Getter) => get(countAtom))\n  const derived1Atom = atom(derive1Fn)\n  const derive2Fn = vi.fn((get: Getter) => get(countAtom))\n  const derived2Atom = atom(derive2Fn)\n\n  expect(store.get(derived1Atom)).toBe(1)\n  expect(store.get(derived2Atom)).toBe(1)\n  expect(derive1Fn).toHaveBeenCalledTimes(1)\n  expect(derive2Fn).toHaveBeenCalledTimes(1)\n\n  store.sub(derived2Atom, vi.fn())\n\n  store.set(countAtom, (c) => c + 1)\n  expect(derive1Fn).toHaveBeenCalledTimes(1)\n  expect(derive2Fn).toHaveBeenCalledTimes(2)\n})\n\nit('should update with conditional dependencies (#2084)', () => {\n  const store = createStore()\n  const f1 = atom(false)\n  const f2 = atom(false)\n  const f3 = atom(\n    (get) => get(f1) && get(f2),\n    (_get, set, val: boolean) => {\n      set(f1, val)\n      set(f2, val)\n    },\n  )\n\n  store.sub(f1, vi.fn())\n  store.sub(f2, vi.fn())\n  store.sub(f3, vi.fn())\n\n  store.set(f3, true)\n  expect(store.get(f3)).toBe(true)\n})\n\nit(\"should recompute dependents' state after onMount (#2098)\", () => {\n  const store = createStore()\n\n  const condAtom = atom(false)\n  const baseAtom = atom(false)\n  baseAtom.onMount = (set) => set(true)\n  const derivedAtom = atom(\n    (get) => get(baseAtom),\n    (_get, set, update: boolean) => set(baseAtom, update),\n  )\n  const finalAtom = atom(\n    (get) => (get(condAtom) ? get(derivedAtom) : undefined),\n    (_get, set, value: boolean) => set(derivedAtom, value),\n  )\n\n  store.sub(finalAtom, () => {}) // mounts finalAtom, but not baseAtom\n  expect(store.get(baseAtom)).toBe(false)\n  expect(store.get(derivedAtom)).toBe(false)\n  expect(store.get(finalAtom)).toBe(undefined)\n\n  store.set(condAtom, true) // mounts baseAtom\n  expect(store.get(baseAtom)).toBe(true)\n  expect(store.get(derivedAtom)).toBe(true)\n  expect(store.get(finalAtom)).toBe(true)\n\n  store.set(finalAtom, false)\n  expect(store.get(baseAtom)).toBe(false)\n  expect(store.get(derivedAtom)).toBe(false)\n  expect(store.get(finalAtom)).toBe(false)\n})\n\nit('should update derived atoms during write (#2107)', () => {\n  const store = createStore()\n\n  const baseCountAtom = atom(1)\n  const countAtom = atom(\n    (get) => get(baseCountAtom),\n    (get, set, newValue: number) => {\n      set(baseCountAtom, newValue)\n      if (get(countAtom) !== newValue) {\n        throw new Error('mismatch')\n      }\n    },\n  )\n\n  store.sub(countAtom, () => {})\n\n  expect(store.get(countAtom)).toBe(1)\n  store.set(countAtom, 2)\n  expect(store.get(countAtom)).toBe(2)\n})\n\nit('resolves dependencies reliably after a delay (#2192)', async () => {\n  expect.assertions(3)\n  const countAtom = atom(0)\n  let result: number | null = null\n\n  const asyncAtom = atom(async (get) => {\n    const count = get(countAtom)\n    await sleep(100)\n    return count\n  })\n  const derivedAtom = atom(\n    async (get, { setSelf }) => {\n      get(countAtom)\n      await sleep(50)\n      result = await get(asyncAtom)\n      if (result === 2) setSelf() // <-- necessary\n    },\n    () => {},\n  )\n  const store = createStore()\n\n  store.sub(derivedAtom, () => {})\n\n  const increment = (c: number) => c + 1\n\n  store.set(countAtom, increment)\n  store.set(countAtom, increment)\n  await vi.advanceTimersByTimeAsync(150)\n  expect(result).toBe(2)\n\n  store.set(countAtom, increment)\n  store.set(countAtom, increment)\n  await vi.advanceTimersByTimeAsync(150)\n  expect(store.get(countAtom)).toBe(4)\n  expect(result).toBe(4)\n})\n\nit('should not recompute a derived atom value if unchanged (#2168)', () => {\n  const store = createStore()\n  const countAtom = atom(1)\n  const derived1Atom = atom((get) => get(countAtom) * 0)\n  const derive2Fn = vi.fn((get: Getter) => get(derived1Atom))\n  const derived2Atom = atom(derive2Fn)\n\n  expect(store.get(derived2Atom)).toBe(0)\n  store.set(countAtom, (c) => c + 1)\n  expect(store.get(derived2Atom)).toBe(0)\n  expect(derive2Fn).toHaveBeenCalledTimes(1)\n})\n\nit('should mount once with atom creator atom (#2314)', () => {\n  const countAtom = atom(1)\n  countAtom.onMount = vi.fn((setAtom: (v: number) => void) => {\n    setAtom(2)\n  })\n  const atomCreatorAtom = atom((get) => {\n    const derivedAtom = atom((get) => get(countAtom))\n    get(derivedAtom)\n  })\n  const store = createStore()\n\n  store.sub(atomCreatorAtom, () => {})\n  expect(countAtom.onMount).toHaveBeenCalledTimes(1)\n})\n\nit('should flush pending write triggered asynchronously and indirectly (#2451)', async () => {\n  const store = createStore()\n  const anAtom = atom('initial')\n  const callbackFn = vi.fn((_value: string) => {})\n  const unsub = store.sub(anAtom, () => {\n    callbackFn(store.get(anAtom))\n  })\n  const actionAtom = atom(null, async (_get, set) => {\n    await Promise.resolve() // waiting a microtask\n    set(indirectSetAtom)\n  })\n  const indirectSetAtom = atom(null, (_get, set) => {\n    set(anAtom, 'next')\n  })\n\n  // executing the chain reaction\n  await store.set(actionAtom)\n  expect(callbackFn).toHaveBeenCalledExactlyOnceWith('next')\n  unsub()\n})\n\ndescribe('async atom with subtle timing', () => {\n  it('case 1', async () => {\n    const store = createStore()\n    const a = atom(1)\n    const b = atom(async (get) => {\n      await sleep(100)\n      return get(a)\n    })\n    const bValue = store.get(b)\n\n    store.set(a, 2)\n    const bValue2 = store.get(b)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(await bValue).toBe(2)\n    expect(await bValue2).toBe(2)\n  })\n\n  it('case 2', async () => {\n    const store = createStore()\n    const a = atom(1)\n    const b = atom(async (get) => {\n      const aValue = get(a)\n      await sleep(100)\n      return aValue\n    })\n    const bValue = store.get(b)\n\n    store.set(a, 2)\n    const bValue2 = store.get(b)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(await bValue).toBe(1) // returns old value\n    expect(await bValue2).toBe(2)\n  })\n})\n\ndescribe('aborting atoms', () => {\n  // We can't use signal.throwIfAborted as it is not available\n  // in earlier versions of TS that this is tested on.\n  const throwIfAborted = (signal: AbortSignal) => {\n    if (signal.aborted) {\n      throw new Error('aborted')\n    }\n  }\n\n  it('should abort the signal when dependencies change', async () => {\n    const a = atom(1)\n    const callBeforeAbort = vi.fn()\n    const callAfterAbort = vi.fn()\n    const store = createStore()\n    const derivedAtom = atom(async (get, { signal }) => {\n      const aVal = get(a)\n      await sleep(100)\n      callBeforeAbort()\n      throwIfAborted(signal)\n      callAfterAbort()\n      return aVal + 1\n    })\n\n    const promise = store.get(derivedAtom)\n    store.set(a, 3)\n    const promise2 = store.get(derivedAtom)\n    await vi.advanceTimersByTimeAsync(100)\n    await expect(promise).rejects.toThrow('aborted')\n    await expect(promise2).resolves.toEqual(4)\n    expect(callBeforeAbort).toHaveBeenCalledTimes(2)\n    expect(callAfterAbort).toHaveBeenCalledTimes(1)\n  })\n\n  it('should abort the signal when dependencies change and the atom is mounted', async () => {\n    const a = atom(1)\n    const callBeforeAbort = vi.fn()\n    const callAfterAbort = vi.fn()\n    const store = createStore()\n    const derivedAtom = atom(async (get, { signal }) => {\n      const aVal = get(a)\n      await sleep(100)\n      callBeforeAbort()\n      throwIfAborted(signal)\n      callAfterAbort()\n      return aVal + 1\n    })\n\n    store.sub(derivedAtom, () => {})\n\n    store.set(a, 3)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(callBeforeAbort).toHaveBeenCalledTimes(2)\n    expect(callAfterAbort).toHaveBeenCalledTimes(1)\n  })\n\n  it('should not abort the signal when unsubscribed', async () => {\n    const a = atom(1)\n    const callBeforeAbort = vi.fn()\n    const callAfterAbort = vi.fn()\n    const store = createStore()\n    const derivedAtom = atom(async (get, { signal }) => {\n      const aVal = get(a)\n      await sleep(100)\n      callBeforeAbort()\n      throwIfAborted(signal)\n      callAfterAbort()\n      return aVal + 1\n    })\n\n    const unsub = store.sub(derivedAtom, () => {})\n    unsub()\n\n    await vi.advanceTimersByTimeAsync(100)\n    expect(await store.get(derivedAtom)).toEqual(2)\n    expect(callBeforeAbort).toHaveBeenCalledTimes(1)\n    expect(callAfterAbort).toHaveBeenCalledTimes(1)\n  })\n})\n\nit('Unmount an atom that is no longer dependent within a derived atom (#2658)', () => {\n  const condAtom = atom(true)\n  const baseAtom = atom(0)\n  const onUnmount = vi.fn()\n  baseAtom.onMount = () => onUnmount\n  const derivedAtom = atom((get) => {\n    if (get(condAtom)) get(baseAtom)\n  })\n  const store = createStore()\n\n  store.sub(derivedAtom, () => {})\n\n  store.set(condAtom, false)\n  expect(onUnmount).toHaveBeenCalledTimes(1)\n})\n\nit('should update derived atom even if dependencies changed (#2697)', () => {\n  const primitiveAtom = atom<number | undefined>(undefined)\n  const derivedAtom = atom((get) => get(primitiveAtom))\n  const conditionalAtom = atom((get) => {\n    const base = get(primitiveAtom)\n    if (!base) return\n    return get(derivedAtom)\n  })\n  const store = createStore()\n  const onChangeDerived = vi.fn()\n\n  store.sub(derivedAtom, onChangeDerived)\n  store.sub(conditionalAtom, () => {})\n\n  expect(onChangeDerived).toHaveBeenCalledTimes(0)\n  store.set(primitiveAtom, 1)\n  expect(onChangeDerived).toHaveBeenCalledTimes(1)\n})\n\ndescribe('should invoke flushPending only after all atoms are updated (#2804)', () => {\n  const store = createStore()\n\n  it('should invoke flushPending only after all atoms are updated with set', () => {\n    const a = atom(0)\n    const setResult = []\n    const w = atom(null, (_get, set, value: number) => {\n      setResult.push('before set')\n      set(a, value)\n      setResult.push('after set')\n    })\n\n    store.sub(a, () => {\n      setResult.push('a value changed - ' + store.get(a))\n    })\n    setResult.push('before store.set')\n    store.set(w, 1)\n    setResult.push('after store.set')\n    expect(setResult).not.toEqual([\n      'before store.set',\n      'before set',\n      'a value changed - 1',\n      'after set',\n      'after store.set',\n    ])\n    expect(setResult).toEqual([\n      'before store.set',\n      'before set',\n      'after set',\n      'a value changed - 1',\n      'after store.set',\n    ])\n  })\n\n  it('should invoke flushPending only after all atoms are updated with mount', () => {\n    const mountResult = []\n    const a = atom(0)\n    const m = atom(null, (_get, set, value: number) => {\n      set(a, value)\n    })\n    m.onMount = (setSelf) => {\n      mountResult.push('before onMount setSelf')\n      setSelf(1)\n      mountResult.push('after onMount setSelf')\n    }\n    mountResult.push('before store.sub')\n    store.sub(a, () => {\n      mountResult.push('a value changed - ' + store.get(a))\n    })\n\n    store.sub(m, () => {})\n\n    mountResult.push('after store.sub')\n    expect(mountResult).not.toEqual([\n      'before store.sub',\n      'before onMount setSelf',\n      'a value changed - 1',\n      'after onMount setSelf',\n      'after store.sub',\n    ])\n    expect(mountResult).toEqual([\n      'before store.sub',\n      'before onMount setSelf',\n      'after onMount setSelf',\n      'a value changed - 1',\n      'after store.sub',\n    ])\n  })\n\n  it('should flush only after all atoms are updated with unmount', () => {\n    const result: string[] = []\n    const a = atom(0)\n    const b = atom(null, (_get, set, value: number) => {\n      set(a, value)\n    })\n    b.onMount = (setAtom) => {\n      return () => {\n        result.push('onUmount: before setAtom')\n        setAtom(1)\n        result.push('onUmount: after setAtom')\n      }\n    }\n    const c = atom(true)\n    const d = atom((get) => get(c) && get(b))\n\n    store.sub(a, () => {\n      result.push('a value changed - ' + store.get(a))\n    })\n    store.sub(d, () => {})\n\n    expect(store.get(d)).toEqual(null)\n    store.set(c, false)\n    expect(result).toEqual([\n      'onUmount: before setAtom',\n      'onUmount: after setAtom',\n      'a value changed - 1',\n    ])\n  })\n})\n\ndescribe('should mount and trigger listeners even when an error is thrown', () => {\n  it('in asynchronous read', async () => {\n    const store = createStore()\n    const a = atom(0)\n    a.onMount = vi.fn()\n    const e = atom(\n      () => {\n        throw new Error('error')\n      },\n      () => {},\n    )\n    e.onMount = vi.fn()\n    const b = atom((get) => {\n      setTimeout(() => {\n        get(a)\n        try {\n          get(e)\n        } catch {\n          // expect error\n        }\n      })\n    })\n\n    store.sub(b, () => {})\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(a.onMount).toHaveBeenCalledOnce()\n    expect(e.onMount).toHaveBeenCalledOnce()\n  })\n\n  it('in read setSelf', async () => {\n    const store = createStore()\n    const a = atom(0)\n    const e = atom(\n      () => {\n        throw new Error('error')\n      },\n      () => {},\n    )\n    const b = atom(\n      (_, { setSelf }) => {\n        setTimeout(() => {\n          try {\n            setSelf()\n          } catch {\n            // expect error\n          }\n        })\n      },\n      (get, set) => {\n        set(a, 1)\n        get(e)\n      },\n    )\n    const listener = vi.fn()\n\n    store.sub(a, listener)\n    store.sub(b, () => {})\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(listener).toHaveBeenCalledOnce()\n  })\n\n  it('in read promise on settled', async () => {\n    const store = createStore()\n    const a = atom(0)\n    a.onMount = vi.fn()\n    const e = atom(\n      () => {\n        throw new Error('error')\n      },\n      () => {},\n    )\n    const b = atom(async (get) => {\n      await sleep(0)\n      get(a)\n      get(e)\n    })\n\n    store.sub(b, () => {})\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(a.onMount).toHaveBeenCalledOnce()\n  })\n\n  it('in asynchronous write', async () => {\n    const store = createStore()\n    const a = atom(0)\n    const e = atom(() => {\n      throw new Error('error')\n    })\n    const b = atom(null, (get, set) => {\n      set(a, 1)\n      get(e)\n    })\n    const w = atom(null, async (_get, set) => {\n      setTimeout(() => {\n        try {\n          set(b)\n        } catch {\n          // expect error\n        }\n      })\n    })\n    const listener = vi.fn()\n\n    store.sub(a, listener)\n    store.set(w)\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(listener).toHaveBeenCalledOnce()\n  })\n\n  it('in synchronous write', () => {\n    const store = createStore()\n    const a = atom(0)\n    const e = atom(() => {\n      throw new Error('error')\n    })\n    const b = atom(null, (get, set) => {\n      set(a, 1)\n      get(e)\n    })\n    const listener = vi.fn()\n    store.sub(a, listener)\n    try {\n      store.set(b)\n    } catch {\n      // expect error\n    }\n\n    expect(listener).toHaveBeenCalledOnce()\n  })\n\n  it('in onmount/onunmount asynchronous setAtom', async () => {\n    const store = createStore()\n    const a = atom(0)\n    const e = atom(() => {\n      throw new Error('error')\n    })\n    const b = atom(null, (get, set) => {\n      set(a, (v) => ++v)\n      get(e)\n    })\n    b.onMount = (setAtom) => {\n      setTimeout(() => {\n        try {\n          setAtom()\n        } catch {\n          // expect error\n        }\n      })\n      return () => {\n        setTimeout(() => {\n          try {\n            setAtom()\n          } catch {\n            // expect error\n          }\n        })\n      }\n    }\n    const listener = vi.fn()\n\n    store.sub(a, listener)\n    const unsub = store.sub(b, () => {})\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(listener).toHaveBeenCalledOnce()\n\n    listener.mockClear()\n    unsub()\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(listener).toHaveBeenCalledOnce()\n  })\n\n  it('in synchronous onmount', () => {\n    const store = createStore()\n    const a = atom(0)\n    const aUnmount = vi.fn()\n    a.onMount = vi.fn(() => aUnmount)\n    const b = atom(\n      (get) => get(a),\n      () => {},\n    )\n    b.onMount = () => {\n      throw new Error('error')\n    }\n    try {\n      store.sub(b, () => {})\n    } catch {\n      // expect error\n    }\n\n    expect(a.onMount).toHaveBeenCalledOnce()\n  })\n\n  it('in synchronous onunmount', () => {\n    const store = createStore()\n    const a = atom(0)\n    const aUnmount = vi.fn()\n    a.onMount = () => aUnmount\n    const b = atom(\n      (get) => get(a),\n      () => {},\n    )\n    b.onMount = () => () => {\n      throw new Error('error')\n    }\n    const unsub = store.sub(b, () => {})\n    try {\n      unsub()\n    } catch {\n      // expect error\n    }\n\n    expect(aUnmount).toHaveBeenCalledOnce()\n  })\n\n  it('in synchronous listener', () => {\n    const store = createStore()\n    const a = atom(0)\n    const e = atom(0)\n    const b = atom(null, (_, set) => {\n      set(a, 1)\n      set(e, 1)\n    })\n    store.sub(e, () => {\n      throw new Error('error')\n    })\n    const listener = vi.fn()\n    store.sub(a, listener)\n    try {\n      store.set(b)\n    } catch {\n      // expect error\n    }\n\n    expect(listener).toHaveBeenCalledOnce()\n  })\n})\n\nit('throws falsy errors in onMount, onUnmount, and listeners', () => {\n  const store = createStore()\n  const a = atom(0)\n  a.onMount = () => {\n    throw ''\n  }\n  expect(() => store.sub(a, () => {})).toThrow('')\n  const b = atom(0)\n  b.onMount = () => () => {\n    throw ''\n  }\n  const unsub = store.sub(b, () => {})\n  expect(() => unsub()).toThrow('')\n  const c = atom(0)\n\n  store.sub(c, () => {\n    throw ''\n  })\n\n  expect(() => store.set(c, 1)).toThrow('')\n})\n\nit('should use the correct pending on unmount', () => {\n  const store = createStore()\n  const a = atom(0)\n  const b = atom(0, (_, set, update: number) => set(a, update))\n  b.onMount = (setAtom) => () => setAtom(1)\n  const aListener = vi.fn()\n\n  store.sub(a, aListener)\n  const unsub = store.sub(b, () => {})\n  aListener.mockClear()\n  unsub()\n\n  expect(store.get(a)).toBe(1)\n  expect(aListener).toHaveBeenCalledTimes(1)\n})\n\nit('should call subscribers after setAtom updates atom value on mount but not on unmount', () => {\n  const store = createStore()\n  const a = atom(0)\n  let unmount\n  a.onMount = vi.fn(((setAtom) => {\n    setAtom(1)\n    unmount = vi.fn(() => {\n      setAtom(2)\n    })\n    return unmount\n  }) as NonNullable<(typeof a)['onMount']>)\n  const listener = vi.fn()\n\n  const unsub = store.sub(a, listener)\n  expect(store.get(a)).toBe(1)\n  expect(a.onMount).toHaveBeenCalledTimes(1)\n  expect(listener).toHaveBeenCalledTimes(1)\n\n  listener.mockClear()\n  unsub()\n\n  expect(store.get(a)).toBe(2)\n  expect(unmount).toHaveBeenCalledTimes(1)\n  expect(listener).toHaveBeenCalledTimes(0)\n})\n\nit('processes deep atom a graph beyond maxDepth', () => {\n  function getMaxDepth() {\n    let depth = 0\n    function d(): number {\n      ++depth\n      try {\n        return d()\n      } catch {\n        return depth\n      }\n    }\n    return d()\n  }\n  const maxDepth = getMaxDepth()\n  const store = createStore()\n  const baseAtom = atom(0)\n  const atoms: [PrimitiveAtom<number>, ...Atom<number>[]] = [baseAtom]\n  Array.from({ length: maxDepth }, (_, i) => {\n    const prevAtom = atoms[i]!\n    const a = atom((get) => get(prevAtom))\n    atoms.push(a)\n  })\n  const lastAtom = atoms[maxDepth]!\n  // store.get(lastAtom) // FIXME: This is causing a stack overflow\n  expect(() => store.sub(lastAtom, () => {})).not.toThrow()\n  // store.get(lastAtom) // FIXME: This is causing a stack overflow\n  expect(() => store.set(baseAtom, 1)).not.toThrow()\n  // store.set(lastAtom) // FIXME: This is causing a stack overflow\n}, 10_000)\n\nit('mounted atom should be recomputed eagerly', () => {\n  const result: string[] = []\n  const a = atom(0)\n  const b = atom((get) => {\n    result.push('bRead')\n    return get(a)\n  })\n  const store = createStore()\n\n  store.sub(a, () => {\n    result.push('aCallback')\n  })\n  store.sub(b, () => {\n    result.push('bCallback')\n  })\n\n  expect(result).toEqual(['bRead'])\n  result.splice(0)\n  store.set(a, 1)\n  expect(result).toEqual(['bRead', 'aCallback', 'bCallback'])\n})\n\nit('should notify subscription even with reading atom in write', () => {\n  const a = atom(1)\n  const b = atom((get) => get(a) * 2)\n  const c = atom((get) => get(b) + 1)\n  const d = atom(null, (get, set) => {\n    set(a, 2)\n    get(b)\n  })\n  const store = createStore()\n  const callback = vi.fn()\n\n  store.sub(c, callback)\n  store.set(d)\n  expect(callback).toHaveBeenCalledTimes(1)\n})\n\nit('should process all atom listeners even if some of them throw errors', () => {\n  const store = createStore()\n  const a = atom(0)\n  const listenerA = vi.fn()\n  const listenerB = vi.fn(() => {\n    throw new Error('error')\n  })\n  const listenerC = vi.fn()\n\n  store.sub(a, listenerA)\n  store.sub(a, listenerB)\n  store.sub(a, listenerC)\n  try {\n    store.set(a, 1)\n  } catch {\n    // expect empty\n  }\n\n  expect(listenerA).toHaveBeenCalledTimes(1)\n  expect(listenerB).toHaveBeenCalledTimes(1)\n  expect(listenerC).toHaveBeenCalledTimes(1)\n})\n\nit('should call onInit only once per atom', () => {\n  const store = createStore()\n  const a = atom(0)\n  const onInit = vi.fn()\n\n  a.INTERNAL_onInit = onInit\n  store.get(a)\n  expect(onInit).toHaveBeenCalledTimes(1)\n  expect(onInit).toHaveBeenCalledWith(store)\n\n  onInit.mockClear()\n  store.get(a)\n  store.set(a, 1)\n\n  const unsub = store.sub(a, () => {})\n  unsub()\n\n  const b = atom((get) => get(a))\n  store.get(b)\n  store.sub(b, () => {})\n  expect(onInit).not.toHaveBeenCalled()\n})\n\nit('should call onInit only once per store', () => {\n  const a = atom(0)\n  const aOnInit = vi.fn()\n  a.INTERNAL_onInit = aOnInit\n  const b = atom(0)\n  const bOnInit = vi.fn()\n  b.INTERNAL_onInit = bOnInit\n  type Store = ReturnType<typeof createStore>\n  function testInStore(store: Store) {\n    store.get(a)\n    store.get(b)\n    expect(aOnInit).toHaveBeenCalledTimes(1)\n    expect(bOnInit).toHaveBeenCalledTimes(1)\n    aOnInit.mockClear()\n    bOnInit.mockClear()\n    return store\n  }\n  testInStore(createStore())\n  const store = testInStore(createStore())\n  testInStore(\n    deriveStore(store, (atomStateMap) => {\n      const initializedAtoms = new WeakSet()\n      return {\n        get: (atom) => {\n          if (!initializedAtoms.has(atom)) {\n            return undefined\n          }\n          return atomStateMap.get(atom)\n        },\n        set: (atom, atomState) => {\n          initializedAtoms.add(atom)\n          atomStateMap.set(atom, atomState)\n        },\n        has: (atom) => {\n          if (!initializedAtoms.has(atom)) {\n            return false\n          }\n          return atomStateMap.has(atom)\n        },\n        delete: (atom) => {\n          initializedAtoms.delete(atom)\n          return atomStateMap.delete(atom)\n        },\n      }\n    }) as Store,\n  )\n})\n\nit('should pass store and atomState to the atom initializer', () => {\n  expect.assertions(1)\n\n  const store = createStore()\n  const a = atom(null)\n\n  a.INTERNAL_onInit = (store) => {\n    expect(store).toBe(store)\n  }\n  store.get(a)\n})\n\nit('recomputes dependents of unmounted atoms', () => {\n  const a = atom(0)\n  const bRead = vi.fn((get: Getter) => {\n    return get(a)\n  })\n  const b = atom(bRead)\n  const c = atom((get) => get(b))\n  const w = atom(null, (get, set) => {\n    set(a, 1)\n    get(c)\n    set(a, 2)\n    bRead.mockClear()\n  })\n  const store = createStore()\n\n  store.set(w)\n  expect(bRead).not.toHaveBeenCalled()\n})\n\nit('recomputes all changed atom dependents together', () => {\n  const a = atom([0])\n  const b = atom([0])\n  const a0 = atom((get) => get(a)[0]!)\n  const b0 = atom((get) => get(b)[0]!)\n  const a0b0 = atom((get) => [get(a0), get(b0)])\n  const w = atom(null, (_, set) => {\n    set(a, [0])\n    set(b, [1])\n  })\n  const store = createStore()\n\n  store.sub(a0b0, () => {})\n  store.set(w)\n  expect(store.get(a0)).toBe(0)\n  expect(store.get(b0)).toBe(1)\n  expect(store.get(a0b0)).toEqual([0, 1])\n})\n\nit('should not inf on subscribe or unsubscribe', () => {\n  const store = createStore()\n  const countAtom = atom(0)\n  const effectAtom = atom(\n    (get) => get(countAtom),\n    (_, set) => set,\n  )\n  effectAtom.onMount = (setAtom) => {\n    const set = setAtom()\n    set(countAtom, 1)\n    return () => {\n      set(countAtom, 2)\n    }\n  }\n\n  const unsub = store.sub(effectAtom, () => {})\n  expect(store.get(countAtom)).toBe(1)\n  unsub()\n  expect(store.get(countAtom)).toBe(2)\n})\n\nit('supports recursion in an atom subscriber', () => {\n  const a = atom(0)\n  const store = createStore()\n\n  store.sub(a, () => {\n    if (store.get(a) < 3) {\n      store.set(a, (v) => v + 1)\n    }\n  })\n  store.set(a, 1)\n  expect(store.get(a)).toBe(3)\n})\n\nit('allows subscribing to atoms during mount', () => {\n  const store = createStore()\n  const a = atom(0)\n  a.onMount = () => {\n    store.sub(b, () => {})\n  }\n  const b = atom(0)\n  let bMounted = false\n  b.onMount = () => {\n    bMounted = true\n  }\n\n  store.sub(a, () => {})\n  expect(bMounted).toBe(true)\n})\n\nit('updates with reading derived atoms (#2959)', () => {\n  const store = createStore()\n  const countAtom = atom(0)\n  const countDerivedAtom = atom((get) => get(countAtom))\n  const countUpAtom = atom(null, (get, set) => {\n    set(countAtom, 1)\n    get(countDerivedAtom)\n    set(countAtom, 2)\n  })\n\n  store.sub(countDerivedAtom, () => {})\n  store.set(countUpAtom)\n  expect(store.get(countDerivedAtom)).toBe(2)\n})\n\nit('updates dependents when it eagerly recomputes dirty atoms', () => {\n  const countAtom = atom(0)\n  const isActiveAtom = atom(false)\n  const activeCountAtom = atom((get) =>\n    get(isActiveAtom) ? get(countAtom) : undefined,\n  )\n  const activateAction = atom(null, (get, set, value: boolean) => {\n    set(isActiveAtom, value)\n    get(activeCountAtom)\n  })\n\n  const store = createStore()\n  store.sub(activeCountAtom, () => {})\n  store.set(activateAction, true)\n  store.set(countAtom, 1)\n\n  expect(store.get(activeCountAtom)).toBe(1)\n})\n\nit('[DEV-ONLY] should warn store mutation during read', () => {\n  const store = createStore()\n  const countAtom = atom(0)\n  const derivedAtom = atom(() => {\n    store.set(countAtom, (c) => c + 1)\n  })\n  store.get(derivedAtom)\n  expect(console.warn).toHaveBeenCalledWith(\n    'Detected store mutation during atom read. This is not supported.',\n  )\n})\n\nit('should keep reactivity when a derived atom returns a function that calls get (#3240)', () => {\n  const store = createStore()\n  const stableAtom = atom(0)\n  const closureAtom = atom((get) => (x: number) => {\n    const s = get(stableAtom)\n    return x + s\n  })\n  const changingAtom = atom(0)\n  const upstreamAtom = atom((get) => {\n    const n = get(changingAtom)\n    const fn = get(closureAtom)\n    return fn(n)\n  })\n  const downstreamAtom = atom((get) => get(upstreamAtom) * 2)\n\n  const callback = vi.fn()\n  store.sub(downstreamAtom, callback)\n\n  expect(store.get(upstreamAtom)).toBe(0)\n  expect(store.get(downstreamAtom)).toBe(0)\n\n  store.set(changingAtom, 1)\n  expect(store.get(upstreamAtom)).toBe(1)\n  expect(store.get(downstreamAtom)).toBe(2)\n  expect(callback).toHaveBeenCalledTimes(1)\n\n  store.set(changingAtom, 2)\n  expect(store.get(upstreamAtom)).toBe(2)\n  expect(store.get(downstreamAtom)).toBe(4)\n  expect(callback).toHaveBeenCalledTimes(2)\n})\n\n// Regression (v2.12.1+, commit f5d843c): when a derived atom's read calls store.set\n// (e.g. via a write-only atom), that atom's subscribers are not notified on dependency\n// changes — store.get(atom) is correct but the subscription callback never runs.\n// Repro: counterAtom, write-only queryAtom, dataAtom = get => query(get(counterAtom)).\n// https://github.com/koutaro-masaki/jotai-atom-in-component-with-usesetatom\nit('notifies derived-atom subscriber when read calls store.set', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const queryAtom = atom(null, (_get, _set, v: number) => v)\n  const dataAtom = atom((get) => {\n    const v = get(counterAtom)\n    const result = store.set(queryAtom, v * 2)\n    return result\n  })\n\n  const dataListener = vi.fn()\n  store.sub(dataAtom, dataListener)\n\n  expect(store.get(dataAtom)).toBe(0)\n  expect(dataListener.mock.calls.length).toBe(0)\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(dataAtom)).toBe(2)\n  expect(dataListener.mock.calls.length).toBe(1)\n\n  store.set(counterAtom, 2)\n\n  expect(store.get(dataAtom)).toBe(4)\n  expect(dataListener.mock.calls.length).toBe(2)\n})\n\nit('notifies subscriber through chain of derived atoms when root calls store.set', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const queryAtom = atom(null, (_get, _set, v: number) => v)\n  const baseAtom = atom((get) => {\n    const v = get(counterAtom)\n    return store.set(queryAtom, v * 2)\n  })\n  const derivedAtom = atom((get) => get(baseAtom) * 2)\n\n  const derivedListener = vi.fn()\n  store.sub(derivedAtom, derivedListener)\n\n  expect(store.get(derivedAtom)).toBe(0)\n  expect(derivedListener.mock.calls.length).toBe(0)\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(derivedAtom)).toBe(4)\n  expect(derivedListener.mock.calls.length).toBe(1)\n})\n\nit('notifies subscriber when nested write uses get to read atom with store.set', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const innerQueryAtom = atom(null, (_get, _set, v: number) => v)\n  const middleAtom = atom((get) => {\n    const v = get(counterAtom)\n    return store.set(innerQueryAtom, v * 3)\n  })\n  const outerQueryAtom = atom(null, (get, _set, v: number) => {\n    const m = get(middleAtom)\n    return v + m\n  })\n  const dataAtom = atom((get) => {\n    const v = get(counterAtom)\n    return store.set(outerQueryAtom, v * 2)\n  })\n\n  const dataListener = vi.fn()\n  store.sub(dataAtom, dataListener)\n\n  expect(store.get(dataAtom)).toBe(0)\n  expect(dataListener.mock.calls.length).toBe(0)\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(dataAtom)).toBe(5)\n  expect(dataListener.mock.calls.length).toBe(1)\n\n  store.set(counterAtom, 2)\n\n  expect(store.get(dataAtom)).toBe(10)\n  expect(dataListener.mock.calls.length).toBe(2)\n})\n\nit('notifies async derived-atom subscriber when read calls store.set before await', async () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const queryAtom = atom(null, (_get, _set, v: number) => v)\n  const dataAtom = atom(async (get) => {\n    const v = get(counterAtom)\n    const result = store.set(queryAtom, v * 2)\n    await sleep(0)\n    return result\n  })\n\n  let lastValue: number | Promise<number> | undefined\n  store.sub(dataAtom, () => {\n    lastValue = store.get(dataAtom)\n  })\n\n  await vi.advanceTimersByTimeAsync(10)\n  expect(await store.get(dataAtom)).toBe(0)\n\n  store.set(counterAtom, 1)\n  await vi.advanceTimersByTimeAsync(10)\n  expect(await lastValue).toBe(2)\n\n  store.set(counterAtom, 2)\n  await vi.advanceTimersByTimeAsync(10)\n  expect(await lastValue).toBe(4)\n})\n\nit('notifies subscriber normally when store.set is in write function, not read', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const innerQueryAtom = atom(null, (_get, _set, v: number) => v)\n  const queryAtom = atom(null, (_get, _set, v: number) =>\n    store.set(innerQueryAtom, v),\n  )\n  const dataAtom = atom((get) => {\n    const v = get(counterAtom)\n    const result = store.set(queryAtom, v * 2)\n    return result\n  })\n\n  const dataListener = vi.fn()\n  store.sub(dataAtom, dataListener)\n\n  expect(store.get(dataAtom)).toBe(0)\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(dataAtom)).toBe(2)\n  expect(dataListener.mock.calls.length).toBe(1)\n\n  store.set(counterAtom, 2)\n\n  expect(store.get(dataAtom)).toBe(4)\n  expect(dataListener.mock.calls.length).toBe(2)\n})\n\nit('store.set before get(dep) causes deep recursion but recovers', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const queryAtom = atom(null, (_get, _set, v: number) => v)\n  const dataAtom = atom((get) => {\n    const result = store.set(queryAtom, 1)\n    const v = get(counterAtom)\n    return result + v\n  })\n\n  const dataListener = vi.fn()\n  store.sub(dataAtom, dataListener)\n\n  expect(store.get(dataAtom)).toBe(1)\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(dataAtom)).toBe(2)\n  expect(dataListener.mock.calls.length).toBeGreaterThanOrEqual(1)\n})\n\nit('notifies subscriber when read calls store.set multiple times', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const query1 = atom(null, (_get, _set, v: number) => v)\n  const query2 = atom(null, (_get, _set, v: number) => v * 10)\n  const dataAtom = atom((get) => {\n    const v = get(counterAtom)\n    const r1 = store.set(query1, v)\n    const r2 = store.set(query2, v)\n    return r1 + r2\n  })\n\n  const dataListener = vi.fn()\n  store.sub(dataAtom, dataListener)\n\n  expect(store.get(dataAtom)).toBe(0)\n  expect(dataListener.mock.calls.length).toBe(0)\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(dataAtom)).toBe(11)\n  expect(dataListener.mock.calls.length).toBe(1)\n\n  store.set(counterAtom, 2)\n\n  expect(store.get(dataAtom)).toBe(22)\n  expect(dataListener.mock.calls.length).toBe(2)\n})\n\nit('does not recompute derived atom redundantly when store.set in read uses return value without setting atoms', () => {\n  const store = createStore()\n  const counterAtom = atom(0)\n  const queryAtom = atom(null, (_get, _set, v: number) => v)\n\n  let baseReadCount = 0\n  const baseAtom = atom((get) => {\n    baseReadCount++\n    const v = get(counterAtom)\n    return store.set(queryAtom, v * 2)\n  })\n  const baseAtom2 = atom((get) => {\n    const v = get(counterAtom)\n    return store.set(queryAtom, v * 2)\n  })\n\n  let derivedReadCount = 0\n  const derivedAtom = atom((get) => {\n    derivedReadCount++\n    get(baseAtom)\n    get(baseAtom2)\n    return get(counterAtom)\n  })\n\n  const listener = vi.fn()\n  store.sub(derivedAtom, listener)\n\n  expect(store.get(derivedAtom)).toBe(0)\n  expect(baseReadCount).toBe(1)\n  expect(derivedReadCount).toBe(1)\n\n  baseReadCount = 0\n  derivedReadCount = 0\n\n  store.set(counterAtom, 1)\n\n  expect(store.get(derivedAtom)).toBe(1)\n  expect(listener).toHaveBeenCalledTimes(1)\n  expect(baseReadCount).toBe(1)\n  expect(derivedReadCount).not.toBe(3)\n  expect(derivedReadCount).toBe(1)\n})\n"
  },
  {
    "path": "tests/vanilla/storedev.test.tsx",
    "content": "import { describe, expect, it, vi } from 'vitest'\nimport { atom } from 'jotai/vanilla'\nimport type { Atom, WritableAtom } from 'jotai/vanilla'\nimport {\n  INTERNAL_buildStoreRev2 as INTERNAL_buildStore,\n  INTERNAL_initializeStoreHooksRev2 as INTERNAL_initializeStoreHooks,\n} from 'jotai/vanilla/internals'\nimport type {\n  INTERNAL_AtomState,\n  INTERNAL_Store,\n} from 'jotai/vanilla/internals'\n\ntype DevStore = {\n  get_internal_weak_map: () => {\n    get: (atom: Atom<unknown>) => INTERNAL_AtomState | undefined\n  }\n  get_mounted_atoms: () => Set<Atom<unknown>>\n  restore_atoms: (values: Iterable<readonly [Atom<unknown>, unknown]>) => void\n}\n\nconst createDevStore = (): INTERNAL_Store & DevStore => {\n  let inRestoreAtom = 0\n  const storeHooks = INTERNAL_initializeStoreHooks({})\n  const atomStateMap = new WeakMap()\n  const mountedAtoms = new WeakMap()\n  const store = INTERNAL_buildStore(\n    atomStateMap,\n    mountedAtoms,\n    undefined,\n    undefined,\n    undefined,\n    undefined,\n    storeHooks,\n    undefined,\n    (_store, atom, get, set, ...args) => {\n      if (inRestoreAtom) {\n        return set(atom, ...(args as any))\n      }\n      return atom.write(get, set, ...(args as any))\n    },\n  )\n  const debugMountedAtoms = new Set<Atom<unknown>>()\n  storeHooks.m.add(undefined, (atom) => {\n    debugMountedAtoms.add(atom)\n    const atomState = atomStateMap.get(atom)\n    // For DevStoreRev4 compatibility\n    ;(atomState as any).m = mountedAtoms.get(atom)\n  })\n  storeHooks.u.add(undefined, (atom) => {\n    debugMountedAtoms.delete(atom)\n    const atomState = atomStateMap.get(atom)\n    // For DevStoreRev4 compatibility\n    delete (atomState as any).m\n  })\n  const devStore: DevStore = {\n    // store dev methods (these are tentative and subject to change without notice)\n    get_internal_weak_map: () => atomStateMap,\n    get_mounted_atoms: () => debugMountedAtoms,\n    restore_atoms: (values) => {\n      const restoreAtom: WritableAtom<null, [], void> = {\n        read: () => null,\n        write: (_get, set) => {\n          ++inRestoreAtom\n          try {\n            for (const [atom, value] of values) {\n              if ('init' in atom) {\n                set(atom as never, value)\n              }\n            }\n          } finally {\n            --inRestoreAtom\n          }\n        },\n      }\n      store.set(restoreAtom)\n    },\n  }\n  return Object.assign(store, devStore)\n}\n\ndescribe('dev-only methods', () => {\n  it('should get atom value', () => {\n    const store = createDevStore()\n    const countAtom = atom(0)\n    store.set(countAtom, 1)\n    const weakMap = store.get_internal_weak_map()\n    expect(weakMap.get(countAtom)?.v).toEqual(1)\n  })\n\n  it('should restore atoms and its dependencies correctly', () => {\n    const store = createDevStore()\n    const countAtom = atom(0)\n    const derivedAtom = atom((get) => get(countAtom) * 2)\n    store.set(countAtom, 1)\n    store.restore_atoms([[countAtom, 2]])\n    expect(store.get(countAtom)).toBe(2)\n    expect(store.get?.(derivedAtom)).toBe(4)\n  })\n\n  it('should restore atoms and call store listeners correctly', () => {\n    const store = createDevStore()\n    const countAtom = atom(0)\n    const derivedAtom = atom((get) => get(countAtom) * 2)\n    const countCb = vi.fn()\n    const derivedCb = vi.fn()\n    store.set(countAtom, 2)\n    const unsubCount = store.sub(countAtom, countCb)\n    const unsubDerived = store.sub(derivedAtom, derivedCb)\n    store.restore_atoms([\n      [countAtom, 1],\n      [derivedAtom, 2],\n    ])\n\n    expect(countCb).toHaveBeenCalled()\n    expect(derivedCb).toHaveBeenCalled()\n    unsubCount()\n    unsubDerived()\n  })\n\n  it('should return all the mounted atoms correctly', () => {\n    const store = createDevStore()\n    const countAtom = atom(0)\n    const derivedAtom = atom((get) => get(countAtom) * 2)\n    const unsub = store.sub(derivedAtom, vi.fn())\n    store.set(countAtom, 1)\n    const result = store.get_mounted_atoms()\n    expect(\n      Array.from(result)\n        .sort((a, b) => Object.keys(a).length - Object.keys(b).length)\n        .map((item) => {\n          const { debugLabel: _, ...rest } = item\n          return rest\n        }),\n    ).toStrictEqual([\n      { toString: expect.any(Function), read: expect.any(Function) },\n      {\n        toString: expect.any(Function),\n        init: 0,\n        read: expect.any(Function),\n        write: expect.any(Function),\n      },\n    ])\n    unsub()\n  })\n\n  it(\"should return all the mounted atoms correctly after they're unsubscribed\", () => {\n    const store = createDevStore()\n    const countAtom = atom(0)\n    const derivedAtom = atom((get) => get(countAtom) * 2)\n    const unsub = store.sub(derivedAtom, vi.fn())\n    store.set(countAtom, 1)\n    unsub()\n    const result = store.get_mounted_atoms()\n    expect(Array.from(result)).toStrictEqual([])\n  })\n\n  it('should restore atoms with custom write function', () => {\n    const store = createDevStore()\n    const countAtom = atom(0, () => {})\n    store.restore_atoms([[countAtom, 1]])\n    expect(store.get(countAtom)).toBe(1)\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/types.test.tsx",
    "content": "import { expectTypeOf, it } from 'vitest'\nimport { atom } from 'jotai/vanilla'\nimport type {\n  Atom,\n  ExtractAtomArgs,\n  ExtractAtomResult,\n  ExtractAtomValue,\n  PrimitiveAtom,\n  WritableAtom,\n} from 'jotai/vanilla'\n\nit('atom() should return the correct types', () => {\n  // primitive atom\n  const primitiveAtom = atom(0)\n  // NOTE: expectTypeOf is not available in TypeScript 4.0.5 and below, toExtend is not available in TypeScript 4.6.4 and below\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(primitiveAtom).toExtend<PrimitiveAtom<number>>()\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(primitiveAtom).not.toExtend<PrimitiveAtom<number | undefined>>()\n\n  // primitive atom without initial value\n  const primitiveWithoutInitialAtom = atom<number | undefined>()\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(primitiveWithoutInitialAtom).toExtend<\n    PrimitiveAtom<number | undefined>\n  >()\n\n  const undefinedAtom = atom<undefined>()\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(undefinedAtom).toExtend<PrimitiveAtom<undefined>>()\n\n  // read-only derived atom\n  const readonlyDerivedAtom = atom((get) => get(primitiveAtom) * 2)\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(readonlyDerivedAtom).toExtend<Atom<number>>()\n\n  // read-write derived atom\n  const readWriteDerivedAtom = atom(\n    (get) => get(primitiveAtom),\n    (get, set, value: number) => {\n      set(primitiveAtom, get(primitiveAtom) + value)\n    },\n  )\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(readWriteDerivedAtom).toExtend<\n    WritableAtom<number, [number], void>\n  >()\n\n  // write-only derived atom\n  const writeonlyDerivedAtom = atom(null, (get, set) => {\n    set(primitiveAtom, get(primitiveAtom) - 1)\n  })\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(writeonlyDerivedAtom).toExtend<WritableAtom<null, [], void>>()\n})\n\nit('type utils should work', () => {\n  const readWriteAtom = atom(\n    (_get) => 1 as number,\n    async (_get, _set, _value: string) => {},\n  )\n  void readWriteAtom\n\n  type ReadWriteAtom = typeof readWriteAtom\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf<ExtractAtomValue<ReadWriteAtom>>().toEqualTypeOf<number>()\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf<ExtractAtomArgs<ReadWriteAtom>>().toEqualTypeOf<[string]>()\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf<ExtractAtomResult<ReadWriteAtom>>().toEqualTypeOf<\n    Promise<void>\n  >()\n})\n\nit('WritableAtom Result type should be covariant', () => {\n  const writableAtom = atom(null, () => null)\n\n  // NOTE: expectTypeOf is not available in TypeScript 4.0.5 and below, toExtend is not available in TypeScript 4.6.4 and below\n  // [ONLY-TS-4.6.4] [ONLY-TS-4.5.5] [ONLY-TS-4.4.4] [ONLY-TS-4.3.5] [ONLY-TS-4.2.3] [ONLY-TS-4.1.5] [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(writableAtom).toExtend<WritableAtom<null, [], number | null>>()\n})\n"
  },
  {
    "path": "tests/vanilla/utils/atomFamily.test.ts",
    "content": "import { expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport type { Atom } from 'jotai/vanilla'\nimport { atomFamily } from 'jotai/vanilla/utils'\n\nit('should create atoms with different params', () => {\n  const store = createStore()\n  const aFamily = atomFamily((param: number) => atom(param))\n\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(store.get(aFamily(2))).toEqual(2)\n})\n\nit('should remove atoms', () => {\n  const store = createStore()\n  const initializeAtom = vi.fn((param: number) => atom(param))\n  const aFamily = atomFamily(initializeAtom)\n\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(store.get(aFamily(2))).toEqual(2)\n  aFamily.remove(2)\n  initializeAtom.mockClear()\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(initializeAtom).toHaveBeenCalledTimes(0)\n  expect(store.get(aFamily(2))).toEqual(2)\n  expect(initializeAtom).toHaveBeenCalledTimes(1)\n})\n\nit('should remove atoms with custom comparator', () => {\n  const store = createStore()\n  const initializeAtom = vi.fn((param: number) => atom(param))\n  const aFamily = atomFamily(initializeAtom, (a, b) => a === b)\n\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(store.get(aFamily(2))).toEqual(2)\n  expect(store.get(aFamily(3))).toEqual(3)\n  aFamily.remove(2)\n  initializeAtom.mockClear()\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(initializeAtom).toHaveBeenCalledTimes(0)\n  expect(store.get(aFamily(2))).toEqual(2)\n  expect(initializeAtom).toHaveBeenCalledTimes(1)\n})\n\nit('should remove atoms with custom shouldRemove', () => {\n  const store = createStore()\n  const initializeAtom = vi.fn((param: number) => atom(param))\n  const aFamily = atomFamily<number, Atom<number>>(initializeAtom)\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(store.get(aFamily(2))).toEqual(2)\n  expect(store.get(aFamily(3))).toEqual(3)\n  aFamily.setShouldRemove((_createdAt, param) => param % 2 === 0)\n  initializeAtom.mockClear()\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(initializeAtom).toHaveBeenCalledTimes(0)\n  expect(store.get(aFamily(2))).toEqual(2)\n  expect(initializeAtom).toHaveBeenCalledTimes(1)\n  expect(store.get(aFamily(3))).toEqual(3)\n  expect(initializeAtom).toHaveBeenCalledTimes(1)\n})\n\nit('should notify listeners', () => {\n  const aFamily = atomFamily((param: number) => atom(param))\n  const listener = vi.fn(() => {})\n  type Event = { type: 'CREATE' | 'REMOVE'; param: number; atom: Atom<number> }\n  const unsubscribe = aFamily.unstable_listen(listener)\n  const atom1 = aFamily(1)\n  expect(listener).toHaveBeenCalledTimes(1)\n  const eventCreate = listener.mock.calls[0]?.at(0) as unknown as Event\n  if (!eventCreate) throw new Error('eventCreate is undefined')\n  expect(eventCreate.type).toEqual('CREATE')\n  expect(eventCreate.param).toEqual(1)\n  expect(eventCreate.atom).toEqual(atom1)\n  listener.mockClear()\n  aFamily.remove(1)\n  expect(listener).toHaveBeenCalledTimes(1)\n  const eventRemove = listener.mock.calls[0]?.at(0) as unknown as Event\n  expect(eventRemove.type).toEqual('REMOVE')\n  expect(eventRemove.param).toEqual(1)\n  expect(eventRemove.atom).toEqual(atom1)\n  unsubscribe()\n  listener.mockClear()\n  aFamily(2)\n  expect(listener).toHaveBeenCalledTimes(0)\n})\n\nit('should return all params', () => {\n  const store = createStore()\n  const aFamily = atomFamily((param: number) => atom(param))\n\n  expect(store.get(aFamily(1))).toEqual(1)\n  expect(store.get(aFamily(2))).toEqual(2)\n  expect(store.get(aFamily(3))).toEqual(3)\n  expect(Array.from(aFamily.getParams())).toEqual([1, 2, 3])\n})\n"
  },
  {
    "path": "tests/vanilla/utils/atomWithDefault.test.ts",
    "content": "import { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport { RESET, atomWithDefault } from 'jotai/vanilla/utils'\n\ndescribe('atomWithDefault', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  it('should reset to initial value using RESET', () => {\n    const initialValue = 10\n    const testAtom = atomWithDefault(() => initialValue)\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, RESET)\n    expect(store.get(testAtom)).toBe(initialValue)\n  })\n\n  it('should reset to initial value derived from another atom', () => {\n    const initialValueAtom = atom(10)\n    const testAtom = atomWithDefault((get) => get(initialValueAtom))\n    const store = createStore()\n    expect(store.get(testAtom)).toBe(10)\n    store.set(testAtom, 123)\n    store.set(initialValueAtom, 20)\n    store.set(testAtom, RESET)\n    expect(store.get(testAtom)).toBe(20)\n  })\n\n  it(`should reflect changes to the initial value atom when main atom hasn't been manually changed`, () => {\n    const initialValueAtom = atom(10)\n    const testAtom = atomWithDefault((get) => get(initialValueAtom))\n    const store = createStore()\n    store.set(initialValueAtom, 20)\n    expect(store.get(testAtom)).toBe(20)\n  })\n\n  it(`should reflect changes to the initial value atom when main atom has been manually changed but then RESET`, () => {\n    const initialValueAtom = atom(10)\n    const testAtom = atomWithDefault((get) => get(initialValueAtom))\n    const store = createStore()\n    store.set(testAtom, 123)\n    // if this RESET were storing 10 rather than EMPTY the next set wouldn't have an effect\n    store.set(testAtom, RESET)\n    store.set(initialValueAtom, 20)\n    expect(store.get(testAtom)).toBe(20)\n  })\n\n  it('should update atom with a new value', () => {\n    const initialValue = 10\n    const testAtom = atomWithDefault(() => initialValue)\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, 30)\n    expect(store.get(testAtom)).toBe(30)\n  })\n\n  it('should update atom using a function', () => {\n    const initialValue = 10\n    const testAtom = atomWithDefault(() => initialValue)\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, (prev: number) => prev + 10)\n    expect(store.get(testAtom)).toBe(133)\n  })\n\n  it('should reset with a function', () => {\n    const initialValue = 10\n    const testAtom = atomWithDefault(() => initialValue)\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, () => RESET)\n    expect(store.get(testAtom)).toBe(initialValue)\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/utils/atomWithLazy.test.ts",
    "content": "import { expect, it, vi } from 'vitest'\nimport { createStore } from 'jotai/vanilla'\nimport { atomWithLazy } from 'jotai/vanilla/utils'\n\nit('initializes on first store get', () => {\n  const storeA = createStore()\n  const storeB = createStore()\n\n  let externalState = 'first'\n  const initializer = vi.fn(() => externalState)\n  const anAtom = atomWithLazy(initializer)\n\n  expect(initializer).not.toHaveBeenCalled()\n  expect(storeA.get(anAtom)).toEqual('first')\n  expect(initializer).toHaveBeenCalledOnce()\n\n  externalState = 'second'\n\n  expect(storeA.get(anAtom)).toEqual('first')\n  expect(initializer).toHaveBeenCalledOnce()\n  expect(storeB.get(anAtom)).toEqual('second')\n  expect(initializer).toHaveBeenCalledTimes(2)\n})\n\nit('is writable', () => {\n  const store = createStore()\n  const anAtom = atomWithLazy(() => 0)\n\n  store.set(anAtom, 123)\n\n  expect(store.get(anAtom)).toEqual(123)\n})\n\nit('should work with a set state action', () => {\n  const store = createStore()\n  const anAtom = atomWithLazy(() => 4)\n\n  store.set(anAtom, (prev: number) => prev * prev)\n\n  expect(store.get(anAtom)).toEqual(16)\n})\n"
  },
  {
    "path": "tests/vanilla/utils/atomWithRefresh.test.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { createStore } from 'jotai/vanilla'\nimport { atomWithRefresh } from 'jotai/vanilla/utils'\n\ndescribe('atomWithRefresh', () => {\n  it('[DEV-ONLY] throws when refresh is called with extra arguments', () => {\n    const atom = atomWithRefresh(() => {})\n    const store = createStore()\n    const args = ['some arg'] as unknown as []\n    expect(() => store.set(atom, ...args)).throws()\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/utils/atomWithReset.test.ts",
    "content": "import { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { createStore } from 'jotai/vanilla'\nimport { RESET, atomWithReset } from 'jotai/vanilla/utils'\n\ndescribe('atomWithReset', () => {\n  let initialValue: number\n  let testAtom: any\n\n  beforeEach(() => {\n    vi.clearAllMocks()\n    initialValue = 10\n    testAtom = atomWithReset(initialValue)\n  })\n\n  it('should reset to initial value using RESET', () => {\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, RESET)\n    expect(store.get(testAtom)).toBe(initialValue)\n  })\n\n  it('should update atom with a new value', () => {\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, 30)\n    expect(store.get(testAtom)).toBe(30)\n  })\n\n  it('should update atom using a function', () => {\n    const store = createStore()\n    store.set(testAtom, 123)\n    store.set(testAtom, (prev: number) => prev + 10)\n    expect(store.get(testAtom)).toBe(133)\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/utils/loadable.test.ts",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport { loadable } from 'jotai/vanilla/utils'\n\nlet savedConsoleWarn: any\n\ndescribe('loadable', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n    savedConsoleWarn = console.warn\n    console.warn = vi.fn()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n    console.warn = savedConsoleWarn\n  })\n\n  it('should return fulfilled value of an already resolved async atom', async () => {\n    const store = createStore()\n    const asyncAtom = atom(Promise.resolve('concrete'))\n\n    expect(store.get(loadable(asyncAtom))).toEqual({\n      state: 'loading',\n    })\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(loadable(asyncAtom))).toEqual({\n      state: 'hasData',\n      data: 'concrete',\n    })\n  })\n\n  it('should get the latest loadable state after the promise resolves', async () => {\n    const store = createStore()\n    const asyncAtom = atom(Promise.resolve())\n    const loadableAtom = loadable(asyncAtom)\n\n    expect(store.get(loadableAtom)).toHaveProperty('state', 'loading')\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(loadableAtom)).toHaveProperty('state', 'hasData')\n  })\n\n  // https://github.com/pmndrs/jotai/discussions/3208#discussioncomment-15431859\n  it('[DEV-ONLY] should not call store.set during atom read', async () => {\n    const store = createStore()\n    const example = atom('Hello')\n    store.get(example)\n    const loadableAtom = loadable(example)\n    vi.clearAllMocks()\n    store.get(loadableAtom)\n    expect(console.warn).not.toHaveBeenCalled()\n  })\n})\n"
  },
  {
    "path": "tests/vanilla/utils/types.test.tsx",
    "content": "import { expectTypeOf, it } from 'vitest'\nimport { atom } from 'jotai/vanilla'\nimport type { Atom, SetStateAction, WritableAtom } from 'jotai/vanilla'\nimport { selectAtom, unwrap } from 'jotai/vanilla/utils'\n\nit('selectAtom() should return the correct types', () => {\n  const doubleCount = (x: number) => x * 2\n  const syncAtom = atom(0)\n  const syncSelectedAtom = selectAtom(syncAtom, doubleCount)\n  // NOTE: expectTypeOf is not available in TypeScript 4.0.5 and below\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(syncSelectedAtom).toEqualTypeOf<Atom<number>>()\n})\n\nit('unwrap() should return the correct types', () => {\n  const getFallbackValue = () => -1\n  const syncAtom = atom(0)\n  const syncUnwrappedAtom = unwrap(syncAtom, getFallbackValue)\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(syncUnwrappedAtom).toEqualTypeOf<\n    WritableAtom<number, [SetStateAction<number>], void>\n  >()\n\n  const asyncAtom = atom(Promise.resolve(0))\n  const asyncUnwrappedAtom = unwrap(asyncAtom, getFallbackValue)\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(asyncUnwrappedAtom).toEqualTypeOf<\n    WritableAtom<number, [SetStateAction<Promise<number>>], void>\n  >()\n\n  const maybeAsyncAtom = atom(Promise.resolve(0) as number | Promise<number>)\n  const maybeAsyncUnwrappedAtom = unwrap(maybeAsyncAtom, getFallbackValue)\n  // [ONLY-TS-4.0.5] [ONLY-TS-3.9.7] [ONLY-TS-3.8.3] @ts-ignore\n  expectTypeOf(maybeAsyncUnwrappedAtom).toEqualTypeOf<\n    WritableAtom<number, [SetStateAction<number | Promise<number>>], void>\n  >()\n})\n"
  },
  {
    "path": "tests/vanilla/utils/unwrap.test.ts",
    "content": "import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'\nimport { atom, createStore } from 'jotai/vanilla'\nimport { unwrap } from 'jotai/vanilla/utils'\nimport { sleep } from '../../test-utils'\n\nlet savedConsoleWarn: any\n\ndescribe('unwrap', () => {\n  beforeEach(() => {\n    vi.useFakeTimers()\n    savedConsoleWarn = console.warn\n    console.warn = vi.fn()\n  })\n\n  afterEach(() => {\n    vi.useRealTimers()\n    console.warn = savedConsoleWarn\n  })\n\n  it('should unwrap a promise with no fallback function', async () => {\n    const store = createStore()\n    const countAtom = atom(1)\n    const asyncAtom = atom(async (get) => {\n      const count = get(countAtom)\n      await sleep(100)\n      return count * 2\n    })\n    const syncAtom = unwrap(asyncAtom)\n\n    expect(store.get(syncAtom)).toBe(undefined)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(2)\n\n    store.set(countAtom, 2)\n    expect(store.get(syncAtom)).toBe(undefined)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(4)\n\n    store.set(countAtom, 3)\n    expect(store.get(syncAtom)).toBe(undefined)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(6)\n  })\n\n  it('should unwrap a promise with fallback function without prev', async () => {\n    const store = createStore()\n    const countAtom = atom(1)\n    const asyncAtom = atom(async (get) => {\n      const count = get(countAtom)\n      await sleep(100)\n      return count * 2\n    })\n    const syncAtom = unwrap(asyncAtom, () => -1)\n\n    expect(store.get(syncAtom)).toBe(-1)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(2)\n\n    store.set(countAtom, 2)\n    expect(store.get(syncAtom)).toBe(-1)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(4)\n\n    store.set(countAtom, 3)\n    expect(store.get(syncAtom)).toBe(-1)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(6)\n  })\n\n  it('should unwrap a promise with fallback function with prev', async () => {\n    const store = createStore()\n    const countAtom = atom(1)\n    const asyncAtom = atom(async (get) => {\n      const count = get(countAtom)\n      await sleep(100)\n      return count * 2\n    })\n    const syncAtom = unwrap(asyncAtom, (prev?: number) => prev ?? 0)\n\n    expect(store.get(syncAtom)).toBe(0)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(2)\n\n    store.set(countAtom, 2)\n    expect(store.get(syncAtom)).toBe(2)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(4)\n\n    store.set(countAtom, 3)\n    expect(store.get(syncAtom)).toBe(4)\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(6)\n\n    store.set(countAtom, 4)\n    expect(store.get(syncAtom)).toBe(6)\n    store.set(countAtom, 5)\n    expect(store.get(syncAtom)).not.toBe(0) // expect 6 or 8\n    await vi.advanceTimersByTimeAsync(100)\n    expect(store.get(syncAtom)).toBe(10)\n  })\n\n  it('should unwrap a sync atom which is noop', () => {\n    const store = createStore()\n    const countAtom = atom(1)\n    const syncAtom = unwrap(countAtom)\n\n    expect(store.get(syncAtom)).toBe(1)\n\n    store.set(countAtom, 2)\n    expect(store.get(syncAtom)).toBe(2)\n\n    store.set(countAtom, 3)\n    expect(store.get(syncAtom)).toBe(3)\n  })\n\n  it('should unwrap an async writable atom', async () => {\n    const store = createStore()\n    const asyncAtom = atom(Promise.resolve(1))\n    const syncAtom = unwrap(asyncAtom, (prev?: number) => prev ?? 0)\n\n    expect(store.get(syncAtom)).toBe(0)\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(syncAtom)).toBe(1)\n\n    store.set(syncAtom, Promise.resolve(2))\n    expect(store.get(syncAtom)).toBe(1)\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(syncAtom)).toBe(2)\n\n    store.set(syncAtom, Promise.resolve(3))\n    expect(store.get(syncAtom)).toBe(2)\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(syncAtom)).toBe(3)\n  })\n\n  it('should unwrap to a fulfilled value of an already resolved async atom', async () => {\n    const store = createStore()\n    const asyncAtom = atom(Promise.resolve('concrete'))\n\n    expect(store.get(unwrap(asyncAtom))).toEqual(undefined)\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(unwrap(asyncAtom))).toEqual('concrete')\n  })\n\n  it('should get a fulfilled value after the promise resolves', async () => {\n    const store = createStore()\n    const asyncAtom = atom(Promise.resolve('concrete'))\n    const syncAtom = unwrap(asyncAtom)\n\n    expect(store.get(syncAtom)).toEqual(undefined)\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(syncAtom)).toEqual('concrete')\n  })\n\n  it('should throw an error if underlying promise is rejected', async () => {\n    const store = createStore()\n    const asyncAtom = atom(Promise.reject<number>('error'))\n    const syncAtom = unwrap(asyncAtom)\n    store.sub(syncAtom, () => {})\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(() => store.get(syncAtom)).toThrow('error')\n\n    store.set(asyncAtom, Promise.resolve(3))\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(store.get(syncAtom)).toBe(3)\n  })\n\n  it('should update dependents with the value of the unwrapped atom when the promise resolves', async () => {\n    const store = createStore()\n    const asyncTarget = atom(() => Promise.resolve('value'))\n    const target = unwrap(asyncTarget)\n    const results: string[] = []\n    const derived = atom(async (get) => {\n      await Promise.resolve()\n      results.push('effect ' + get(target))\n    })\n\n    store.sub(derived, () => {})\n\n    await vi.advanceTimersByTimeAsync(0)\n    expect(results).toEqual(['effect undefined', 'effect value'])\n  })\n\n  // https://github.com/pmndrs/jotai/discussions/3208#discussioncomment-15431859\n  it('[DEV-ONLY] should not call store.set during atom read', async () => {\n    const store = createStore()\n    const example = atom('Hello')\n    store.get(example)\n    const unwrapAtom = unwrap(example)\n    vi.clearAllMocks()\n    store.get(unwrapAtom)\n    expect(console.warn).not.toHaveBeenCalled()\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      \"jotai\": [\"./src/index.ts\"],\n      \"jotai/*\": [\"./src/*.ts\"]\n    }\n  },\n  \"include\": [\"src/**/*\", \"tests/**/*\", \"benchmarks/**/*\"],\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "vitest.config.mts",
    "content": "import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport react from '@vitejs/plugin-react'\nimport { defineConfig } from 'vitest/config'\n\nexport default defineConfig({\n  resolve: {\n    alias: [\n      { find: /^jotai$/, replacement: resolve('./src/index.ts') },\n      { find: /^jotai(.*)$/, replacement: resolve('./src/$1.ts') },\n    ],\n  },\n  plugins: [\n    react({\n      babel: {\n        plugins: existsSync('./dist/babel/plugin-debug-label.js')\n          ? [\n              // FIXME Can we read from ./src instead of ./dist?\n              './dist/babel/plugin-debug-label.js',\n            ]\n          : [],\n      },\n    }),\n  ],\n  test: {\n    name: 'jotai',\n    // Keeping globals to true triggers React Testing Library's auto cleanup\n    // https://vitest.dev/guide/migration.html\n    globals: true,\n    environment: 'jsdom',\n    dir: 'tests',\n    reporters: process.env.GITHUB_ACTIONS\n      ? ['default', 'github-actions']\n      : ['default'],\n    setupFiles: ['tests/setup.ts'],\n    coverage: {\n      reporter: ['text', 'json', 'html', 'text-summary'],\n      reportsDirectory: './coverage/',\n      provider: 'v8',\n      include: ['src/**'],\n    },\n    onConsoleLog(log) {\n      if (log.includes('DOMException')) return false\n    },\n  },\n})\n"
  },
  {
    "path": "website/.babelrc",
    "content": "{\n  \"presets\": [\n    [\n      \"babel-preset-gatsby\",\n      {\n        \"reactRuntime\": \"automatic\",\n        \"targets\": {\n          \"browsers\": [\n            \">0.25%\",\n            \"not dead\",\n            \"not ie <=11\",\n            \"not ie_mob <=11\",\n            \"not op_mini all\"\n          ]\n        }\n      }\n    ]\n  ],\n  \"plugins\": [\"jotai/babel/plugin-react-refresh\"]\n}\n"
  },
  {
    "path": "website/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# dotenv environment variable files\n.env*\n\n# gatsby files\n.cache/\npublic\n\n# Mac files\n.DS_Store\n\n# Yarn\nyarn-error.log\n.pnp/\n.pnp.js\n# Yarn Integrity file\n.yarn-integrity\n"
  },
  {
    "path": "website/README.md",
    "content": "## Getting Started\n\nInstall the dependencies:\n\n```bash\npnpm install --ignore-workspace\n```\n\nMake a cache directory:\n\n```bash\nmkdir .cache\n```\n\nRun the development server:\n\n```bash\npnpm run dev\n```\n\nOpen [http://localhost:9000](http://localhost:9000) with your browser to see the result.\n"
  },
  {
    "path": "website/api/contact.js",
    "content": "import * as postmark from 'postmark'\n\nconst client = new postmark.ServerClient(process.env.POSTMARK_API_TOKEN)\n\nexport default async function handler(request, response) {\n  const body = request.body\n\n  if (!body.name || !body.email || !body.message) {\n    return response.status(400).json({ data: 'Invalid' })\n  }\n\n  const subject = `Message from ${body.name} (${body.email}) via jotai.org`\n\n  const message = `\n    Name: ${body.name}\\r\\n\n    Email: ${body.email}\\r\\n\n    Message: ${body.message}\n  `\n\n  try {\n    await client.sendEmail({\n      From: 'noreply@jotai.org',\n      To: process.env.EMAIL_RECIPIENTS,\n      Subject: subject,\n      ReplyTo: body.email,\n      TextBody: message,\n    })\n\n    response.status(200).json({ status: 'Sent' })\n  } catch (error) {\n    response.status(500).json({ status: 'Not sent' })\n  }\n}\n"
  },
  {
    "path": "website/gatsby-browser.js",
    "content": "import './src/styles/index.css'\n\nexport { wrapRootElement, wrapPageElement } from './gatsby-shared.js'\n"
  },
  {
    "path": "website/gatsby-config.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nrequire('dotenv').config()\n\nconst kebabCase = require('just-kebab-case')\nconst getAnchor = (value) => {\n  return typeof value === 'string'\n    ? kebabCase(value.toLowerCase().replaceAll(\"'\", ''))\n    : ''\n}\n\nconst DOCS_QUERY = `\n  query {\n    allMdx {\n      nodes {\n        slug\n        meta: frontmatter {\n          title\n          description\n          keywords\n        }\n        headings(depth: h2) {\n          value\n        }\n        excerpt\n        rawBody\n      }\n    }\n  }\n`\n\nconst queries = [\n  {\n    query: DOCS_QUERY,\n    transformer: ({ data }) => {\n      const results = []\n\n      data.allMdx.nodes.forEach((item) => {\n        const transformedNode = {\n          objectID: item.slug,\n          slug: item.slug,\n          title: item.meta.title,\n          description: item.meta.description,\n          keywords: item.meta?.keywords?.split(',') ?? [],\n          excerpt: item.excerpt,\n          headings: item.headings.map((heading) => heading.value).join(' '),\n          body: item.rawBody.replace(/(<([^>]+)>)/gi, ''),\n          level: 1,\n        }\n\n        if (item.slug !== 'introduction') {\n          item.headings\n            .map((heading) => heading.value)\n            .forEach((heading) => {\n              const transformedNode = {\n                objectID: `${item.slug}#${getAnchor(heading)}`,\n                slug: `${item.slug}#${getAnchor(heading)}`,\n                title: heading,\n                description: '',\n                keywords: [],\n                excerpt: '',\n                headings: [],\n                body: '',\n                level: 2,\n              }\n\n              results.push(transformedNode)\n            })\n        }\n\n        results.push(transformedNode)\n      })\n\n      return results\n    },\n    indexName: 'Docs',\n    settings: {\n      searchableAttributes: [\n        'slug',\n        'title',\n        'description',\n        'keywords',\n        'excerpt',\n        'headings',\n        'body',\n      ],\n      indexLanguages: ['en'],\n    },\n    mergeSettings: false,\n  },\n]\n\nmodule.exports = {\n  siteMetadata: {\n    title: `Jotai, primitive and flexible state management for React`,\n    description: `Jotai takes a bottom-up approach to global React state management with an atomic model inspired by Recoil. One can build state by combining atoms and renders are optimized based on atom dependency. This solves the extra re-render issue of React context and eliminates the need for memoization.`,\n    siteUrl: `https://jotai.org`,\n    shortName: `Jotai`,\n  },\n  plugins: [\n    `gatsby-plugin-pnpm`,\n    {\n      resolve: `gatsby-source-filesystem`,\n      options: {\n        name: `docs`,\n        path: `../docs`,\n      },\n    },\n    {\n      resolve: `gatsby-plugin-mdx`,\n      options: {\n        extensions: [`.md`, `.mdx`],\n      },\n    },\n    `gatsby-plugin-postcss`,\n    {\n      resolve: `gatsby-plugin-algolia`,\n      options: {\n        appId: process.env.GATSBY_ALGOLIA_APP_ID,\n        apiKey: process.env.ALGOLIA_ADMIN_KEY,\n        queries,\n        skipIndexing: process.env.ALGOLIA_SKIP_INDEXING === 'true',\n      },\n    },\n    `gatsby-plugin-sitemap`,\n    {\n      resolve: `gatsby-plugin-google-gtag`,\n      options: {\n        trackingIds: ['G-WWJ8XD0QP0'],\n        gtagConfig: {\n          anonymize_ip: true,\n          cookie_expires: 0,\n        },\n        pluginConfig: {\n          head: false,\n          respectDNT: true,\n        },\n      },\n    },\n  ],\n  flags: {\n    DEV_SSR: false,\n    LAZY_IMAGES: true,\n    PRESERVE_FILE_DOWNLOAD_CACHE: true,\n    PARALLEL_SOURCING: true,\n  },\n  graphqlTypegen: false,\n  jsxRuntime: 'automatic',\n  polyfill: false,\n  trailingSlash: 'never',\n}\n"
  },
  {
    "path": "website/gatsby-node.js",
    "content": "exports.createPages = ({ actions }) => {\n  const { createRedirect } = actions\n\n  actions.setWebpackConfig({\n    module: {\n      rules: [\n        {\n          test: require.resolve(`@gatsbyjs/reach-router/index`),\n          type: `javascript/auto`,\n          use: [\n            {\n              loader: require.resolve(`./reach-router`),\n            },\n          ],\n        },\n      ],\n    },\n  })\n\n  createRedirect({\n    fromPath: `/docs/introduction`,\n    toPath: `/docs`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/api/babel`,\n    toPath: `/docs/tools/babel`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/api/core`,\n    toPath: `/docs/core/atom`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/api/devtools`,\n    toPath: `/docs/tools/devtools`,\n    isPermanent: false,\n  })\n  createRedirect({\n    fromPath: `/docs/api/devtools`,\n    toPath: `/docs/tools/devtools`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/api/swc`,\n    toPath: `/docs/tools/swc`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/api/utils`,\n    toPath: `/docs/tools/introduction`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-family`,\n    toPath: `/docs/utilities/family`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-with-default`,\n    toPath: `/docs/utilities/resettable`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-with-hash`,\n    toPath: `/docs/extensions/location`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-with-observable`,\n    toPath: `/docs/utilities/async`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-with-reducer`,\n    toPath: `/docs/utilities/reducer`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-with-reset`,\n    toPath: `/docs/utilities/resettable`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/atom-with-storage`,\n    toPath: `/docs/utilities/storage`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/freeze-atom-creator`,\n    toPath: `/docs/tools/devtools`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/freeze-atom`,\n    toPath: `/docs/tools/devtools`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/loadable`,\n    toPath: `/docs/utilities/async`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/reset`,\n    toPath: `/docs/utilities/resettable`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/select-atom`,\n    toPath: `/docs/utilities/select`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/split-atom`,\n    toPath: `/docs/utilities/split`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/use-atom-callback`,\n    toPath: `/docs/utilities/callback`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/use-atom-value`,\n    toPath: `/docs/core/use-atom`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/use-hydrate-atoms`,\n    toPath: `/docs/utilities/ssr`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/use-reducer-atom`,\n    toPath: `/docs/utilities/reducer`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/use-reset-atom`,\n    toPath: `/docs/utilities/resttable`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/utils/use-update-atom`,\n    toPath: `/docs/core/use-atom`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/cache`,\n    toPath: `/docs/extensions/cache`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/effect`,\n    toPath: `/docs/extensions/effect`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/immer`,\n    toPath: `/docs/extensions/immer`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/location`,\n    toPath: `/docs/extensions/location`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/optics`,\n    toPath: `/docs/extensions/optics`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/query`,\n    toPath: `/docs/extensions/query`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/redux`,\n    toPath: `/docs/extensions/redux`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/relay`,\n    toPath: `/docs/extensions/relay`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/scope`,\n    toPath: `/docs/extensions/scope`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/trpc`,\n    toPath: `/docs/extensions/trpc`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/urql`,\n    toPath: `/docs/extensions/urql`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/valtio`,\n    toPath: `/docs/extensions/valtio`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/xstate`,\n    toPath: `/docs/extensions/xstate`,\n    isPermanent: false,\n  })\n\n  createRedirect({\n    fromPath: `/docs/integrations/zustand`,\n    toPath: `/docs/extensions/zustand`,\n    isPermanent: false,\n  })\n}\n"
  },
  {
    "path": "website/gatsby-shared.js",
    "content": "import { MDXProvider } from '@mdx-js/react'\nimport { Provider as JotaiProvider, createStore } from 'jotai'\nimport { countAtom, menuAtom, searchAtom, textAtom } from './src/atoms/index.js'\nimport { CodeSandbox } from './src/components/code-sandbox.js'\nimport { Code } from './src/components/code.js'\nimport { InlineCode } from './src/components/inline-code.js'\nimport { Layout } from './src/components/layout.js'\nimport { A, H2, H3, H4, H5 } from './src/components/mdx.js'\nimport { Stackblitz } from './src/components/stackblitz.js'\nimport { TOC } from './src/components/toc.js'\n\nconst store = createStore()\n\nstore.set(countAtom, 0)\nstore.set(menuAtom, false)\nstore.set(searchAtom, false)\nstore.set(textAtom, 'hello')\n\nconst components = {\n  code: Code,\n  inlineCode: InlineCode,\n  CodeSandbox,\n  Stackblitz,\n  TOC,\n  h2: H2,\n  h3: H3,\n  h4: H4,\n  h5: H5,\n  a: A,\n}\n\nexport const wrapRootElement = ({ element }) => (\n  <JotaiProvider store={store}>\n    <MDXProvider components={components}>{element}</MDXProvider>\n  </JotaiProvider>\n)\n\nexport const wrapPageElement = ({ element, props }) => {\n  return <Layout isDocs={props.path.startsWith('/docs')}>{element}</Layout>\n}\n"
  },
  {
    "path": "website/gatsby-ssr.js",
    "content": "export { wrapRootElement, wrapPageElement } from './gatsby-shared.js'\n\nexport const onRenderBody = ({ setHtmlAttributes, setPreBodyComponents }) => {\n  setHtmlAttributes({ lang: 'en' })\n  setPreBodyComponents([\n    <script\n      key=\"dark-mode\"\n      dangerouslySetInnerHTML={{\n        __html: `(function() {\n  var storageKey = 'darkMode';\n  var classNameDark = 'dark';\n  var classNameLight = 'light';\n  function setClassOnDocumentBody(darkMode) {\n    document.body.classList.add(darkMode ? classNameDark : classNameLight);\n    document.body.classList.remove(darkMode ? classNameLight : classNameDark);\n  }\n  var localStorageTheme = null;\n  try {\n    localStorageTheme = localStorage.getItem(storageKey);\n  } catch (err) {}\n  var localStorageExists = localStorageTheme !== null;\n  if (localStorageExists) {\n    localStorageTheme = JSON.parse(localStorageTheme);\n  }\n  if (localStorageExists) {\n    setClassOnDocumentBody(localStorageTheme);\n  } else {\n    var isDarkMode = document.body.classList.contains(classNameDark);\n    localStorage.setItem(storageKey, JSON.stringify(isDarkMode));\n  }\n})();`,\n      }}\n    />,\n  ])\n}\n"
  },
  {
    "path": "website/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"checkJs\": true,\n    \"removeComments\": true\n  },\n  \"include\": [\"**/*.js\", \"**/*.jsx\"],\n  \"exclude\": [\"node_modules\", \"public\"]\n}\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"jotai-website\",\n  \"version\": \"0.0.0\",\n  \"description\": \"👻 Primitive and flexible state management for React\",\n  \"homepage\": \"https://github.com/pmndrs/jotai\",\n  \"bugs\": \"https://github.com/pmndrs/jotai/issues\",\n  \"license\": \"MIT\",\n  \"author\": \"Sophia Michelle Andren <sophia+poimandres@candycode.com> (https://candycode.com/)\",\n  \"sideEffects\": [\n    \"*.css\"\n  ],\n  \"scripts\": {\n    \"dev\": \"gatsby develop -H 0.0.0.0 -p 9000\",\n    \"build\": \"gatsby build\",\n    \"serve\": \"gatsby serve\",\n    \"clean\": \"gatsby clean\",\n    \"browserslist\": \"npx browserslist@latest --update-db\"\n  },\n  \"packageManager\": \"pnpm@10.18.3\",\n  \"dependencies\": {\n    \"@headlessui/react\": \"^1.7.7\",\n    \"@mdx-js/mdx\": \"^1.6.22\",\n    \"@mdx-js/react\": \"^1.6.22\",\n    \"algoliasearch\": \"^4.13.1\",\n    \"classnames\": \"^2.3.2\",\n    \"gatsby\": \"^4.23.0\",\n    \"gatsby-plugin-algolia\": \"^0.26.0\",\n    \"gatsby-plugin-google-gtag\": \"^4.23.0\",\n    \"gatsby-plugin-mdx\": \"^3.17.0\",\n    \"gatsby-plugin-pnpm\": \"^1.2.10\",\n    \"gatsby-plugin-postcss\": \"^5.23.0\",\n    \"gatsby-plugin-sitemap\": \"^5.23.0\",\n    \"gatsby-source-filesystem\": \"^4.23.0\",\n    \"jotai\": \"^2.4.1\",\n    \"jotai-immer\": \"^0.2.0\",\n    \"just-kebab-case\": \"^4.1.1\",\n    \"just-throttle\": \"^4.1.1\",\n    \"prism-react-renderer\": \"^1.3.3\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-instantsearch-hooks-web\": \"^6.33.0\",\n    \"react-remove-scroll\": \"^2.5.5\"\n  },\n  \"devDependencies\": {\n    \"@gatsbyjs/reach-router\": \"^2.0.1\",\n    \"@tailwindcss/forms\": \"^0.5.6\",\n    \"autoprefixer\": \"^10.4.11\",\n    \"babel-core\": \"^6.26.3\",\n    \"babel-loader\": \"^9.1.3\",\n    \"babel-preset-gatsby\": \"^2.23.0\",\n    \"postcss\": \"^8.4.29\",\n    \"postcss-import\": \"^15.0.0\",\n    \"postmark\": \"^3.0.14\",\n    \"prettier\": \"^3.0.3\",\n    \"tailwindcss\": \"^3.1.8\"\n  },\n  \"private\": true,\n  \"browserslist\": [\n    \">0.25%\",\n    \"not dead\",\n    \"not ie <=11\",\n    \"not ie_mob <=11\",\n    \"not op_mini all\"\n  ]\n}\n"
  },
  {
    "path": "website/postcss.config.js",
    "content": "/** @type {import('postcss-load-config').Config} */\nmodule.exports = {\n  plugins: {\n    'postcss-import': {},\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "website/reach-router.js",
    "content": "exports.default = function (source) {\n  if (source.includes('exports.BaseContext')) {\n    return source\n  } else {\n    return source + 'exports.BaseContext = BaseContext;'\n  }\n}\n"
  },
  {
    "path": "website/src/api/contact.js",
    "content": "import * as postmark from 'postmark'\n\nconst client = new postmark.ServerClient(process.env.POSTMARK_API_TOKEN)\n\nexport default async function handler(request, response) {\n  const body = request.body\n\n  if (!body.name || !body.email || !body.message) {\n    return response.status(400).json({ data: 'Invalid' })\n  }\n\n  const subject = `Message from ${body.name} (${body.email}) via jotai.org`\n\n  const message = `\n    Name: ${body.name}\\r\\n\n    Email: ${body.email}\\r\\n\n    Message: ${body.message}\n  `\n\n  try {\n    await client.sendEmail({\n      From: 'noreply@jotai.org',\n      To: process.env.EMAIL_RECIPIENTS,\n      Subject: subject,\n      ReplyTo: body.email,\n      TextBody: message,\n    })\n\n    response.status(200).json({ status: 'Sent' })\n  } catch (error) {\n    response.status(500).json({ status: 'Not sent' })\n  }\n}\n"
  },
  {
    "path": "website/src/atoms/index.js",
    "content": "/* eslint-disable import/extensions */\nimport { atom } from 'jotai'\nimport { atomWithStorage } from 'jotai/utils'\nimport { atomWithImmer } from 'jotai-immer'\n\nexport const menuAtom = atom(false)\nexport const searchAtom = atom(false)\nexport const helpAtom = atom(false)\nexport const textAtom = atom('hello')\nexport const uppercaseAtom = atom((get) => get(textAtom).toUpperCase())\nexport const darkModeAtom = atomWithStorage('darkMode', false)\nexport const countAtom = atomWithImmer(0)\n"
  },
  {
    "path": "website/src/components/button.js",
    "content": "import cx from 'classnames'\nimport { Link } from 'gatsby'\nimport { Icon } from '../components/icon.js'\n\nexport const Button = ({\n  type = 'button',\n  onClick,\n  icon = undefined,\n  disabled = false,\n  to,\n  external = false,\n  dark = false,\n  bold = false,\n  small = false,\n  className = '',\n  children,\n  ...rest\n}) => {\n  const buttonClassNames = cx(\n    'inline-flex select-none items-center border dark:!shadow-none dark:!border-transparent group',\n    bold && 'font-medium',\n    !small\n      ? 'space-x-4 rounded-md px-6 py-3 text-base shadow-md sm:rounded-lg'\n      : 'space-x-2 rounded px-3 py-1.5 text-xs shadow-sm sm:rounded-md',\n    !dark\n      ? 'border-gray-200 bg-gray-100 text-black hover:bg-blue-100 dark:border-gray-800 dark:bg-gray-900 hover:border-blue-200 dark:hover:bg-white dark:text-gray-300 dark:hover:text-black'\n      : 'border-gray-800 bg-gray-900 text-gray-300',\n    !disabled ? 'cursor-pointer' : 'cursor-not-allowed',\n    className,\n  )\n\n  const iconClassNames = cx(\n    'flex-shrink-0 fill-current object-contain transition ease-in-out duration-300',\n    !small ? 'h-6 w-6' : 'h-4 w-4',\n    !dark\n      ? 'text-gray-700 dark:text-gray-300 dark:group-hover:text-black'\n      : 'text-gray-300',\n  )\n\n  if (onClick && to) {\n    return (\n      <Link\n        to={to}\n        onClick={onClick}\n        role=\"button\"\n        className={buttonClassNames}\n        {...rest}\n      >\n        {icon && <Icon icon={icon} className={iconClassNames} />}\n        <span>{children}</span>\n      </Link>\n    )\n  } else if (onClick) {\n    return (\n      <button\n        type=\"button\"\n        onClick={onClick}\n        disabled={disabled}\n        className={buttonClassNames}\n        {...rest}\n      >\n        {icon && <Icon icon={icon} className={iconClassNames} />}\n        <span>{children}</span>\n      </button>\n    )\n  } else if (to && external) {\n    return (\n      <a\n        href={to}\n        target=\"_blank\"\n        rel=\"noreferrer\"\n        className={buttonClassNames}\n        {...rest}\n      >\n        {icon && <Icon icon={icon} className={iconClassNames} />}\n        <span>{children}</span>\n      </a>\n    )\n  } else if (to) {\n    return (\n      <Link to={to} className={buttonClassNames} {...rest}>\n        {icon && <Icon icon={icon} className={iconClassNames} />}\n        <span>{children}</span>\n      </Link>\n    )\n  } else {\n    return (\n      <button\n        type={type}\n        disabled={disabled}\n        className={buttonClassNames}\n        {...rest}\n      >\n        {icon && <Icon icon={icon} className={iconClassNames} />}\n        <span>{children}</span>\n      </button>\n    )\n  }\n}\n"
  },
  {
    "path": "website/src/components/client-only.js",
    "content": "import { useEffect, useState } from 'react'\n\nexport const ClientOnly = ({ children }) => {\n  const [hasMounted, setHasMounted] = useState(false)\n\n  useEffect(() => {\n    setHasMounted(true)\n  }, [])\n\n  if (!hasMounted) {\n    return null\n  }\n\n  return children\n}\n"
  },
  {
    "path": "website/src/components/code-sandbox.js",
    "content": "export const CodeSandbox = ({ id, tests }) => {\n  return (\n    <div className=\"mb-8 overflow-hidden rounded-md border-b border-gray-200 shadow-lg dark:border-gray-800 dark:!shadow-none sm:rounded-lg\">\n      <iframe\n        title={id}\n        className=\"h-[400px] w-full\"\n        src={`https://codesandbox.io/embed/${id}?codemirror=1&fontsize=14&hidenavigation=1&theme=light&hidedevtools=1${\n          tests ? '&previewwindow=tests' : ''\n        }`}\n        allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n        sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n        loading=\"lazy\"\n      />\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/code.js",
    "content": "import Highlight, { defaultProps } from 'prism-react-renderer'\n\nexport const Code = ({ language = 'jsx', children }) => {\n  const code = children.trim()\n\n  return (\n    <Highlight\n      {...defaultProps}\n      language={language}\n      code={code}\n      theme={undefined}\n    >\n      {({ className, style, tokens, getLineProps, getTokenProps }) => (\n        <pre className={`${className} notranslate`} style={style}>\n          {tokens.map((line, i) => (\n            <div {...getLineProps({ line, key: i })}>\n              {line.map((token, key) => (\n                <span {...getTokenProps({ token, key })} />\n              ))}\n            </div>\n          ))}\n        </pre>\n      )}\n    </Highlight>\n  )\n}\n"
  },
  {
    "path": "website/src/components/core-demo.js",
    "content": "import { useAtom } from 'jotai'\nimport { textAtom, uppercaseAtom } from '../atoms/index.js'\nimport { Code } from '../components/code.js'\n\nexport const CoreDemo = () => {\n  const Input = () => {\n    const [text, setText] = useAtom(textAtom)\n\n    return (\n      <input\n        value={text}\n        onChange={(event) => setText(event.target.value)}\n        className=\"w-full bg-transparent focus:!ring-transparent !border-none\"\n      />\n    )\n  }\n\n  const Uppercase = () => {\n    const [uppercase] = useAtom(uppercaseAtom)\n\n    return <span className=\"flex-shrink-0 font-bold\">{uppercase}</span>\n  }\n\n  const code = `import { atom, useAtom } from 'jotai'\n\n// Create your atoms and derivatives\nconst textAtom = atom('hello')\nconst uppercaseAtom = atom(\n  (get) => get(textAtom).toUpperCase()\n)\n\n// Use them anywhere in your app\nconst Input = () => {\n  const [text, setText] = useAtom(textAtom)\n  const handleChange = (e) => setText(e.target.value)\n  return (\n    <input value={text} onChange={handleChange} />\n  )\n}\n\nconst Uppercase = () => {\n  const [uppercase] = useAtom(uppercaseAtom)\n  return (\n    <div>Uppercase: {uppercase}</div>\n  )\n}\n\n// Now you have the components\nconst App = () => {\n  return (\n    <>\n      <Input />\n      <Uppercase />\n    </>\n  )\n}`\n\n  return (\n    <>\n      <div className=\"py-8 text-sm\">\n        <div className=\"flex items-center rounded-lg border border-gray-300 bg-white px-4 py-2 text-lg focus-within:ring focus-within:ring-blue-400 dark:border-gray-800 dark:bg-gray-950 dark:focus-within:ring-teal-700\">\n          <Input />\n          <Uppercase />\n        </div>\n      </div>\n      <Code>{code}</Code>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/credits.js",
    "content": "import { Button } from '../components/button.js'\n\nexport const Credits = () => {\n  return (\n    <>\n      <Button\n        to=\"https://twitter.com/dai_shi\"\n        title=\"Daishi Kato\"\n        className=\"inline-flex items-center justify-center\"\n        style={{ width: 150 }}\n        dark\n        small\n        external\n      >\n        library by <span className=\"font-semibold\">Daishi Kato</span>\n      </Button>\n      <Button\n        to=\"https://jessiewaters.com\"\n        title=\"Jessie Waters\"\n        className=\"inline-flex items-center justify-center\"\n        style={{ width: 150 }}\n        dark\n        small\n        external\n      >\n        art by <span className=\"font-semibold\">Jessie Waters</span>\n      </Button>\n      <Button\n        to=\"https://candycode.com/\"\n        title=\"candycode, an alternative graphic design and web development agency based in San Diego\"\n        className=\"inline-flex items-center justify-center\"\n        style={{ width: 150, height: 28 }}\n        dark\n        small\n        external\n      >\n        <div className=\"inline-flex items-center space-x-1\">\n          <span className=\"whitespace-nowrap\">site by</span>\n          <img\n            src=\"https://storage.googleapis.com/candycode/candycode.svg\"\n            alt=\"candycode alternative graphic design web development agency San Diego\"\n            style={{ position: 'relative', bottom: -0.5, height: 15 }}\n          />\n        </div>\n      </Button>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/docs.js",
    "content": "import { Link, graphql, useStaticQuery } from 'gatsby'\nimport { useSetAtom } from 'jotai'\nimport { menuAtom } from '../atoms/index.js'\nimport { Icon } from '../components/icon.js'\n\nexport const Docs = (props) => {\n  const data = useStaticQuery(staticQuery)\n  const setIsMenuOpen = useSetAtom(menuAtom)\n\n  const allDocs = data.allMdx.nodes.filter(checkDocs).sort(sortDocs)\n  const allNavLinks = parseDocs(allDocs)\n  const navLinks = allNavLinks.slice(1)\n\n  const renderSection = (section, index) => (\n    <div key={section.title || index}>\n      {section.title && (\n        <div className=\"relative -left-0.5 flex items-center gap-1 mt-8 lg:mt-0\">\n          <span className=\"text-base font-bold uppercase tracking-widest text-gray-350 dark:text-white\">\n            {section.title}\n          </span>\n          <span className=\"relative top-px\">\n            <Icon icon=\"chevron\" className=\"h-auto w-4 text-gray-350\" />\n          </span>\n        </div>\n      )}\n      <ul className=\"space-y-0.5 mt-2\">\n        {section.contents.map((doc) => (\n          <li key={doc.slug}>\n            <Link\n              to={`/docs/${doc.slug}`}\n              onClick={() => setIsMenuOpen(false)}\n              className=\"relative -left-3 inline-block whitespace-nowrap rounded border dark:!border-none border-transparent px-2 py-1 text-base text-black hover:!border-blue-200 hover:bg-blue-100 dark:text-gray-300 dark:hover:!text-black dark:hover:bg-white lg:whitespace-normal\"\n              activeClassName=\"!border-blue-200 dark:!border-white bg-blue-100 dark:bg-white dark:!text-black\"\n            >\n              {doc.meta.title}\n            </Link>\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n\n  const renderGroupedSections = () => {\n    const totalSections = navLinks.length\n    const groupsCount = Math.floor((totalSections - 2) / 2)\n    const result = []\n\n    for (let i = 0; i < groupsCount; i++) {\n      const startIndex = i * 2\n      result.push(\n        <section\n          key={`group-${i}`}\n          className=\"contents lg:flex lg:flex-col lg:gap-8\"\n        >\n          {renderSection(navLinks[startIndex], startIndex)}\n          {renderSection(navLinks[startIndex + 1], startIndex + 1)}\n        </section>,\n      )\n    }\n\n    const remainingStart = groupsCount * 2\n\n    for (let i = remainingStart; i < totalSections; i++) {\n      result.push(renderSection(navLinks[i], i))\n    }\n\n    return result\n  }\n\n  return <div {...props}>{renderGroupedSections()}</div>\n}\n\nconst staticQuery = graphql`\n  query {\n    allMdx {\n      nodes {\n        slug\n        meta: frontmatter {\n          title\n          nav\n          published\n        }\n      }\n    }\n  }\n`\n\nconst checkDocs = (doc) => doc.meta?.nav !== null\n\nconst sortDocs = (a, b) => a.meta.nav - b.meta.nav\n\nconst parseDocs = (docs) => {\n  let directories = []\n  let newDocs = []\n\n  docs.forEach(({ slug }) => {\n    const hasParent = slug.includes('/')\n\n    let parent = undefined\n\n    if (hasParent) {\n      parent = slug.split('/')[0]\n\n      if (!directories.includes(parent)) {\n        directories = [...directories, parent]\n      }\n    }\n  })\n\n  newDocs = [{ contents: [...docs.filter((doc) => !doc.slug.includes('/'))] }]\n\n  directories.forEach((directory) => {\n    newDocs = [\n      ...newDocs,\n      {\n        title: directory.replace('-', ' '),\n        contents: [...docs.filter((doc) => doc.slug.startsWith(directory))],\n      },\n    ]\n  })\n\n  return newDocs\n}\n"
  },
  {
    "path": "website/src/components/extensions-demo.js",
    "content": "import { useAtom } from 'jotai'\nimport { countAtom } from '../atoms/index.js'\nimport { Button } from '../components/button.js'\nimport { Code } from '../components/code.js'\n\nexport const ExtensionsDemo = () => {\n  const [count, setCount] = useAtom(countAtom)\n\n  const increment = () => setCount((c) => (c = c + 1))\n\n  const code = `import { useAtom } from 'jotai'\nimport { atomWithImmer } from 'jotai-immer'\n\n// Create a new atom with an immer-based write function\nconst countAtom = atomWithImmer(0)\n\nconst Counter = () => {\n  const [count] = useAtom(countAtom)\n  return (\n    <div>count: {count}</div>\n  )\n}\n\nconst Controls = () => {\n  // setCount === update: (draft: Draft<Value>) => void\n  const [, setCount] = useAtom(countAtom)\n  const increment = () => setCount((c) => (c = c + 1))\n  return (\n    <button onClick={increment}>+1</button>\n  )\n}`\n\n  return (\n    <>\n      <div className=\"flex items-center space-x-8 pt-4 lg:pt-8 lg:pb-4\">\n        <Button onClick={increment} icon=\"plus\" className=\"focus:ring\">\n          Increment\n        </Button>\n        <span className=\"text-3xl font-bold ordinal slashed-zero tabular-nums\">\n          {count}\n        </span>\n      </div>\n      <Code>{code}</Code>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/external-link.js",
    "content": "export const ExternalLink = ({ to, children, ...rest }) => {\n  return (\n    <a href={to} target=\"_blank\" rel=\"noreferrer\" {...rest}>\n      {children}\n    </a>\n  )\n}\n"
  },
  {
    "path": "website/src/components/footer.js",
    "content": "import { Credits } from '../components/credits.js'\n\nexport const Footer = () => {\n  return (\n    <footer\n      className=\"mt-8 inline-flex flex-col space-y-2 lg:hidden\"\n      style={{ marginBottom: 79 }}\n    >\n      <Credits />\n    </footer>\n  )\n}\n"
  },
  {
    "path": "website/src/components/headline.js",
    "content": "import cx from 'classnames'\n\nexport const Headline = ({ className = '', children }) => {\n  return (\n    <div\n      className={cx(\n        'text-4xl font-bold tracking-tight text-black dark:text-gray-50 lg:text-7xl lg:text-gray-300',\n        className,\n      )}\n    >\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/icon.js",
    "content": "export const Icon = ({ icon, ...rest }) => {\n  switch (icon) {\n    case 'book':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 9 4 C 7.355469 4 6 5.355469 6 7 L 6 25 C 6 26.644531 7.355469 28 9 28 L 26 28 L 26 4 Z M 9 6 L 24 6 L 24 22 L 9 22 C 8.648438 22 8.316406 22.074219 8 22.1875 L 8 7 C 8 6.433594 8.433594 6 9 6 Z M 11 9 L 11 11 L 22 11 L 22 9 Z M 9 24 L 24 24 L 24 26 L 9 26 C 8.433594 26 8 25.566406 8 25 C 8 24.433594 8.433594 24 9 24 Z\" />\n        </svg>\n      )\n    case 'chalkboard':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 4 6 L 4 8 L 26 8 L 26 24 L 12 24 L 12 26 L 30 26 L 30 24 L 28 24 L 28 6 L 4 6 z M 8.0019531 9 C 5.8032706 9 4 10.802666 4 13 C 4 15.198683 5.8040743 17 8.0019531 17 C 10.197134 17 12 15.198683 12 13 C 12 10.802666 10.19794 9 8.0019531 9 z M 14 10 L 14 12 L 19 12 L 19 10 L 14 10 z M 21 10 L 21 12 L 24 12 L 24 10 L 21 10 z M 8.0019531 11 C 9.1159662 11 10 11.883334 10 13 C 10 14.119317 9.1167719 15 8.0019531 15 C 6.881832 15 6 14.119317 6 13 C 6 11.883334 6.8826357 11 8.0019531 11 z M 14 14 L 14 16 L 24 16 L 24 14 L 14 14 z M 4 18 L 4 26 L 6 26 L 6 20 L 9 20 L 9 26 L 11 26 L 11 20.658203 L 13.064453 21.75 C 13.648752 22.060158 14.351587 22.058921 14.935547 21.75 L 14.935547 21.751953 L 18.466797 19.884766 L 17.533203 18.115234 L 14.001953 19.982422 L 10.90625 18.347656 C 10.475078 18.120046 9.9935054 18 9.5039062 18 L 4 18 z\" />\n        </svg>\n      )\n    case 'chevron':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 6.90625 6.59375 L 6.1875 7.28125 L 2.28125 11.1875 L 1.59375 11.90625 L 16 26.3125 L 30.40625 11.90625 L 29.71875 11.1875 L 25.8125 7.28125 L 25.09375 6.59375 L 16 15.6875 Z M 6.875 9.4375 L 15.28125 17.8125 L 16 18.5 L 16.71875 17.8125 L 25.125 9.4375 L 27.5625 11.875 L 16 23.46875 L 4.4375 11.875 Z\" />\n        </svg>\n      )\n    case 'close':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 16 3 C 8.832031 3 3 8.832031 3 16 C 3 23.167969 8.832031 29 16 29 C 23.167969 29 29 23.167969 29 16 C 29 8.832031 23.167969 3 16 3 Z M 16 5 C 22.085938 5 27 9.914063 27 16 C 27 22.085938 22.085938 27 16 27 C 9.914063 27 5 22.085938 5 16 C 5 9.914063 9.914063 5 16 5 Z M 12.21875 10.78125 L 10.78125 12.21875 L 14.5625 16 L 10.78125 19.78125 L 12.21875 21.21875 L 16 17.4375 L 19.78125 21.21875 L 21.21875 19.78125 L 17.4375 16 L 21.21875 12.21875 L 19.78125 10.78125 L 16 14.5625 Z\" />\n        </svg>\n      )\n    case 'discord':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 12.65625 4.90625 L 11.875 5 C 11.875 5 8.371094 5.382813 5.8125 7.4375 L 5.78125 7.4375 L 5.75 7.46875 C 5.175781 7.996094 4.925781 8.644531 4.53125 9.59375 C 4.136719 10.542969 3.714844 11.753906 3.34375 13.09375 C 2.601563 15.777344 2 19.027344 2 22 L 2 22.25 L 2.125 22.5 C 3.050781 24.125 4.695313 25.160156 6.21875 25.875 C 7.742188 26.589844 9.058594 26.96875 9.96875 27 L 10.5625 27.03125 L 10.875 26.5 L 11.96875 24.5625 C 13.128906 24.824219 14.464844 25 16 25 C 17.535156 25 18.871094 24.824219 20.03125 24.5625 L 21.125 26.5 L 21.4375 27.03125 L 22.03125 27 C 22.941406 26.96875 24.257813 26.589844 25.78125 25.875 C 27.304688 25.160156 28.949219 24.125 29.875 22.5 L 30 22.25 L 30 22 C 30 19.027344 29.398438 15.777344 28.65625 13.09375 C 28.285156 11.753906 27.863281 10.542969 27.46875 9.59375 C 27.074219 8.644531 26.824219 7.996094 26.25 7.46875 L 26.21875 7.4375 L 26.1875 7.4375 C 23.628906 5.382813 20.125 5 20.125 5 L 19.34375 4.90625 L 19.0625 5.625 C 19.0625 5.625 18.773438 6.355469 18.59375 7.1875 C 17.460938 7.035156 16.535156 7 16 7 C 15.464844 7 14.539063 7.035156 13.40625 7.1875 C 13.226563 6.355469 12.9375 5.625 12.9375 5.625 Z M 11.28125 7.1875 C 11.324219 7.328125 11.367188 7.449219 11.40625 7.5625 C 10.113281 7.882813 8.734375 8.371094 7.46875 9.15625 L 8.53125 10.84375 C 11.125 9.234375 14.851563 9 16 9 C 17.148438 9 20.875 9.234375 23.46875 10.84375 L 24.53125 9.15625 C 23.265625 8.371094 21.886719 7.882813 20.59375 7.5625 C 20.632813 7.449219 20.675781 7.328125 20.71875 7.1875 C 21.652344 7.375 23.433594 7.804688 24.90625 8.96875 C 24.898438 8.972656 25.28125 9.550781 25.625 10.375 C 25.976563 11.222656 26.367188 12.351563 26.71875 13.625 C 27.394531 16.066406 27.925781 19.039063 27.96875 21.65625 C 27.339844 22.617188 26.171875 23.484375 24.9375 24.0625 C 23.859375 24.566406 23.007813 24.75 22.5 24.84375 L 22 24 C 22.296875 23.890625 22.589844 23.769531 22.84375 23.65625 C 24.382813 22.980469 25.21875 22.25 25.21875 22.25 L 23.90625 20.75 C 23.90625 20.75 23.34375 21.265625 22.03125 21.84375 C 20.71875 22.421875 18.714844 23 16 23 C 13.285156 23 11.28125 22.421875 9.96875 21.84375 C 8.65625 21.265625 8.09375 20.75 8.09375 20.75 L 6.78125 22.25 C 6.78125 22.25 7.617188 22.980469 9.15625 23.65625 C 9.410156 23.769531 9.703125 23.890625 10 24 L 9.5 24.84375 C 8.992188 24.75 8.140625 24.566406 7.0625 24.0625 C 5.828125 23.484375 4.660156 22.617188 4.03125 21.65625 C 4.074219 19.039063 4.605469 16.066406 5.28125 13.625 C 5.632813 12.351563 6.023438 11.222656 6.375 10.375 C 6.71875 9.550781 7.101563 8.972656 7.09375 8.96875 C 8.566406 7.804688 10.347656 7.375 11.28125 7.1875 Z M 12.5 14 C 11.726563 14 11.042969 14.441406 10.625 15 C 10.207031 15.558594 10 16.246094 10 17 C 10 17.753906 10.207031 18.441406 10.625 19 C 11.042969 19.558594 11.726563 20 12.5 20 C 13.273438 20 13.957031 19.558594 14.375 19 C 14.792969 18.441406 15 17.753906 15 17 C 15 16.246094 14.792969 15.558594 14.375 15 C 13.957031 14.441406 13.273438 14 12.5 14 Z M 19.5 14 C 18.726563 14 18.042969 14.441406 17.625 15 C 17.207031 15.558594 17 16.246094 17 17 C 17 17.753906 17.207031 18.441406 17.625 19 C 18.042969 19.558594 18.726563 20 19.5 20 C 20.273438 20 20.957031 19.558594 21.375 19 C 21.792969 18.441406 22 17.753906 22 17 C 22 16.246094 21.792969 15.558594 21.375 15 C 20.957031 14.441406 20.273438 14 19.5 14 Z M 12.5 16 C 12.554688 16 12.625 16.019531 12.75 16.1875 C 12.875 16.355469 13 16.648438 13 17 C 13 17.351563 12.875 17.644531 12.75 17.8125 C 12.625 17.980469 12.554688 18 12.5 18 C 12.445313 18 12.375 17.980469 12.25 17.8125 C 12.125 17.644531 12 17.351563 12 17 C 12 16.648438 12.125 16.355469 12.25 16.1875 C 12.375 16.019531 12.445313 16 12.5 16 Z M 19.5 16 C 19.554688 16 19.625 16.019531 19.75 16.1875 C 19.875 16.355469 20 16.648438 20 17 C 20 17.351563 19.875 17.644531 19.75 17.8125 C 19.625 17.980469 19.554688 18 19.5 18 C 19.445313 18 19.375 17.980469 19.25 17.8125 C 19.125 17.644531 19 17.351563 19 17 C 19 16.648438 19.125 16.355469 19.25 16.1875 C 19.375 16.019531 19.445313 16 19.5 16 Z\" />\n        </svg>\n      )\n    case 'file':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 6 3 L 6 29 L 26 29 L 26 9.59375 L 25.71875 9.28125 L 19.71875 3.28125 L 19.40625 3 Z M 8 5 L 18 5 L 18 11 L 24 11 L 24 27 L 8 27 Z M 20 6.4375 L 22.5625 9 L 20 9 Z M 11 13 L 11 15 L 21 15 L 21 13 Z M 11 17 L 11 19 L 21 19 L 21 17 Z M 11 21 L 11 23 L 21 23 L 21 21 Z\" />\n        </svg>\n      )\n    case 'file-blank':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 6 3 L 6 29 L 26 29 L 26 9.59375 L 25.71875 9.28125 L 19.71875 3.28125 L 19.40625 3 Z M 8 5 L 18 5 L 18 11 L 24 11 L 24 27 L 8 27 Z M 20 6.4375 L 22.5625 9 L 20 9 Z\" />\n        </svg>\n      )\n    case 'github':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path\n            fillRule=\"evenodd\"\n            d=\"M 16 4 C 9.371094 4 4 9.371094 4 16 C 4 21.300781 7.4375 25.800781 12.207031 27.386719 C 12.808594 27.496094 13.027344 27.128906 13.027344 26.808594 C 13.027344 26.523438 13.015625 25.769531 13.011719 24.769531 C 9.671875 25.492188 8.96875 23.160156 8.96875 23.160156 C 8.421875 21.773438 7.636719 21.402344 7.636719 21.402344 C 6.546875 20.660156 7.71875 20.675781 7.71875 20.675781 C 8.921875 20.761719 9.554688 21.910156 9.554688 21.910156 C 10.625 23.746094 12.363281 23.214844 13.046875 22.910156 C 13.15625 22.132813 13.46875 21.605469 13.808594 21.304688 C 11.144531 21.003906 8.34375 19.972656 8.34375 15.375 C 8.34375 14.0625 8.8125 12.992188 9.578125 12.152344 C 9.457031 11.851563 9.042969 10.628906 9.695313 8.976563 C 9.695313 8.976563 10.703125 8.65625 12.996094 10.207031 C 13.953125 9.941406 14.980469 9.808594 16 9.804688 C 17.019531 9.808594 18.046875 9.941406 19.003906 10.207031 C 21.296875 8.65625 22.300781 8.976563 22.300781 8.976563 C 22.957031 10.628906 22.546875 11.851563 22.421875 12.152344 C 23.191406 12.992188 23.652344 14.0625 23.652344 15.375 C 23.652344 19.984375 20.847656 20.996094 18.175781 21.296875 C 18.605469 21.664063 18.988281 22.398438 18.988281 23.515625 C 18.988281 25.121094 18.976563 26.414063 18.976563 26.808594 C 18.976563 27.128906 19.191406 27.503906 19.800781 27.386719 C 24.566406 25.796875 28 21.300781 28 16 C 28 9.371094 22.628906 4 16 4 Z\"\n          />\n        </svg>\n      )\n    case 'help':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 16 4 C 9.382813 4 4 9.382813 4 16 C 4 22.617188 9.382813 28 16 28 C 22.617188 28 28 22.617188 28 16 C 28 9.382813 22.617188 4 16 4 Z M 16 6 C 21.535156 6 26 10.464844 26 16 C 26 21.535156 21.535156 26 16 26 C 10.464844 26 6 21.535156 6 16 C 6 10.464844 10.464844 6 16 6 Z M 16 10 C 13.800781 10 12 11.800781 12 14 L 14 14 C 14 12.882813 14.882813 12 16 12 C 17.117188 12 18 12.882813 18 14 C 18 14.765625 17.507813 15.445313 16.78125 15.6875 L 16.375 15.8125 C 15.558594 16.082031 15 16.863281 15 17.71875 L 15 19 L 17 19 L 17 17.71875 L 17.40625 17.59375 C 18.945313 17.082031 20 15.621094 20 14 C 20 11.800781 18.199219 10 16 10 Z M 15 20 L 15 22 L 17 22 L 17 20 Z\" />\n        </svg>\n      )\n    case 'home':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 16 2.59375 L 15.28125 3.28125 L 2.28125 16.28125 L 3.71875 17.71875 L 5 16.4375 L 5 28 L 14 28 L 14 18 L 18 18 L 18 28 L 27 28 L 27 16.4375 L 28.28125 17.71875 L 29.71875 16.28125 L 16.71875 3.28125 Z M 16 5.4375 L 25 14.4375 L 25 26 L 20 26 L 20 16 L 12 16 L 12 26 L 7 26 L 7 14.4375 Z\" />\n        </svg>\n      )\n    case 'menu':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 4 7 L 4 9 L 28 9 L 28 7 Z M 4 15 L 4 17 L 28 17 L 28 15 Z M 4 23 L 4 25 L 28 25 L 28 23 Z\" />\n        </svg>\n      )\n    case 'message':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 3 5 L 3 23 L 8 23 L 8 28.078125 L 14.351563 23 L 29 23 L 29 5 Z M 5 7 L 27 7 L 27 21 L 13.648438 21 L 10 23.917969 L 10 21 L 5 21 Z\" />\n        </svg>\n      )\n    case 'moon':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 5 5 L 5 7 L 3 7 L 3 9 L 5 9 L 5 11 L 7 11 L 7 9 L 9 9 L 9 7 L 7 7 L 7 5 Z M 20.28125 7.9375 L 18.625 8 C 13.28125 8.191406 9 12.578125 9 17.96875 C 9 23.480469 13.488281 27.96875 19 27.96875 C 24.390625 27.96875 28.777344 23.6875 28.96875 18.34375 L 29.03125 16.71875 L 27.5625 17.40625 C 26.78125 17.777344 25.914063 17.96875 25 17.96875 C 21.675781 17.96875 19 15.292969 19 11.96875 C 19 11.054688 19.222656 10.21875 19.59375 9.4375 Z M 17.375 10.3125 C 17.25 10.867188 17 11.375 17 11.96875 C 17 16.375 20.59375 19.96875 25 19.96875 C 25.605469 19.96875 26.121094 19.722656 26.6875 19.59375 C 25.925781 23.21875 22.859375 25.96875 19 25.96875 C 14.570313 25.96875 11 22.398438 11 17.96875 C 11 14.117188 13.757813 11.082031 17.375 10.3125 Z\" />\n        </svg>\n      )\n    case 'npm':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 0 10 L 0 21 L 9 21 L 9 23 L 16 23 L 16 21 L 32 21 L 32 10 L 0 10 z M 1.7773438 11.777344 L 8.8886719 11.777344 L 8.890625 11.777344 L 8.890625 19.445312 L 7.1113281 19.445312 L 7.1113281 13.556641 L 5.3339844 13.556641 L 5.3339844 19.445312 L 1.7773438 19.445312 L 1.7773438 11.777344 z M 10.667969 11.777344 L 17.777344 11.777344 L 17.779297 11.777344 L 17.779297 19.443359 L 14.222656 19.443359 L 14.222656 21.222656 L 10.667969 21.222656 L 10.667969 11.777344 z M 19.556641 11.777344 L 30.222656 11.777344 L 30.224609 11.777344 L 30.224609 19.445312 L 28.445312 19.445312 L 28.445312 13.556641 L 26.667969 13.556641 L 26.667969 19.445312 L 24.890625 19.445312 L 24.890625 13.556641 L 23.111328 13.556641 L 23.111328 19.445312 L 19.556641 19.445312 L 19.556641 11.777344 z M 14.222656 13.556641 L 14.222656 17.667969 L 16 17.667969 L 16 13.556641 L 14.222656 13.556641 z\" />\n        </svg>\n      )\n    case 'plus':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 16 3 C 8.832031 3 3 8.832031 3 16 C 3 23.167969 8.832031 29 16 29 C 23.167969 29 29 23.167969 29 16 C 29 8.832031 23.167969 3 16 3 Z M 16 5 C 22.085938 5 27 9.914063 27 16 C 27 22.085938 22.085938 27 16 27 C 9.914063 27 5 22.085938 5 16 C 5 9.914063 9.914063 5 16 5 Z M 15 10 L 15 15 L 10 15 L 10 17 L 15 17 L 15 22 L 17 22 L 17 17 L 22 17 L 22 15 L 17 15 L 17 10 Z\" />\n        </svg>\n      )\n    case 'search':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 19 3 C 13.488281 3 9 7.488281 9 13 C 9 15.394531 9.839844 17.589844 11.25 19.3125 L 3.28125 27.28125 L 4.71875 28.71875 L 12.6875 20.75 C 14.410156 22.160156 16.605469 23 19 23 C 24.511719 23 29 18.511719 29 13 C 29 7.488281 24.511719 3 19 3 Z M 19 5 C 23.429688 5 27 8.570313 27 13 C 27 17.429688 23.429688 21 19 21 C 14.570313 21 11 17.429688 11 13 C 11 8.570313 14.570313 5 19 5 Z\" />\n        </svg>\n      )\n    case 'sun':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 15 3 L 15 8 L 17 8 L 17 3 Z M 7.5 6.09375 L 6.09375 7.5 L 9.625 11.0625 L 11.0625 9.625 Z M 24.5 6.09375 L 20.9375 9.625 L 22.375 11.0625 L 25.90625 7.5 Z M 16 9 C 12.144531 9 9 12.144531 9 16 C 9 19.855469 12.144531 23 16 23 C 19.855469 23 23 19.855469 23 16 C 23 12.144531 19.855469 9 16 9 Z M 16 11 C 18.773438 11 21 13.226563 21 16 C 21 18.773438 18.773438 21 16 21 C 13.226563 21 11 18.773438 11 16 C 11 13.226563 13.226563 11 16 11 Z M 3 15 L 3 17 L 8 17 L 8 15 Z M 24 15 L 24 17 L 29 17 L 29 15 Z M 9.625 20.9375 L 6.09375 24.5 L 7.5 25.90625 L 11.0625 22.375 Z M 22.375 20.9375 L 20.9375 22.375 L 24.5 25.90625 L 25.90625 24.5 Z M 15 24 L 15 29 L 17 29 L 17 24 Z\" />\n        </svg>\n      )\n    case 'twitter':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 30 30\" {...rest}>\n          <path d=\"M26.37,26l-8.795-12.822l0.015,0.012L25.52,4h-2.65l-6.46,7.48L11.28,4H4.33l8.211,11.971L12.54,15.97L3.88,26h2.65 l7.182-8.322L19.42,26H26.37z M10.23,6l12.34,18h-2.1L8.12,6H10.23z\" />\n        </svg>\n      )\n    case 'warning':\n      return (\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\" {...rest}>\n          <path d=\"M 16 4 C 9.382813 4 4 9.382813 4 16 C 4 22.617188 9.382813 28 16 28 C 22.617188 28 28 22.617188 28 16 C 28 9.382813 22.617188 4 16 4 Z M 16 6 C 21.535156 6 26 10.464844 26 16 C 26 21.535156 21.535156 26 16 26 C 10.464844 26 6 21.535156 6 16 C 6 10.464844 10.464844 6 16 6 Z M 15 10 L 15 18 L 17 18 L 17 10 Z M 15 20 L 15 22 L 17 22 L 17 20 Z\" />\n        </svg>\n      )\n    default:\n      return null\n  }\n}\n"
  },
  {
    "path": "website/src/components/inline-code.js",
    "content": "import cx from 'classnames'\n\nexport const InlineCode = ({ dark = false, children }) => {\n  return (\n    <code\n      className={cx(\n        'relative -top-px rounded px-1 py-0.5 text-sm lg:text-base',\n        !dark\n          ? 'bg-gray-100 text-black dark:bg-gray-800 dark:text-gray-300'\n          : 'bg-gray-200 text-black dark:bg-gray-700 dark:text-gray-200',\n      )}\n    >\n      {children}\n    </code>\n  )\n}\n"
  },
  {
    "path": "website/src/components/intro.js",
    "content": "import { InlineCode } from '../components/inline-code.js'\nimport { Jotai } from '../components/jotai.js'\n\nexport const Intro = () => {\n  return (\n    <header>\n      <Jotai className=\"max-w-xs lg:hidden\" />\n      <div className=\"mt-8 flex items-center space-x-4 sm:space-x-8 sm:px-16 lg:mt-0 lg:px-0\">\n        <div className=\"relative w-1/3 max-w-[215px] lg:w-1/4\">\n          <img\n            src=\"https://cdn.candycode.com/jotai/jotai-mascot.png\"\n            title=\"Atomikku, the Jotai mascot\"\n            alt=\"Atomikku, the Jotai mascot\"\n          />\n          <div className=\"absolute -right-2 -bottom-3 z-20 inline-flex h-10 w-10 items-center justify-center rounded-full bg-black text-lg font-black text-white shadow-md dark:bg-white dark:text-black dark:!shadow-none lg:-right-4 lg:-bottom-6 lg:h-[4.5rem] lg:w-[4.5rem] lg:text-[2rem]\">\n            v2\n          </div>\n        </div>\n        <div className=\"relative w-2/3 space-y-4 rounded-xl bg-gray-100 p-4 text-sm leading-snug text-gray-800 dark:bg-gray-900 dark:text-gray-300 sm:text-base md:text-lg lg:w-3/4 lg:p-8 lg:leading-normal after:absolute after:left-0 after:top-1/2 after:w-0 after:h-0 after:-ml-6 after:-mt-4 after:border-solid after:border-transparent after:border-t-[16px] after:border-r-[24px] after:border-b-[16px] after:border-l-0 after:border-r-[#f5f5f5] after:dark:!border-r-[#171717]\">\n          <div>Welcome to Jotai v2!</div>\n          <div>\n            Fully compatible with React 18 and the upcoming{' '}\n            <InlineCode dark>use</InlineCode> hook. Now with a store interface\n            that can be used outside of React.\n          </div>\n          <div>Enjoy the new “Getting started” experience below!</div>\n        </div>\n      </div>\n    </header>\n  )\n}\n"
  },
  {
    "path": "website/src/components/jotai.js",
    "content": "import cx from 'classnames'\nimport { Link } from 'gatsby'\nimport { Logo } from '../components/logo.js'\n\nexport const Jotai = ({ isDocs = false, small = false, ...rest }) => {\n  return (\n    <div {...rest}>\n      <Headline mainTitle={!isDocs}>\n        <Link to=\"/\" className=\"inline-block rounded-lg focus:ring-offset-4\">\n          <Logo\n            className={cx(\n              isDocs\n                ? 'text-gray-300 transition duration-300 ease-in-out hover:text-black dark:text-white dark:hover:text-white '\n                : 'text-black dark:text-white',\n              !small\n                ? 'w-full max-w-[12rem] lg:max-w-[16rem] 2xl:max-w-[18rem]'\n                : 'w-[4rem]',\n            )}\n          />\n        </Link>\n        <span className=\"sr-only\">Jotai</span>\n      </Headline>\n      <div\n        className={cx(\n          !small\n            ? 'mt-2 space-x-6 text-gray-400 lg:space-x-4 2xl:mt-6 2xl:space-x-6'\n            : 'mt-1 space-x-2 text-gray-350 dark:text-gray-500',\n          'flex items-center',\n        )}\n      >\n        <div\n          className={cx(\n            !small ? 'text-lg 2xl:text-xl' : 'text-xs',\n            'whitespace-nowrap',\n          )}\n        >\n          状態\n        </div>\n        <div\n          className={cx(\n            !small\n              ? 'text-sm leading-snug 2xl:text-base'\n              : 'text-xs leading-tight',\n          )}\n        >\n          Primitive and flexible state management for React\n        </div>\n      </div>\n    </div>\n  )\n}\n\nconst Headline = ({ mainTitle = false, children, ...rest }) => {\n  return mainTitle ? (\n    <h1 {...rest}>{children}</h1>\n  ) : (\n    <h2 {...rest}>{children}</h2>\n  )\n}\n"
  },
  {
    "path": "website/src/components/layout.js",
    "content": "import { ClientOnly } from '../components/client-only.js'\nimport { Footer } from '../components/footer.js'\nimport { Main } from '../components/main.js'\nimport { Menu } from '../components/menu.js'\nimport { SearchModal } from '../components/search-modal.js'\nimport { Shelf } from '../components/shelf.js'\nimport { Sidebar } from '../components/sidebar.js'\nimport { SupportModal } from '../components/support-modal.js'\nimport { Toggle } from '../components/toggle.js'\nimport { Wrapper } from '../components/wrapper.js'\n\nexport const Layout = ({ isDocs = false, children }) => {\n  return (\n    <>\n      <Wrapper>\n        <Sidebar isDocs={isDocs} />\n        <Main>\n          <div className=\"prose\">{children}</div>\n          <Footer />\n        </Main>\n        <Shelf />\n      </Wrapper>\n      <ClientOnly>\n        <Toggle />\n      </ClientOnly>\n      <SearchModal />\n      <SupportModal />\n      <Menu />\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/logo-cloud.js",
    "content": "import cx from 'classnames'\nimport { ExternalLink } from '../components/external-link.js'\n\nexport const LogoCloud = () => {\n  return (\n    <div className=\"mx-auto grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-6 2xl:-mx-6\">\n      <Logo to=\"https://about.meta.com/\">\n        <img\n          src=\"https://cdn.candycode.com/jotai/logos/meta-current.svg\"\n          alt=\"\"\n          className=\"w-full opacity-50 transition duration-300 ease-in-out dark:invert\"\n        />\n        <HoverLogo>\n          <img\n            src=\"https://cdn.candycode.com/jotai/logos/meta-color.svg\"\n            alt=\"\"\n            className=\"w-full\"\n            aria-hidden\n          />\n        </HoverLogo>\n      </Logo>\n      <Logo to=\"https://candycode.com/\">\n        <img\n          src=\"https://cdn.candycode.com/jotai/logos/candycode-current.svg\"\n          alt=\"candycode alternative graphic design web development agency\"\n          className=\"aspect-[16/9] w-full opacity-50 transition duration-300 ease-in-out dark:invert\"\n        />\n        <HoverLogo>\n          <img\n            src=\"https://cdn.candycode.com/jotai/logos/candycode-color.svg\"\n            alt=\"\"\n            className=\"aspect-[16/9] w-full\"\n            aria-hidden\n          />\n        </HoverLogo>\n      </Logo>\n      <Logo to=\"https://www.adobe.com/\">\n        <img\n          src=\"https://cdn.candycode.com/jotai/logos/adobe-current.svg\"\n          alt=\"Adobe\"\n          className=\"w-full px-1 opacity-50 transition duration-300 ease-in-out dark:invert lg:px-2\"\n        />\n        <HoverLogo>\n          <img\n            src=\"https://cdn.candycode.com/jotai/logos/adobe-color.svg\"\n            alt=\"\"\n            className=\"w-full px-1 lg:px-2\"\n            aria-hidden\n          />\n        </HoverLogo>\n      </Logo>\n      <Logo to=\"https://ping.gg\">\n        <img\n          src=\"https://cdn.candycode.com/jotai/logos/ping-current.svg\"\n          alt=\"Ping Labs\"\n          className=\"aspect-[24/9] w-full opacity-50 transition duration-300 ease-in-out dark:invert\"\n        />\n        <HoverLogo>\n          <img\n            src=\"https://cdn.candycode.com/jotai/logos/ping-color.svg\"\n            alt=\"\"\n            className=\"aspect-[24/9] w-full\"\n            aria-hidden\n          />\n        </HoverLogo>\n      </Logo>\n      <Logo to=\"https://www.tiktok.com/\">\n        <img\n          src=\"https://cdn.candycode.com/jotai/logos/tiktok-current.svg\"\n          alt=\"TokTok\"\n          className=\"w-full px-1 opacity-50 transition duration-300 ease-in-out dark:invert lg:px-2\"\n        />\n        <HoverLogo>\n          <img\n            src=\"https://cdn.candycode.com/jotai/logos/tiktok-color.svg\"\n            alt=\"\"\n            className=\"w-full px-1 lg:px-2\"\n            aria-hidden\n          />\n        </HoverLogo>\n      </Logo>\n      <Logo to=\"https://uniswap.org/\">\n        <img\n          src=\"https://cdn.candycode.com/jotai/logos/uniswap-current.svg\"\n          alt=\"Uniswap\"\n          className=\"aspect-[16/9] w-full opacity-50 transition duration-300 ease-in-out dark:invert\"\n        />\n        <HoverLogo>\n          <img\n            src=\"https://cdn.candycode.com/jotai/logos/uniswap-color.svg\"\n            alt=\"\"\n            className=\"aspect-[16/9] w-full\"\n            aria-hidden\n          />\n        </HoverLogo>\n      </Logo>\n    </div>\n  )\n}\n\nconst Logo = ({ to, className = '', children }) => {\n  return (\n    <ExternalLink\n      to={to}\n      className={cx(\n        'group relative flex aspect-video items-center justify-center rounded-lg bg-gray-100 px-6 transition duration-300 ease-in-out hover:!bg-black dark:bg-gray-900',\n        className,\n      )}\n    >\n      {children}\n    </ExternalLink>\n  )\n}\n\nconst HoverLogo = ({ children }) => {\n  return (\n    <div className=\"absolute inset-0 flex h-full w-full items-center justify-center px-6 opacity-0 transition duration-300 ease-in-out group-hover:opacity-100 text-white\">\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/logo.js",
    "content": "export const Logo = ({ ...props }) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 289.19 99.77\"\n      {...props}\n    >\n      <title>Jotai</title>\n      <path\n        d=\"M42.36,5.32H61.82V70.23a29.46,29.46,0,0,1-4,15.61A27.19,27.19,0,0,1,46.64,96.07a36.26,36.26,0,0,1-16.59,3.61,37.56,37.56,0,0,1-15.25-3A24.3,24.3,0,0,1,4,87.59Q0,81.5,0,72.23H19.59c.06,3.69,1.13,6.57,3.21,8.61a11.21,11.21,0,0,0,8.25,3.07q11.22,0,11.31-13.68Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M105,99.77q-10.59,0-18.29-4.52A30.54,30.54,0,0,1,74.82,82.61a40.52,40.52,0,0,1-4.18-18.84,40.75,40.75,0,0,1,4.18-18.93A30.6,30.6,0,0,1,86.71,32.2,35.52,35.52,0,0,1,105,27.68a35.58,35.58,0,0,1,18.3,4.52,30.57,30.57,0,0,1,11.88,12.64,40.76,40.76,0,0,1,4.19,18.93,40.52,40.52,0,0,1-4.19,18.84A30.51,30.51,0,0,1,123.3,95.25Q115.59,99.78,105,99.77ZM127.14,5.32v10.5H82.87V5.32Zm-22,79.45a12,12,0,0,0,10.89-6q3.7-6,3.7-15.13T116,48.48a12,12,0,0,0-10.89-6,12.15,12.15,0,0,0-11,6q-3.73,6-3.73,15.16t3.73,15.13A12.16,12.16,0,0,0,105.09,84.77Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M186.3,28.59V43.14H173.16V77q0,4,1.82,5.4a7.5,7.5,0,0,0,4.73,1.41,14.72,14.72,0,0,0,2.72-.25l2.09-.38,3,14.41c-1,.3-2.33.66-4.09,1.06a34.13,34.13,0,0,1-6.41.75q-10.55.47-16.93-4.56T153.8,79.5V43.14h-9.55V28.59h9.55V11.86h19.36V28.59Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M216,99.73q-10,0-16.59-5.23t-6.59-15.59q0-7.81,3.68-12.27a21.19,21.19,0,0,1,9.66-6.53A54.78,54.78,0,0,1,219,57.41a98.57,98.57,0,0,0,13-1.91q3.92-1,3.91-4.36v-.28a8.42,8.42,0,0,0-2.7-6.68q-2.72-2.35-7.66-2.36a13.77,13.77,0,0,0-8.32,2.27,10.7,10.7,0,0,0-4.09,5.77l-17.91-1.45a23.88,23.88,0,0,1,9.93-15.14q7.94-5.58,20.48-5.59a42.24,42.24,0,0,1,14.54,2.46,24.19,24.19,0,0,1,10.94,7.66q4.16,5.21,4.16,13.52V98.41H236.92V88.73h-.54a20.18,20.18,0,0,1-7.62,7.93Q223.69,99.73,216,99.73Zm5.54-13.37A15,15,0,0,0,232,82.66a11.94,11.94,0,0,0,4.09-9.2V66.05a11.38,11.38,0,0,1-3.52,1.36c-1.6.39-3.29.73-5.1,1s-3.41.54-4.84.75a19.19,19.19,0,0,0-8.2,2.87,7.06,7.06,0,0,0-3.11,6.22,6.94,6.94,0,0,0,2.88,6A12.43,12.43,0,0,0,221.51,86.36Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M278.69,19.59a10.41,10.41,0,0,1-7.37-2.89,9.14,9.14,0,0,1-3.09-6.93,9.1,9.1,0,0,1,3.09-6.91,11,11,0,0,1,14.78,0,9.1,9.1,0,0,1,3.09,6.91,9.14,9.14,0,0,1-3.09,6.93A10.45,10.45,0,0,1,278.69,19.59ZM269,98.41V28.59h19.36V98.41Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "website/src/components/main.js",
    "content": "export const Main = ({ children, ...rest }) => {\n  return (\n    <main\n      className=\"p-6 sm:p-8 lg:mt-8 lg:max-w-5xl lg:flex-shrink xl:p-16 grow\"\n      {...rest}\n    >\n      {children}\n    </main>\n  )\n}\n"
  },
  {
    "path": "website/src/components/mdx.js",
    "content": "import { getAnchor } from '../utils/index.js'\n\nexport const H2 = ({ children }) => {\n  const anchor = getAnchor(children)\n  const link = `#${anchor}`\n\n  return (\n    <h2 id={anchor}>\n      <a href={link}>{children}</a>\n    </h2>\n  )\n}\n\nexport const H3 = ({ children }) => {\n  const anchor = getAnchor(children)\n  const link = `#${anchor}`\n\n  return (\n    <h3 id={anchor}>\n      <a href={link}>{children}</a>\n    </h3>\n  )\n}\n\nexport const H4 = ({ children }) => {\n  const anchor = getAnchor(children)\n  const link = `#${anchor}`\n\n  return (\n    <h4 id={anchor}>\n      <a href={link}>{children}</a>\n    </h4>\n  )\n}\n\nexport const H5 = ({ children }) => {\n  const anchor = getAnchor(children)\n  const link = `#${anchor}`\n\n  return (\n    <h5 id={anchor}>\n      <a href={link}>{children}</a>\n    </h5>\n  )\n}\n\nexport const A = ({ href, children, ...rest }) => {\n  if (href.startsWith('http')) {\n    return (\n      <a href={href} target=\"_blank\" rel=\"noreferrer\" {...rest}>\n        {children}\n      </a>\n    )\n  }\n\n  const newHref = href.replace('.mdx', '')\n\n  return (\n    <a href={newHref} {...rest}>\n      {children}\n    </a>\n  )\n}\n"
  },
  {
    "path": "website/src/components/menu.js",
    "content": "import cx from 'classnames'\nimport { useAtom } from 'jotai'\nimport { menuAtom } from '../atoms/index.js'\nimport { Button } from '../components/button.js'\nimport { Docs } from '../components/docs.js'\nimport { Icon } from '../components/icon.js'\nimport { SearchButton } from '../components/search-button.js'\nimport { useOnEscape } from '../hooks/index.js'\n\nexport const Menu = () => {\n  const [isMenuOpen, setIsMenuOpen] = useAtom(menuAtom)\n\n  useOnEscape(() => setIsMenuOpen(false))\n\n  return (\n    <div\n      className={cx(\n        isMenuOpen ? 'opacity-100 visible' : 'opacity-0 invisible',\n        'fixed inset-0 z-50 flex h-screen max-h-screen items-end bg-black/75 p-4 sm:p-6 lg:p-8',\n      )}\n    >\n      <div className=\"max-h-full h-full w-full overflow-y-scroll !overscroll-none rounded-lg border border-gray-300 bg-white p-8 shadow-2xl dark:border-gray-800 dark:bg-gray-950 dark:!shadow-none lg:!overflow-y-auto lg:p-16\">\n        <div className=\"px-3 pb-16 sm:pb-0 max-w-[1920px] mx-auto\">\n          <div className=\"-mx-3 mb-6 lg:hidden\">\n            <SearchButton className=\"w-full\" />\n          </div>\n          <h2 className=\"hidden 2xl:block mb-8 2xl:mb-16 text-4xl font-extrabold leading-tight tracking-tight text-black dark:text-gray-50 lg:text-7xl lg:tracking-tighter\">\n            Jotai docs\n          </h2>\n          <Docs className=\"my-8 space-y-8 lg:my-0 lg:space-y-0 lg:grid lg:grid-cols-5 lg:gap-8\" />\n        </div>\n        <div className=\"z-70 fixed left-8 right-8 bottom-8 sm:left-auto sm:right-16 sm:bottom-16 lg:bottom-auto lg:top-16\">\n          <Button\n            icon=\"close\"\n            onClick={() => setIsMenuOpen(false)}\n            className=\"w-full font-bold uppercase tracking-wider lg:hidden\"\n            dark\n          >\n            Close\n          </Button>\n          <button\n            onClick={() => setIsMenuOpen(false)}\n            className=\"hidden lg:block\"\n            aria-label=\"Close\"\n          >\n            <Icon\n              icon=\"close\"\n              className=\"w-8 h-8 text-gray-700 dark:text-gray-300 dark:group-hover:text-black' : 'text-gray-300 flex-shrink-0 fill-current object-contain transition ease-in-out duration-300\"\n            />\n          </button>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/meta.js",
    "content": "import { graphql, useStaticQuery } from 'gatsby'\n\nexport const Meta = ({ lang = 'en', title, description, uri }) => {\n  const data = useStaticQuery(staticQuery)\n\n  const { site } = data\n\n  const siteTitle = site.siteMetadata.title\n  const siteUrl = site.siteMetadata.siteUrl\n  const siteIcon = `/favicon.svg`\n  const socialMediaCardImage = `https://cdn.candycode.com/jotai/jotai-opengraph-v2.png`\n  const shortName = site.siteMetadata.shortName\n\n  const pageTitle = title\n    ? `${title} — ${title.length <= 10 ? siteTitle : shortName}`\n    : siteTitle\n  const pageDescription = description || site.siteMetadata.description\n  const pageUrl = uri ? `${siteUrl}/${uri}` : siteUrl\n\n  return (\n    <>\n      <title>{pageTitle}</title>\n      <meta property=\"description\" content={pageDescription} />\n      <meta property=\"og:locale\" content={lang} />\n      <meta property=\"og:site_name\" content={shortName} />\n      <meta property=\"og:title\" content={pageTitle} />\n      <meta property=\"og:description\" content={pageDescription} />\n      <meta property=\"og:type\" content=\"website\" />\n      <meta property=\"og:image\" content={socialMediaCardImage} />\n      <meta property=\"og:image:url\" content={socialMediaCardImage} />\n      <meta property=\"og:image:secure_url\" content={socialMediaCardImage} />\n      <meta property=\"og:image:width\" content=\"1200\" />\n      <meta property=\"og:image:height\" content=\"630\" />\n      <meta property=\"og:url\" content={pageUrl} />\n      <meta property=\"twitter:card\" content=\"summary_large_image\" />\n      <link rel=\"icon\" type=\"image/svg+xml\" href={siteIcon} />\n      <link rel=\"canonical\" href={pageUrl} />\n      <link rel=\"dns-prefetch\" href=\"appid-dsn.algolia.net\" />\n      <link\n        rel=\"preconnect\"\n        href=\"https://98qz5x9lgr-dsn.algolia.net\"\n        crossOrigin=\"true\"\n      />\n      <link\n        rel=\"preconnect\"\n        href=\"https://cdn.candycode.com\"\n        crossOrigin=\"true\"\n      />\n      <link\n        rel=\"preconnect\"\n        href=\"https://storage.googleapis.com\"\n        crossOrigin=\"true\"\n      />\n    </>\n  )\n}\n\nconst staticQuery = graphql`\n  query {\n    site {\n      siteMetadata {\n        title\n        description\n        siteUrl\n        shortName\n      }\n    }\n  }\n`\n"
  },
  {
    "path": "website/src/components/modal.js",
    "content": "import { Dialog } from '@headlessui/react'\nimport { RemoveScroll } from 'react-remove-scroll'\n\nexport const Modal = ({ isOpen, onClose, children, ...rest }) => {\n  return (\n    <Dialog open={isOpen} onClose={onClose} {...rest}>\n      <div\n        className=\"z-100 fixed top-0 right-0 bottom-0 h-full w-8 bg-white dark:bg-gray-950\"\n        aria-hidden=\"true\"\n      />\n      <div className=\"fixed inset-0 h-full w-full z-[1000] flex justify-center bg-black/50 p-8 backdrop-blur sm:p-12 2xl:p-32\">\n        <RemoveScroll className=\"w-full max-w-3xl\">\n          <Dialog.Panel className=\"z-[1001] min-w-full overflow-y-auto max-h-full\">\n            {children}\n          </Dialog.Panel>\n        </RemoveScroll>\n      </div>\n    </Dialog>\n  )\n}\n"
  },
  {
    "path": "website/src/components/search-button.js",
    "content": "import { useSetAtom } from 'jotai'\nimport { searchAtom } from '../atoms/index.js'\nimport { Button } from '../components/button.js'\n\nexport const SearchButton = (props) => {\n  const setIsSearchOpen = useSetAtom(searchAtom)\n\n  return (\n    <Button onClick={() => setIsSearchOpen(true)} icon=\"search\" dark {...props}>\n      Search...\n    </Button>\n  )\n}\n"
  },
  {
    "path": "website/src/components/search-modal.js",
    "content": "import { useCallback, useMemo, useState } from 'react'\nimport algoliasearch from 'algoliasearch/lite.js'\nimport cx from 'classnames'\nimport { Link } from 'gatsby'\nimport { useAtom, useSetAtom } from 'jotai'\nimport throttle from 'just-throttle'\nimport {\n  Hits,\n  InstantSearch,\n  useInstantSearch,\n  useSearchBox,\n} from 'react-instantsearch-hooks-web'\nimport { searchAtom } from '../atoms/index.js'\nimport { Button } from '../components/button.js'\nimport { Icon } from '../components/icon.js'\nimport { Modal } from '../components/modal.js'\n\nconst searchClient = algoliasearch(\n  process.env.GATSBY_ALGOLIA_APP_ID,\n  process.env.GATSBY_ALGOLIA_SEARCH_KEY,\n)\n\nexport const SearchModal = () => {\n  const [isSearchOpen, setIsSearchOpen] = useAtom(searchAtom)\n\n  const onClose = useCallback(() => {\n    setIsSearchOpen(false)\n  }, [setIsSearchOpen])\n\n  return (\n    <Modal isOpen={isSearchOpen} onClose={onClose}>\n      <InstantSearch searchClient={searchClient} indexName=\"Docs\">\n        <CustomSearchBox />\n        <Boundary fallback={null}>\n          <div className=\"overflow-hidden rounded-b-lg\">\n            <Hits\n              hitComponent={Hit}\n              className=\"max-h-[400px] overflow-y-scroll border-l border-r border-b border-gray-300 bg-white p-8 pb-0 dark:border-gray-800 dark:bg-gray-950\"\n            />\n          </div>\n        </Boundary>\n      </InstantSearch>\n      <div className=\"flex justify-end p-8 lg:hidden\">\n        <Button\n          icon=\"close\"\n          onClick={() => setIsSearchOpen(false)}\n          className=\"w-full font-bold uppercase tracking-wider lg:w-auto\"\n          dark\n        >\n          Close\n        </Button>\n      </div>\n    </Modal>\n  )\n}\n\nconst Boundary = ({ children, fallback }) => {\n  const { indexUiState, results } = useInstantSearch()\n\n  if (!indexUiState.query) {\n    return fallback\n  }\n\n  if (results.nbHits === 0) {\n    return (\n      <div className=\"flex items-center space-x-3 rounded-b-lg border-l border-r border-b border-gray-300 bg-white p-8 dark:border-gray-800 dark:bg-gray-950\">\n        <div>\n          <Icon icon=\"warning\" className=\"h-6 w-6 fill-current text-red-400\" />\n        </div>\n        <div className=\"text-lg font-semibold text-black dark:text-white\">\n          No results have been found for “{indexUiState.query}”. Please revise\n          your query.\n        </div>\n      </div>\n    )\n  }\n\n  return children\n}\n\nconst CustomSearchBox = (props) => {\n  const [query, setQuery] = useState('')\n\n  const { refine } = useSearchBox(props)\n\n  const throttledRefine = useMemo(\n    () => throttle((value) => refine(value), 200, { trailing: true }),\n    [refine],\n  )\n\n  const onChange = useCallback(\n    (event) => {\n      const newQuery = event.currentTarget.value\n      setQuery(newQuery)\n      throttledRefine(newQuery)\n    },\n    [throttledRefine],\n  )\n\n  return (\n    <div className=\"relative flex items-center\">\n      <input\n        type=\"search\"\n        placeholder=\"Search here...\"\n        value={query}\n        onChange={onChange}\n        autoFocus\n        className={cx(\n          query.length === 0 ? 'rounded-lg' : 'rounded-t-lg',\n          'dark:focus-border-gray-800 flex w-full items-center border border-gray-300 bg-white px-6 py-3 text-lg text-black ring-0 focus:border-gray-300 focus:ring-0 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-200',\n        )}\n      />\n      <a\n        href=\"https://algolia.com\"\n        target=\"_blank\"\n        rel=\"noreferrer\"\n        className=\"absolute right-4 z-10 block\"\n      >\n        <img src=\"/search-by-algolia.svg\" alt=\"Search by Algolia\" aria-hidden />\n      </a>\n    </div>\n  )\n}\n\nconst Hit = ({ hit }) => {\n  const { title, excerpt, slug, level } = hit\n  const setIsSearchOpen = useSetAtom(searchAtom)\n\n  return (\n    <Link\n      to={`/docs/${slug}`}\n      onClick={() => setIsSearchOpen(false)}\n      className=\"group mb-8 flex space-x-3\"\n    >\n      <div>\n        <Icon\n          icon={level === 1 ? 'file' : 'file-blank'}\n          className=\"h-6 w-6 fill-current text-gray-500\"\n        />\n      </div>\n      <div>\n        <div className=\"text-xl font-semibold dark:text-gray-200 inline-flex gap-1.5 items-center\">\n          <span>{title}</span>\n          {level === 2 && (\n            <span className=\"px-1 py-0.5 bg-gray-100 rounded text-xs dark:bg-gray-800\">\n              section\n            </span>\n          )}\n        </div>\n        {excerpt && (\n          <div className=\"mt-1 text-sm leading-snug text-gray-500\">\n            {excerpt}\n          </div>\n        )}\n        <div className=\"mt-1 text-xs font-medium tracking-wider text-gray-400 group-hover:underline\">\n          jotai.org/docs/{slug}\n        </div>\n      </div>\n    </Link>\n  )\n}\n"
  },
  {
    "path": "website/src/components/shelf.js",
    "content": "import { useSetAtom } from 'jotai'\nimport { menuAtom } from '../atoms/index.js'\nimport { Button } from '../components/button.js'\n\nexport const Shelf = () => {\n  const setIsMenuOpen = useSetAtom(menuAtom)\n\n  return (\n    <div className=\"fixed left-0 bottom-0 right-0 lg:hidden\">\n      <div className=\"flex w-full justify-center space-x-4 border-t border-gray-700 bg-gray-900 p-4 dark:border-gray-800\">\n        <Button\n          icon=\"github\"\n          to=\"https://github.com/pmndrs/jotai\"\n          external\n          className=\"font-bold uppercase tracking-wider\"\n          dark\n          small\n        >\n          GitHub\n        </Button>\n        <Button\n          icon=\"npm\"\n          to=\"https://www.npmjs.com/package/jotai\"\n          external\n          className=\"font-bold uppercase tracking-wider\"\n          dark\n          small\n        >\n          npm\n        </Button>\n        <Button\n          icon=\"book\"\n          onClick={() => setIsMenuOpen(true)}\n          className=\"font-bold uppercase tracking-wider\"\n          dark\n          small\n        >\n          Docs\n        </Button>\n      </div>\n      <div className=\"h-4 w-full bg-black\" />\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/sidebar.js",
    "content": "import { useSetAtom } from 'jotai'\nimport { menuAtom, helpAtom } from '../atoms/index.js'\nimport { Button } from '../components/button.js'\nimport { Credits } from '../components/credits.js'\nimport { Jotai } from '../components/jotai.js'\nimport { SearchButton } from '../components/search-button.js'\n\nexport const Sidebar = ({ isDocs = false }) => {\n  const setIsMenuOpen = useSetAtom(menuAtom)\n  const setShowHelp = useSetAtom(helpAtom)\n\n  return (\n    <aside className=\"scrollbar sticky top-0 hidden h-full max-h-screen min-h-full w-full flex-shrink-0 flex-col justify-between overflow-y-scroll overscroll-none p-8 lg:flex lg:max-w-[288px] xl:max-w-[384px] xl:p-16 2xl:max-w-[448px]\">\n      <div className=\"flex-grow\">\n        <Jotai isDocs={isDocs} />\n        <div className=\"mt-8 flex flex-col space-y-4\">\n          <SearchButton />\n          {!isDocs ? (\n            <Button to=\"/docs\" icon=\"book\">\n              Documentation\n            </Button>\n          ) : (\n            <>\n              <Button to=\"/docs\" icon=\"book\" className=\"xl:hidden\">\n                Documentation\n              </Button>\n              <Button\n                onClick={() => setIsMenuOpen(true)}\n                icon=\"book\"\n                className=\"!hidden xl:!flex\"\n              >\n                Documentation\n              </Button>\n            </>\n          )}\n          <Button icon=\"chalkboard\" to=\"https://tutorial.jotai.org\" external>\n            Tutorial\n          </Button>\n          <Button icon=\"help\" onClick={() => setShowHelp(true)}>\n            Support\n          </Button>\n          <Button icon=\"github\" to=\"https://github.com/pmndrs/jotai\" external>\n            Repository\n          </Button>\n          <Button\n            icon=\"npm\"\n            to=\"https://www.npmjs.com/package/jotai\"\n            external\n            className=\"hidden 2xl:inline-flex\"\n          >\n            Package\n          </Button>\n          <Button\n            icon=\"discord\"\n            to=\"https://discord.gg/poimandres\"\n            external\n            className=\"hidden 2xl:inline-flex\"\n          >\n            Community\n          </Button>\n          <Button\n            icon=\"twitter\"\n            to=\"https://twitter.com/jotaijs\"\n            external\n            className=\"hidden 2xl:inline-flex\"\n          >\n            Updates\n          </Button>\n        </div>\n      </div>\n      <div className=\"mt-6 inline-flex flex-col space-y-1.5 text-center\">\n        <Credits />\n      </div>\n    </aside>\n  )\n}\n"
  },
  {
    "path": "website/src/components/stackblitz.js",
    "content": "export const Stackblitz = ({ id, file }) => {\n  return (\n    <div className=\"mb-8 mt-4 overflow-hidden rounded-md border-b border-gray-200 shadow-lg dark:!shadow-none sm:rounded-lg\">\n      <iframe\n        title={id}\n        className=\"h-[400px] w-full\"\n        src={`https://stackblitz.com/edit/${id}?embed=1${file ? `&file=${file}` : ''}&terminal=dev&ctl=1`}\n        allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n        sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n        loading=\"lazy\"\n      />\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/support-modal.js",
    "content": "import { useCallback } from 'react'\nimport { useAtom } from 'jotai'\nimport { helpAtom } from '../atoms/index.js'\nimport { Modal } from '../components/modal.js'\nimport { Support } from '../components/support.js'\n\nexport const SupportModal = () => {\n  const [showHelp, setShowHelp] = useAtom(helpAtom)\n\n  const onClose = useCallback(() => {\n    setShowHelp(false)\n  }, [setShowHelp])\n\n  return (\n    <Modal isOpen={showHelp} onClose={onClose}>\n      <div className=\"rounded-xl bg-gray-100 p-4 text-sm leading-snug text-gray-700 dark:bg-gray-900 dark:text-gray-300 sm:text-base md:text-lg lg:gap-8 lg:p-8 lg:leading-normal\">\n        <Support />\n      </div>\n    </Modal>\n  )\n}\n"
  },
  {
    "path": "website/src/components/support.js",
    "content": "import { useCallback, useState } from 'react'\nimport cx from 'classnames'\nimport { Button } from '../components/button'\n\nexport const Support = () => {\n  const [hasSubmitted, setHasSubmitted] = useState(false)\n  const [hasReceived, setHasReceived] = useState(false)\n  const [name, setName] = useState('')\n  const [email, setEmail] = useState('')\n  const [message, setMessage] = useState('')\n  const [honey, setHoney] = useState('')\n\n  const handleSubmit = useCallback(async () => {\n    if (honey !== '') return\n    if (hasReceived) return\n\n    setHasSubmitted(true)\n\n    const data = {\n      name,\n      email,\n      message,\n    }\n\n    const JSONdata = JSON.stringify(data)\n    const endpoint = '/api/contact'\n    const options = {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n      body: JSONdata,\n    }\n\n    const response = await fetch(endpoint, options)\n\n    if (response.status === 200) {\n      setHasReceived(true)\n      setName('')\n      setEmail('')\n      setMessage('')\n    }\n  }, [name, email, message, hasReceived, honey])\n\n  return (\n    <>\n      <div className=\"flex\">\n        <div className=\"w-1/3 border-r border-gray-200 pr-8 dark:border-gray-800\">\n          <div className=\"text-2xl font-bold leading-tight text-gray-350 dark:text-gray-200 lg:text-3xl\">\n            Self-help\n          </div>\n          <div className=\"text-base\">Check out these helpful resources.</div>\n          <div className=\"mt-4 flex flex-col gap-4\">\n            <Button\n              to=\"https://egghead.io/courses/manage-application-state-with-jotai-atoms-2c3a29f0\"\n              className=\"w-full dark:!bg-gray-950 dark:hover:!bg-teal-950\"\n              bold\n              external\n            >\n              Manage Application State with Jotai Atoms\n              <div className=\"absolute right-0 top-0 translate-x-1/2 -translate-y-1/2 overflow-hidden rounded bg-gray-800 px-1.5 pt-[4px] pb-[3px] text-xs font-semibold uppercase leading-none text-white dark:bg-gray-200 dark:text-black\">\n                Free\n              </div>\n            </Button>\n            <Button\n              to=\"https://daishi.gumroad.com/l/learn-jotai/website_qpiwdj8\"\n              className=\"w-full dark:!bg-gray-950 dark:hover:!bg-teal-950\"\n              bold\n              external\n            >\n              Learn Simplified Jotai\n            </Button>\n            <Button\n              to=\"https://daishi.gumroad.com/l/philosophy-of-jotai-1\"\n              className=\"w-full dark:!bg-gray-950 dark:hover:!bg-teal-950\"\n              bold\n              external\n            >\n              Philosophy of Jotai: Part 1\n            </Button>\n          </div>\n        </div>\n        <div className=\"relative w-2/3 pl-8\">\n          <div>\n            <div className=\"text-2xl font-bold leading-tight text-gray-350 dark:text-gray-200 lg:text-3xl\">\n              Professional support\n            </div>\n            <div className=\"text-base\">\n              Need more help? Request an expert code architecture review from\n              Daishi Kato, the author of Jotai.\n            </div>\n            <div className=\"mt-4 flex flex-col gap-4\">\n              <label>\n                <div>Name</div>\n                <input\n                  type=\"text\"\n                  value={name}\n                  onChange={(event) => setName(event.currentTarget.value)}\n                  className=\"form-input w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-lg focus-within:ring focus-within:ring-blue-400 dark:border-gray-800 dark:bg-gray-950 dark:focus-within:ring-teal-700\"\n                  required\n                />\n              </label>\n              <label>\n                <div>Email</div>\n                <input\n                  type=\"email\"\n                  value={email}\n                  onChange={(event) => setEmail(event.currentTarget.value)}\n                  className=\"form-input w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-lg focus-within:ring focus-within:ring-blue-400 dark:border-gray-800 dark:bg-gray-950 dark:focus-within:ring-teal-700\"\n                  required\n                />\n              </label>\n              <label>\n                <div>Message</div>\n                <textarea\n                  value={message}\n                  onChange={(event) => setMessage(event.currentTarget.value)}\n                  rows={5}\n                  className=\"form-input w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-lg focus-within:ring focus-within:ring-blue-400 dark:border-gray-800 dark:bg-gray-950 dark:focus-within:ring-teal-700\"\n                />\n              </label>\n              <label className=\"sr-only\">\n                <div>Don’t fill this out if you’re human:</div>\n                <input\n                  type=\"text\"\n                  value={honey}\n                  onChange={(event) => setHoney(event.currentTarget.value)}\n                />\n              </label>\n              <div className={cx(hasSubmitted && 'opacity-0')}>\n                <Button\n                  icon=\"message\"\n                  onClick={handleSubmit}\n                  dark\n                  bold\n                  className=\"dark:!bg-black\"\n                >\n                  Send inquiry\n                </Button>\n              </div>\n            </div>\n          </div>\n          {hasSubmitted && (\n            <div className=\"absolute inset-0 z-10 flex h-full w-full items-center justify-center bg-gray-100 text-3xl font-bold leading-tight text-gray-350 dark:bg-gray-900 dark:text-gray-200 lg:text-4xl\">\n              {hasReceived ? <span>Thanks!</span> : <span>Sending...</span>}\n            </div>\n          )}\n        </div>\n      </div>\n      <div>\n        <div className=\"mt-8 flex items-center gap-6\">\n          <a\n            href=\"https://twitter.com/dai_shi\"\n            target=\"_blank\"\n            rel=\"noreferrer\"\n            className=\"flex-shrink-0\"\n          >\n            <img\n              src=\"https://cdn.candycode.com/jotai/daishi.png\"\n              className=\"aspect-square h-28 w-28 rounded-full border border-gray-300 bg-white dark:border-gray-800 dark:bg-black\"\n              alt=\"Daishi Kato\"\n            />\n          </a>\n          <div className=\"text-sm leading-tight\">\n            <span className=\"font-bold\">Daishi Kato</span> is a software\n            engineer who is passionate about open source software. He has been a\n            researcher of peer-to-peer networks and web technologies for\n            decades. His interest is in engineering, and he has been working\n            with start-ups for the last 5 years. He has been actively involved\n            in open source software since the 90s, and his latest work focuses\n            on developing various libraries with JavaScript and React.\n          </div>\n        </div>\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/tabs.js",
    "content": "import { useMemo } from 'react'\n\nexport const Tabs = ({ tabs = {} }) => {\n  const tabContents = useMemo(() => Object.values(tabs), [tabs])\n\n  return (\n    <>\n      <div className=\"space-y-12\">\n        {tabContents.map((content) => (\n          <div>{content}</div>\n        ))}\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/toc.js",
    "content": "import cx from 'classnames'\nimport { Link, graphql, useStaticQuery } from 'gatsby'\n\nexport const TOC = ({ section = '' }) => {\n  const data = useStaticQuery(staticQuery)\n\n  const docs = data.allMdx.nodes.sort(sortDocs)\n  const sectionLinks = parseDocs(docs, section)\n\n  return (\n    <section className=\"mt-4 grid grid-cols-2 gap-4 text-xs sm:text-sm md:grid-cols-3 md:text-base lg:grid-cols-4\">\n      {sectionLinks?.map((sectionLink) => (\n        <Link\n          key={sectionLink.slug}\n          to={`/docs/${sectionLink.slug}`}\n          title={sectionLink.meta.title}\n          className=\"inline-flex aspect-[21/9] items-center justify-center rounded-md border border-gray-200 bg-gray-100 p-2 text-center leading-snug !text-black !no-underline hover:border-blue-200 hover:bg-blue-100 dark:border-gray-800 dark:bg-gray-900 dark:!text-gray-300 dark:!border-none dark:hover:bg-white sm:rounded-lg sm:p-4 dark:hover:!text-black text-sm lg:text-base text-pretty\"\n        >\n          <div\n            className={cx(\n              section === 'recipes' &&\n                sectionLink.meta.title.startsWith('atom') &&\n                'truncate text-xs md:text-sm max-w-full font-mono',\n            )}\n          >\n            {sectionLink.meta.title}\n          </div>\n        </Link>\n      ))}\n    </section>\n  )\n}\n\nconst staticQuery = graphql`\n  query {\n    allMdx {\n      nodes {\n        slug\n        meta: frontmatter {\n          title\n          nav\n          published\n        }\n      }\n    }\n  }\n`\n\nconst sortDocs = (a, b) => a.meta.nav - b.meta.nav\n\nconst parseDocs = (docs, section) => {\n  let directories = []\n  let newDocs = []\n\n  docs.forEach(({ slug }) => {\n    const hasParent = slug.includes('/')\n\n    let parent = undefined\n\n    if (hasParent) {\n      parent = slug.split('/')[0]\n\n      if (!directories.includes(parent)) {\n        directories = [...directories, parent]\n      }\n    }\n  })\n\n  newDocs = [{ contents: [...docs.filter((doc) => !doc.slug.includes('/'))] }]\n\n  directories.forEach((directory) => {\n    newDocs = [\n      ...newDocs,\n      {\n        title: directory,\n        contents: [\n          ...docs.filter(\n            (doc) =>\n              doc.slug.startsWith(directory) && doc.meta.published !== false,\n          ),\n        ],\n      },\n    ]\n  })\n\n  newDocs = newDocs.find((docSection) => docSection.title === section)?.contents\n\n  return newDocs\n}\n"
  },
  {
    "path": "website/src/components/toggle.js",
    "content": "import { useCallback, useEffect } from 'react'\nimport cx from 'classnames'\nimport { useAtom } from 'jotai'\nimport { darkModeAtom } from '../atoms/index.js'\nimport { Icon } from '../components/icon.js'\n\nexport const Toggle = () => {\n  const [darkMode, setDarkMode] = useAtom(darkModeAtom)\n\n  const toggleDarkMode = useCallback(() => {\n    setDarkMode(!darkMode)\n  }, [darkMode, setDarkMode])\n\n  useEffect(() => {\n    if (darkMode) {\n      document.body.classList.add('dark')\n      document.body.classList.remove('light')\n    } else {\n      document.body.classList.add('light')\n      document.body.classList.remove('dark')\n    }\n  }, [darkMode])\n\n  return (\n    <div className=\"absolute top-0 right-0 lg:fixed\">\n      <button\n        type=\"button\"\n        onClick={toggleDarkMode}\n        className=\"relative m-4 inline-flex h-10 w-10 select-none items-center justify-center rounded-full border border-gray-200 bg-gray-100 text-black shadow-md hover:border-blue-200 hover:bg-blue-100 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-300 dark:!shadow-none dark:hover:!border-teal-800 dark:hover:bg-teal-950\"\n      >\n        <div className=\"relative\">\n          <Icon\n            icon=\"sun\"\n            className={cx(\n              darkMode ? 'opacity-100' : 'opacity-0',\n              'h-5 w-5 fill-current transition-opacity duration-300 ease-in-out',\n            )}\n          />\n          <Icon\n            icon=\"moon\"\n            className={cx(\n              darkMode ? 'opacity-0' : 'opacity-100',\n              'absolute left-0 top-0 h-5 w-5 fill-current transition-opacity duration-300 ease-in-out',\n            )}\n          />\n        </div>\n      </button>\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/components/utilities-demo.js",
    "content": "import cx from 'classnames'\nimport { useAtom } from 'jotai'\nimport { darkModeAtom } from '../atoms/index.js'\nimport { ClientOnly } from '../components/client-only.js'\nimport { Code } from '../components/code.js'\n\nexport const UtilitiesDemo = () => {\n  const [darkMode, setDarkMode] = useAtom(darkModeAtom)\n\n  const code = `import { useAtom } from 'jotai'\nimport { atomWithStorage } from 'jotai/utils'\n\n// Set the string key and the initial value\nconst darkModeAtom = atomWithStorage('darkMode', false)\n\nconst Page = () => {\n  // Consume persisted state like any other atom\n  const [darkMode, setDarkMode] = useAtom(darkModeAtom)\n  const toggleDarkMode = () => setDarkMode(!darkMode)\n  return (\n    <>\n      <h1>Welcome to {darkMode ? 'dark' : 'light'} mode!</h1>\n      <button onClick={toggleDarkMode}>toggle theme</button>\n    </>\n  )\n}`\n\n  return (\n    <>\n      <div className=\"py-8\">\n        <ClientOnly>\n          <div\n            className={cx(\n              darkMode\n                ? 'bg-gray-900 text-gray-100'\n                : 'bg-gray-100 text-gray-900',\n              'flex items-center space-x-4 rounded-xl p-4 transition duration-300 ease-in-out lg:space-x-8 lg:p-8',\n            )}\n          >\n            <div>\n              <button\n                onClick={() => setDarkMode(!darkMode)}\n                className={cx(\n                  darkMode ? 'bg-gray-700' : 'bg-gray-300',\n                  'dark:focus-teal-700 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-opacity duration-200 ease-in-out focus:outline-none focus:ring focus:ring-blue-400',\n                )}\n              >\n                <span\n                  className={cx(\n                    darkMode ? 'translate-x-5' : 'translate-x-0',\n                    'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',\n                  )}\n                  aria-hidden=\"true\"\n                />\n              </button>\n            </div>\n            <div className=\"text-sm leading-relaxed lg:text-lg\">\n              This toggle will be persisted between user sessions via\n              localStorage.\n            </div>\n          </div>\n        </ClientOnly>\n      </div>\n      <Code>{code}</Code>\n    </>\n  )\n}\n"
  },
  {
    "path": "website/src/components/wrapper.js",
    "content": "export const Wrapper = ({ children, ...rest }) => {\n  return (\n    <div\n      className=\"relative flex flex-col lg:mx-auto lg:max-w-[1920px] lg:w-full lg:flex-row lg:justify-center lg:min-h-screen\"\n      {...rest}\n    >\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "website/src/hooks/index.js",
    "content": "import { useCallback, useEffect } from 'react'\n\nexport const useOnEscape = (handler) => {\n  const handleEscape = useCallback(\n    ({ code }) => {\n      if (code === 'Escape') {\n        handler()\n      }\n    },\n    [handler],\n  )\n\n  useEffect(() => {\n    document.addEventListener('keydown', handleEscape, false)\n\n    return () => {\n      document.removeEventListener('keydown', handleEscape, false)\n    }\n  }, [handleEscape])\n}\n"
  },
  {
    "path": "website/src/pages/404.js",
    "content": "import { useEffect } from 'react'\nimport { navigate } from 'gatsby'\n\nexport default function NotFoundPage() {\n  useEffect(() => {\n    navigate('/')\n  }, [])\n\n  return null\n}\n"
  },
  {
    "path": "website/src/pages/docs/{Mdx.slug}.js",
    "content": "import { graphql } from 'gatsby'\nimport { MDXRenderer } from 'gatsby-plugin-mdx'\nimport { Jotai } from '../../components/jotai.js'\nimport { Meta } from '../../components/meta.js'\n\nexport default function DocsPage({ data }) {\n  const { frontmatter, body } = data.mdx\n  const { title } = frontmatter\n\n  return (\n    <>\n      <div className=\"mb-4 lg:hidden\">\n        <Jotai isDocsPage small />\n      </div>\n      <h1 className=\"-mt-1\">{title}</h1>\n      <div className=\"text-pretty\">\n        <MDXRenderer>{body}</MDXRenderer>\n      </div>\n    </>\n  )\n}\n\nexport const Head = ({ data }) => {\n  const { slug, frontmatter } = data.mdx\n  const { title, description } = frontmatter\n  const uri = `docs/${slug}`\n\n  return <Meta title={title} description={description} uri={uri} />\n}\n\nexport const pageQuery = graphql`\n  query PageQuery($slug: String) {\n    mdx(slug: { eq: $slug }) {\n      slug\n      frontmatter {\n        title\n        description\n      }\n      body\n    }\n  }\n`\n"
  },
  {
    "path": "website/src/pages/index.js",
    "content": "import { Code } from '../components/code.js'\nimport { CoreDemo } from '../components/core-demo.js'\nimport { ExtensionsDemo } from '../components/extensions-demo.js'\nimport { ExternalLink } from '../components/external-link.js'\nimport { Headline } from '../components/headline.js'\nimport { InlineCode } from '../components/inline-code.js'\nimport { Intro } from '../components/intro.js'\nimport { LogoCloud } from '../components/logo-cloud.js'\nimport { Meta } from '../components/meta.js'\nimport { Tabs } from '../components/tabs.js'\nimport { UtilitiesDemo } from '../components/utilities-demo.js'\n\nexport default function HomePage() {\n  return (\n    <>\n      <Intro />\n      <div className=\"mt-12 space-y-12 lg:mt-24 lg:space-y-24 text-pretty\">\n        <div className=\"space-y-4\">\n          <Headline>Introduction</Headline>\n          <p>\n            Jotai takes an atomic approach to global React state management.\n          </p>\n          <p>\n            Build state by combining atoms and renders are automatically\n            optimized based on atom dependency. This solves the extra re-render\n            issue of React context, eliminates the need for memoization, and\n            provides a similar developer experience to signals while maintaining\n            a declarative programming model.\n          </p>\n          <p>\n            It scales from a simple <InlineCode>useState</InlineCode>{' '}\n            replacement to an enterprise TypeScript application with complex\n            requirements. Plus there are plenty of utilities and extensions to\n            help you along the way!\n          </p>\n          <p>\n            Jotai is trusted in production at innovative companies like these.\n          </p>\n          <LogoCloud />\n        </div>\n        <div className=\"space-y-4\">\n          <Headline>Getting started</Headline>\n          <p className=\"!mb-8\">\n            This walks you through the process of creating a simple Jotai\n            application. It starts with installation, then explores the basics\n            of the core API, and ends with server-side rendering in a React\n            framework.\n          </p>\n          <Tabs tabs={gettingStartedTabs} />\n        </div>\n        <div className=\"space-y-4\">\n          <Headline>API overview</Headline>\n          <Tabs tabs={apiTabs} />\n        </div>\n        <div className=\"space-y-4\">\n          <Headline>Learn more</Headline>\n          <p>\n            Check out the free Egghead course by Daishi, the creator of Jotai.\n          </p>\n          <a\n            href=\"https://egghead.io/courses/manage-application-state-with-jotai-atoms-2c3a29f0\"\n            target=\"_blank\"\n            rel=\"noreferrer\"\n            className=\"mt-4 block\"\n          >\n            <img\n              src=\"https://cdn.candycode.com/jotai/jotai-course-banner.jpg\"\n              className=\"block rounded-md shadow-lg dark:!shadow-none sm:rounded-lg\"\n              alt=\"Jotai course\"\n              title=\"Jotai course\"\n            />\n          </a>\n        </div>\n      </div>\n    </>\n  )\n}\n\nconst apiTabs = {\n  Core: (\n    <section>\n      <h2>Core</h2>\n      <p>\n        Jotai has a very minimal API and is TypeScript oriented. It is as simple\n        to use as React’s integrated <InlineCode>useState</InlineCode> hook, but\n        all state is globally accessible, derived state is easy to implement,\n        and unnecessary re-renders are automatically eliminated.\n      </p>\n      <CoreDemo />\n    </section>\n  ),\n  Utilities: (\n    <section>\n      <h2>Utilities</h2>\n      <p>\n        The Jotai package also includes a <InlineCode>jotai/utils</InlineCode>{' '}\n        bundle. These extra functions add support for persisting an atom in\n        localStorage, hydrating an atom during server-side rendering, creating\n        atoms with Redux-like reducers and action types, and much more.\n      </p>\n      <UtilitiesDemo />\n    </section>\n  ),\n  Extensions: (\n    <section>\n      <h2>Extensions</h2>\n      <p>\n        There are also separate packages for each official extension: tRPC,\n        Immer, Query, XState, URQL, Optics, Relay, location, molecules, cache,\n        and more.\n      </p>\n      <p>\n        Some extensions provide new atom types with alternate write functions\n        such as <InlineCode>atomWithImmer</InlineCode> (Immer) or{' '}\n        <InlineCode>atomWithMachine</InlineCode> (XState).\n      </p>\n      <p>\n        Others provide new atom types with two-way data binding such as{' '}\n        <InlineCode>atomWithLocation</InlineCode> or{' '}\n        <InlineCode>atomWithHash</InlineCode>.\n      </p>\n      <ExtensionsDemo />\n    </section>\n  ),\n}\n\nconst gettingStartedTabs = {\n  Installation: (\n    <section>\n      <h2>Installation</h2>\n      <p>First add Jotai as a dependency to your React project.</p>\n      <Code language=\"bash\">{`# npm\nnpm install jotai\n\n# yarn\nyarn add jotai\n\n# pnpm\npnpm add jotai\n`}</Code>\n    </section>\n  ),\n  'Create atoms': (\n    <section>\n      <h2>Create atoms</h2>\n      <p>First create primitive and derived atoms to build state.</p>\n      <h3>Primitive atoms</h3>\n      <p>\n        A primitive atom can be any type: booleans, numbers, strings, objects,\n        arrays, sets, maps, and so on.\n      </p>\n      <Code>{`import { atom } from 'jotai'\n\nconst countAtom = atom(0)\n\nconst countryAtom = atom('Japan')\n\nconst citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])\n\nexport const animeAtom = atom([\n  {\n    title: 'Ghost in the Shell',\n    year: 1995,\n    watched: true\n  },\n  {\n    title: 'Serial Experiments Lain',\n    year: 1998,\n    watched: false\n  }\n])`}</Code>\n      <h3>Derived atoms</h3>\n      <p>\n        A derived atom can read from other atoms before returning its own value.\n      </p>\n      <Code>{`const progressAtom = atom((get) => {\n  const anime = get(animeAtom)\n  return anime.filter((item) => item.watched).length / anime.length\n})`}</Code>\n    </section>\n  ),\n  'Use atoms': (\n    <section>\n      <h2>Use atoms</h2>\n      <p>Then use atoms within React components to read or write state.</p>\n      <h3>Read and write from same component</h3>\n      <p>\n        When atoms are both read and written within the same component, use the\n        combined <InlineCode>useAtom</InlineCode> hook for simplicity.\n      </p>\n      <Code>{`import { useAtom } from 'jotai'\n\nimport { animeAtom } from './atoms'\n\nconst AnimeApp = () => {\n  const [anime, setAnime] = useAtom(animeAtom)\n\n  return (\n    <>\n      <ul>\n        {anime.map((item) => (\n          <li key={item.title}>{item.title}</li>\n        ))}\n      </ul>\n      <button onClick={() => {\n        setAnime((anime) => [\n          ...anime,\n          {\n            title: 'Cowboy Bebop',\n            year: 1998,\n            watched: false\n          }\n        ])\n      }}>\n        Add Cowboy Bebop\n      </button>\n    </>\n  )\n}`}</Code>\n      <h3>Read and write from separate components</h3>\n      <p>\n        When atom values are only read or written, use the separate{' '}\n        <InlineCode>useAtomValue</InlineCode> and{' '}\n        <InlineCode>useSetAtom</InlineCode> hooks to optimize re-renders.\n      </p>\n      <Code>{`import { useAtomValue, useSetAtom } from 'jotai'\n\nimport { animeAtom } from './atoms'\n\nconst AnimeList = () => {\n  const anime = useAtomValue(animeAtom)\n\n  return (\n    <ul>\n      {anime.map((item) => (\n        <li key={item.title}>{item.title}</li>\n      ))}\n    </ul>\n  )\n}\n\nconst AddAnime = () => {\n  const setAnime = useSetAtom(animeAtom)\n\n  return (\n    <button onClick={() => {\n      setAnime((anime) => [\n        ...anime,\n        {\n          title: 'Cowboy Bebop',\n          year: 1998,\n          watched: false\n        }\n      ])\n    }}>\n      Add Cowboy Bebop\n    </button>\n  )\n}\n\nconst ProgressTracker = () => {\n  const progress = useAtomValue(progressAtom)\n\n  return (\n    <div>{Math.trunc(progress * 100)}% watched</div>\n  )\n}\n\nconst AnimeApp = () => {\n  return (\n    <>\n      <AnimeList />\n      <AddAnime />\n      <ProgressTracker />\n    </>\n  )\n}`}</Code>\n    </section>\n  ),\n  SSR: (\n    <section>\n      <h2>Server-side rendering</h2>\n      <p>\n        If server-side rendering with a framework such as{' '}\n        <ExternalLink to=\"https://nextjs.org\">Next.js</ExternalLink> or{' '}\n        <ExternalLink to=\"https://waku.gg\">Waku</ExternalLink>, make sure to add\n        a Jotai Provider component at the root.\n      </p>\n      <h3>Next.js (app directory)</h3>\n      <p>\n        Create the provider in a separate client component. Then import the\n        provider into the root <InlineCode>layout.js</InlineCode> server\n        component.\n      </p>\n      <Code>{`// ./components/providers.js\n'use client'\n\nimport { Provider } from 'jotai'\n\nexport const Providers = ({ children }) => {\n  return (\n    <Provider>\n      {children}\n    </Provider>\n  )\n}\n\n\n// ./app/layout.js\nimport { Providers } from '../components/providers'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <Providers>\n          {children}\n        </Providers>\n      </body>\n    </html>\n  )\n}\n`}</Code>\n      <h3>Next.js (pages directory)</h3>\n      <p>\n        Create the provider in <InlineCode>_app.js</InlineCode>.\n      </p>\n      <Code>{`// ./pages/_app.js\nimport { Provider } from 'jotai'\n\nexport default function App({ Component, pageProps }) {\n  return (\n    <Provider>\n      <Component {...pageProps} />\n    </Provider>\n  )\n}\n`}</Code>\n      <h3>Waku</h3>\n      <p>\n        Create the provider in a separate client component. Then import the\n        provider into the root layout.\n      </p>\n      <Code>{`// ./src/components/providers.js\n'use client'\n\nimport { Provider } from 'jotai'\n\nexport const Providers = ({ children }) => {\n  return (\n    <Provider>\n      {children}\n    </Provider>\n  )\n}\n\n\n// ./src/pages/_layout.js\nimport { Providers } from '../components/providers'\n\nexport default async function RootLayout({ children }) {\n  return (\n    <Providers>\n      {children}\n    </Providers>\n  )\n}\n`}</Code>\n    </section>\n  ),\n}\n\nexport const Head = () => {\n  return <Meta />\n}\n"
  },
  {
    "path": "website/src/styles/base.css",
    "content": "@tailwind base;\n"
  },
  {
    "path": "website/src/styles/components.css",
    "content": ".prose h1,\n.prose h2,\n.prose h3,\n.prose h4,\n.prose h5 {\n  scroll-margin-top: 3rem;\n}\n\n.prose h1 {\n  @apply text-4xl font-extrabold leading-tight tracking-tight text-black dark:text-gray-50 lg:text-7xl lg:tracking-tighter;\n}\n\n.prose h2 {\n  @apply text-2xl font-bold leading-tight text-gray-350 dark:text-gray-200 lg:text-3xl;\n}\n\n.prose .tabs h2 {\n  @apply text-black dark:text-gray-200;\n}\n\n.prose h2 a code {\n  @apply px-2 py-1 !text-black dark:!text-white;\n  font-size: 0.7em;\n}\n\n.prose * + h2 {\n  @apply mt-12 lg:mt-16;\n}\n\n.prose h3 {\n  @apply mt-6 text-lg font-semibold leading-tight text-gray-700 dark:text-gray-300 lg:mt-8 lg:text-xl;\n}\n\n.prose h3 a code {\n  @apply px-2 py-1 !text-black dark:!text-white;\n  font-size: 0.7em;\n}\n\n.prose h4 {\n  @apply mt-8 text-base font-semibold leading-tight text-gray-600 dark:text-gray-400 lg:text-lg;\n}\n\n.prose h4 a code {\n  @apply px-2 py-1 !text-black dark:!text-white;\n  font-size: 0.7em;\n}\n\n.prose h1 + h2 {\n  @apply !mt-12;\n}\n\n.prose h2 + h3,\n.prose h3 + h4 {\n  @apply mt-2;\n}\n\n.prose h1 a,\n.prose h2 a,\n.prose h3 a,\n.prose h4 a,\n.prose h5 a {\n  all: inherit;\n  @apply inline cursor-pointer;\n}\n\n.prose ol,\n.prose ul {\n  @apply my-2 space-y-1 pl-5 text-sm font-normal leading-normal text-gray-600 dark:text-gray-400 sm:text-base lg:text-lg;\n}\n\n.prose ol {\n  @apply list-decimal;\n}\n\n.prose ul {\n  @apply list-disc marker:text-gray-400 dark:marker:text-gray-600;\n}\n\n.prose li {\n  @apply pl-1;\n}\n\n.prose p {\n  @apply mt-1 text-sm leading-normal text-gray-600 dark:text-gray-400 sm:text-base lg:text-lg;\n}\n\n.prose p + p {\n  @apply mt-2;\n}\n\n.prose h1 + p {\n  @apply mt-8;\n}\n\n.prose a {\n  @apply text-blue-600 underline dark:text-teal-400;\n}\n\n.dark .prose h1 a,\n.dark .prose h2 a,\n.dark .prose h3 a,\n.dark .prose h4 a,\n.dark .prose h5 a {\n  color: inherit;\n}\n\n.prose a code {\n  @apply !text-blue-600 dark:!text-teal-400;\n}\n\n.prose blockquote {\n  @apply my-4 rounded-md border border-gray-200 bg-gray-100 px-4 py-2 dark:border-gray-800 dark:bg-gray-900 sm:rounded-lg;\n}\n\n.prose blockquote a {\n  @apply text-gray-900 dark:text-gray-300;\n}\n\n.prose blockquote p {\n  @apply text-gray-900 dark:text-gray-300;\n}\n\n.prose blockquote a code {\n  @apply !text-blue-900;\n}\n\n.prose hr {\n  @apply my-8 h-px w-full border-none bg-gray-300 dark:bg-gray-700;\n}\n\n@tailwind components;\n"
  },
  {
    "path": "website/src/styles/fonts.css",
    "content": "@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=block');\n@import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=block');\n"
  },
  {
    "path": "website/src/styles/index.css",
    "content": "@import './base.css';\n@import './fonts.css';\n@import './pmndrs.css';\n@import './layout.css';\n@import './components.css';\n@import './utilities.css';\n"
  },
  {
    "path": "website/src/styles/layout.css",
    "content": "html {\n  overflow: visible !important;\n  padding-right: 0 !important;\n}\n\nbody {\n  @apply overflow-x-clip overflow-y-scroll overscroll-none bg-black leading-none font-sans;\n  text-rendering: optimizeLegibility;\n  -webkit-tap-highlight-color: transparent;\n}\n\n#___gatsby {\n  @apply bg-white text-black dark:bg-gray-950 dark:text-gray-50;\n}\n\n#___gatsby > div {\n  opacity: 0;\n  animation: fadeIn cubic-bezier(0.4, 0, 0.2, 1) 0.5s;\n  animation-iteration-count: 1;\n  animation-fill-mode: forwards !important;\n  animation-delay: 0.7s;\n}\n\n@keyframes fadeIn {\n  0% {\n    opacity: 0;\n  }\n  100% {\n    opacity: 1;\n  }\n}\n\npre,\ncode,\npre span,\ncode span {\n  font-family: 'Fira Code', monospace !important;\n}\n\n::selection {\n  @apply bg-black text-white dark:bg-gray-50 dark:text-gray-950;\n}\n\na,\nbutton {\n  @apply transition-all duration-300 ease-in-out;\n}\n\n*:focus {\n  outline: 0 !important;\n}\n\na,\nbutton,\ninput,\nselect,\ntextarea {\n  @apply relative z-0;\n}\n\na:focus,\nbutton:focus,\ninput:focus,\nselect:focus,\ntextarea:focus {\n  @apply z-10 ring-blue-400 dark:!ring-teal-700;\n}\n\ninput[type='search']::-webkit-search-decoration,\ninput[type='search']::-webkit-search-cancel-button,\ninput[type='search']::-webkit-search-results-button,\ninput[type='search']::-webkit-search-results-decoration {\n  @apply appearance-none;\n}\n\nsvg:not([fill]) {\n  fill: currentColor;\n}\n\n*::-webkit-scrollbar {\n  @apply h-full w-4 bg-white dark:bg-gray-950;\n}\n\nbody.dark::-webkit-scrollbar {\n  @apply bg-gray-950;\n}\n\n*::-webkit-scrollbar-track {\n  @apply bg-white dark:bg-gray-950;\n}\n\nbody.dark::-webkit-scrollbar-track {\n  @apply bg-gray-950;\n}\n\n*::-webkit-scrollbar-thumb {\n  @apply rounded-2xl border-4 border-solid border-white bg-gray-350 dark:border-gray-950 dark:bg-gray-800;\n}\n\nbody.dark::-webkit-scrollbar-thumb {\n  @apply border-gray-950 bg-gray-800;\n}\n\n*::-webkit-scrollbar-button {\n  @apply hidden;\n}\n"
  },
  {
    "path": "website/src/styles/pmndrs.css",
    "content": "/**\n * Pmndrs theme for JavaScript, CSS and HTML\n * Loosely based on https://marketplace.visualstudio.com/items?itemName=pmndrs.pmndrs\n * @author Paul Henschel\n */\n\ncode[class*='language-'],\npre[class*='language-'] {\n  word-spacing: normal;\n  word-wrap: normal;\n  tab-size: 2;\n  hyphens: none;\n  font-size: 0.9em;\n  @apply break-normal rounded-md text-left leading-normal text-[#e4f0fb] shadow-lg dark:!shadow-none sm:rounded-lg;\n}\n\n/* Code blocks */\npre[class*='language-'] {\n  @apply my-4 overflow-x-auto whitespace-pre p-6 sm:p-8;\n}\n\n:not(pre) > code[class*='language-'],\npre[class*='language-'] {\n  @apply bg-[#252b37];\n}\n\n.dark :not(pre) > code[class*='language-'],\n.dark pre[class*='language-'] {\n  @apply bg-[#13161c];\n}\n\n/* Inline code */\n:not(pre) > code[class*='language-'] {\n  @apply whitespace-normal rounded-md p-3 shadow-lg dark:!shadow-none;\n}\n\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  @apply text-[#a6accd];\n}\n\n.token.punctuation {\n  @apply text-[#e4f0fb];\n}\n\n.token.namespace {\n  @apply opacity-70;\n}\n\n.token.property,\n.token.tag,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  @apply text-[#e4f0fb];\n}\n\n.token.boolean,\n.token.number {\n  @apply text-[#5de4c7];\n}\n\n.token.selector,\n.token.attr-value,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  @apply text-[#5de4c7];\n}\n\n.token.attr-name,\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string,\n.token.variable {\n  @apply text-[#add7ff];\n}\n\n.token.atrule,\n.token.function,\n.token.class-name {\n  @apply text-[#5de4c7];\n}\n\n.token.keyword {\n  @apply text-[#add7ff];\n}\n\n.token.regex,\n.token.important {\n  @apply text-[#fffac2];\n}\n\n.token.important,\n.token.bold {\n  font-weight: 700;\n}\n\n.token.italic {\n  font-style: italic;\n}\n\n.token.entity {\n  @apply cursor-help;\n}\n"
  },
  {
    "path": "website/src/styles/utilities.css",
    "content": "@tailwind utilities;\n"
  },
  {
    "path": "website/src/utils/index.js",
    "content": "import { Children, isValidElement } from 'react'\nimport kebabCase from 'just-kebab-case'\n\nexport const getAnchor = (value) => {\n  return kebabCase(getTextContent(value).toLowerCase().replaceAll(\"'\", ''))\n}\n\nconst getTextContent = (children) => {\n  let text = ''\n\n  Children.toArray(children).forEach((child) => {\n    if (typeof child === 'string') {\n      text += child\n    } else if (isValidElement(child) && child.props.children) {\n      text += getTextContent(child.props.children)\n    }\n  })\n\n  return text\n}\n"
  },
  {
    "path": "website/static/robots.txt",
    "content": "User-agent: *\nAllow: /\nSitemap: https://jotai.org/sitemap/sitemap-index.xml\n"
  },
  {
    "path": "website/tailwind.config.js",
    "content": "/* eslint-disable import/extensions */\n/* eslint-disable @typescript-eslint/no-var-requires */\nconst colors = require('tailwindcss/colors')\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    './src/**/*.js',\n    './src/**/*.jsx',\n    './src/**/*.ts',\n    './src/**/*.tsx',\n  ],\n  darkMode: 'class',\n  theme: {\n    colors: {\n      transparent: 'transparent',\n      current: 'currentColor',\n      black: colors.black,\n      white: colors.white,\n      gray: {\n        ...colors.neutral,\n        350: '#bcbcbc',\n        650: '#494949',\n        950: '#0c0c0c',\n      },\n      blue: { ...colors.blue, 950: '#0f1d45' },\n      red: { ...colors.red, 950: '#400f0f' },\n      teal: { ...colors.teal, 950: '#0a2725' },\n    },\n    fontFamily: {\n      sans: ['\"Inter\"', 'sans-serif'],\n      mono: ['\"Fira Code\"', 'monospace'],\n    },\n    fontSize: {\n      xs: ['0.75rem'],\n      sm: ['0.875rem'],\n      base: ['1rem'],\n      lg: ['1.125rem'],\n      xl: ['1.25rem'],\n      '2xl': ['1.5rem'],\n      '3xl': ['1.875rem'],\n      '4xl': ['2.25rem'],\n      '5xl': ['3rem'],\n      '6xl': ['3.75rem'],\n      '7xl': ['4.5rem'],\n      '8xl': ['6rem'],\n      '9xl': ['8rem'],\n    },\n  },\n  plugins: [require('@tailwindcss/forms')],\n  future: {\n    hoverOnlyWhenSupported: true,\n  },\n}\n"
  },
  {
    "path": "website/vercel.json",
    "content": "{\n  \"functions\": {\n    \"api/*.js\": {\n      \"memory\": 128,\n      \"maxDuration\": 30\n    }\n  },\n  \"redirects\": [\n    {\n      \"source\": \"/docs/introduction\",\n      \"destination\": \"/docs\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/:slug\",\n      \"destination\": \"/docs/utilities/:slug\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/advanced-recipes/:slug\",\n      \"destination\": \"/docs/recipes/:slug\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/api/babel\",\n      \"destination\": \"/docs/tools/babel\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/api/core\",\n      \"destination\": \"/docs/core/atom\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/api/devtools\",\n      \"destination\": \"/docs/tools/devtools\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/api/devtools\",\n      \"destination\": \"/docs/tools/devtools\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/api/swc\",\n      \"destination\": \"/docs/tools/swc\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/api/utils\",\n      \"destination\": \"/docs/tools/introduction\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-family\",\n      \"destination\": \"/docs/utilities/family\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-with-default\",\n      \"destination\": \"/docs/utilities/resettable\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-with-hash\",\n      \"destination\": \"/docs/extensions/location\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-with-observable\",\n      \"destination\": \"/docs/utilities/async\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-with-reducer\",\n      \"destination\": \"/docs/utilities/reducer\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-with-reset\",\n      \"destination\": \"/docs/utilities/resettable\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/atom-with-storage\",\n      \"destination\": \"/docs/utilities/storage\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/freeze-atom-creator\",\n      \"destination\": \"/docs/tools/devtools\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/freeze-atom\",\n      \"destination\": \"/docs/tools/devtools\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/loadable\",\n      \"destination\": \"/docs/utilities/async\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/reset\",\n      \"destination\": \"/docs/utilities/resettable\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/select-atom\",\n      \"destination\": \"/docs/utilities/select\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/split-atom\",\n      \"destination\": \"/docs/utilities/split\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/use-atom-callback\",\n      \"destination\": \"/docs/utilities/callback\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/use-atom-value\",\n      \"destination\": \"/docs/core/use-atom\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/use-hydrate-atoms\",\n      \"destination\": \"/docs/utilities/ssr\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/use-reducer-atom\",\n      \"destination\": \"/docs/utilities/reducer\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/use-reset-atom\",\n      \"destination\": \"/docs/utilities/resttable\",\n      \"permanent\": false\n    },\n    {\n      \"source\": \"/docs/utils/use-update-atom\",\n      \"destination\": \"/docs/core/use-atom\",\n      \"permanent\": false\n    }\n  ],\n  \"trailingSlash\": false\n}\n"
  }
]