[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\non: [push, pull_request]\njobs:\n  run:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - name: Set up Node 18\n        uses: actions/setup-node@v3\n        with:\n          node-version: 18\n      - name: Install dependencies\n        run: yarn\n      - name: Run tests and collect coverage\n        run: yarn test\n      - name: Upload coverage to Codecov\n        uses: codecov/codecov-action@v3\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\ncoverage/*"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"singleQuote\": true,\n  \"semi\": false,\n  \"printWidth\": 100\n}\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2022-present velopert\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-ko.md",
    "content": "# sangte\n\n[![](https://img.shields.io/npm/v/sangte?style=flat-square)](https://www.npmjs.com/package/sangte)\n[![](https://img.shields.io/bundlephobia/min/sangte?style=flat-square)](https://bundlephobia.com/package/sangte)\n[![](https://img.shields.io/codecov/c/github/velopert/sangte?style=flat-square)](https://app.codecov.io/gh/velopert/sangte)\n\nSangte 는 리액트의 상태 관리 라이브러리입니다. 이 라이브러리는 [Redux Toolkit](https://redux-toolkit.js.org/)과 [Recoil](https://recoiljs.org/)에서 영감을 받아 만들어졌습니다.\n\n> Sangte는 \"상태\"의 발음을 알파벳으로 표기한 것입니다.\n\n## 설치\n\n다음 명령어로 이 라이브러리를 설치하세요:\n\n```\nnpm install sangte\n```\n\nyarn을 사용하신다면:\n\n```\nyarn add sangte\n```\n\n## 왜 Sangte 를 쓰나요?\n\n- 준비해야 할 코드가 적음 (Less boilerplate)\n- 원하는 상태가 업데이트 됐을 때만 리렌더링함\n- 사용하기 쉬움\n- 여러개의 Provider 허용\n- 타입스크립트 지원\n\n## 사용법\n\n### 먼저, 상태를 만드세요\n\n상태를 만드려면 `sangte` 함수를 사용해야 합니다. 이 라이브러리의 상태는 초깃값이 있어야 하며, 상태를 업데이트하는 액션을 가질 수 있습니다. 액션은 선택사항입니다.\n\nSangte는 내부적으로 [immer](https://immerjs.github.io/immer/)를 사용하여 상태를 업데이트합니다. 그래서 상태를 직접 변경하면서 불변성을 유지할 수 있습니다.\n\n```ts\nimport { sangte } from 'sangte'\n\nconst counterState = sangte(0)\nconst textState = sangte('text')\nconst userState = sangte({ id: 1, name: 'John', email: 'john@email.com' }, (prev) => ({\n  setName(name: string) {\n    prev.name = name\n  },\n  setEmail(email: string) {\n    return {\n      ...prev,\n      email,\n    }\n  },\n}))\n\ninterface Todo {\n  id: number\n  text: string\n  done: boolean\n}\n\nconst todosState = sangte<Todo[]>([], (prev) => ({\n  add(todo: Todo) {\n    return prev.push(todo)\n  },\n  remove(id: number) {\n    return prev.filter((todo) => todo.id !== id)\n  },\n  toggle(id: number) {\n    const todo = prev.find((todo) => todo.id === id)\n    todo.done = !todo.done\n  },\n}))\n```\n\n### 컴포넌트에서 상태 또는 액션을 사용하기\n\n이 라이브러리는 상태나 액션을 여러분의 컴포넌트에서 사용 할 수 있도록 Hook 함수들을 제공합니다.\n\n#### useSangte\n\n`useSangte`는 `useState`와 비슷하지만, 전역적으로 작동합니다. 이 함수는 상태값과 업데이트 함수를 반환합니다.\n\n```tsx\nimport { sangte, useSangte } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction Counter() {\n  const [counter, setCounter] = useSangte(counterState)\n  return (\n    <div>\n      <h1>{counter}</h1>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n      <button onClick={() => setCounter(0)}>Reset</button>\n    </div>\n  )\n}\n\nexport default Counter\n```\n\n#### useSangteValue\n\n만약 컴포넌트에서 상태값만 필요로 한다면 `useSangteValue` 를 사용하세요.\n\n```tsx\nimport { useSangteValue } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction CounterValue() {\n  const counter = useSangteValue(counterState)\n  return <h1>{counter}</h1>\n}\n```\n\n상태에서 일부분의 값만 필요로 한다면 두번째 인자에 셀렉터 함수를 넣어서 원하는 부분만 선택할 수 있습니다. 객체 형태로 여러 필드를 선택하게 된다면 기본적으로 shallow compare를 하고 나서 리렌더링을 합니다. 비교 함수는 세번째 인자에 임의 비교 함수를 전달하여 override 할 수 있습니다.\n\n```tsx\nimport { sangte, useSangteValue } from 'sangte'\n\nconst userState = sangte({ id: 1, name: 'John', email: 'john@email.com' })\n\nfunction User() {\n  const { name, email } = useSangteValue(userState, (state) => ({\n    name: state.name,\n    email: state.email,\n  }))\n  return (\n    <div>\n      <h1>{name}</h1>\n      <h2>{email}</h2>\n    </div>\n  )\n}\n```\n\n만약 의존하는 값이 업데이트 되었을 때만 실행되는 memoized 셀렉터를 사용하고 싶으시다면 다음과 같이 읽기 전용 Sangte를 만들어서 사용할 수 있습니다.\n\n```tsx\nimport { sangte } from 'sangte'\nconst todosState = sangte([\n  { id: 1, text: 'Basic usage', done: true },\n  { id: 21, text: 'Ready-only sangte', done: false },\n])\nconst undoneTodosValue = sangte((get) => get(todosState).filter((todo) => !todo.done))\nfunction UndoneTodos() {\n  const undoneTodos = useSangteValue(undoneTodosValue)\n\n  return <div>{undoneTodos.length} todos undone.</div>\n}\n```\n\n#### useSetSangte\n\n만약 컴포넌트에서 상태 업데이트 함수만을 필요로 한다면 `useSetSangte`를 사용하세요.\n\n```tsx\nimport { sangte, useSetSangte } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction CounterButtons() {\n  const setCounter = useSetSangte(counterState)\n  return (\n    <div>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n    </div>\n  )\n}\n```\n\n#### useSangteActions\n\n상태를 만들 때 액션을 정의했다면, `useSangteActions`를 사용하여 액션을 가져와서 호출할 수 있습니다.\n\n```tsx\nimport { sangte, useSangteActions } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  increase() {\n    return prev + 1\n  },\n  decreaseBy(amount: number) {\n    return prev - amount\n  },\n}))\n\nfunction CounterButtons() {\n  const { increase, decreaseBy } = useSangteActions(counterState)\n  return (\n    <div>\n      <button onClick={increase}>Increase</button>\n      <button onClick={() => decreaseBy(10)}>Decrease</button>\n    </div>\n  )\n}\n```\n\n#### useResetSangte\n\n상태를 초깃값으로 바꾸고 싶다면 `useResetSangte`를 사용하세요.\n\n```tsx\nimport { sangte, useResetSangte } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction Counter() {\n  const [counter, setCounter] = useSangte(counterState)\n  const resetCounter = useResetSangte(counterState)\n  return (\n    <div>\n      <h1>{counter}</h1>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n      <button onClick={resetCounter}>Reset</button>\n    </div>\n  )\n}\n```\n\n#### useResetAllSangte\n\n모든 상태를 초기화하고 싶다면 `useResetAllSangte`를 사용하세요. 이 hook은 자식 SangteProvider에도 영향을 미칩니다.\n\n```tsx\nimport { sangte, useResetAllSangte } from 'sangte'\n\nconst counterState = sangte(0)\nconst textState = sangte('text')\n\nfunction Counter() {\n  const [counter, setCounter] = useSangte(counterState)\n  const [text, setText] = useSangte(textState)\n  const resetAll = useResetAllSangte()\n\n  return (\n    <div>\n      <h1>\n        {counter} | {text}\n      </h1>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n      <button onClick={() => setText('Hello World'}>Update Text</button>\n      <button onClick={resetAll}>Reset</button>\n    </div>\n  )\n}\n```\n\n전역적으로 모든 상태(모든 부모 SangteProvider에 있는 상태 포함)를 초기화하고 싶다면 함수의 첫번째 인자에 `true`를 넘겨주세요.\n\n```tsx\nimport { sangte, useResetAllSangte } from 'sangte'\n\nfunction RestAll() {\n  const resetAll = useResetAllSangte()\n  return <button onClick={() => resetAll(true)}>Reset All</button>\n}\n```\n\n#### useSangteCallback\n\n콜백 함수 안에서 상태 값을 사용하고 싶지만 상태 값이 바뀔 때마다 컴포넌트가 리렌더링 되는 것을 원하지 않는다면, `useSangteCallback`을 쓰세요.\n\n```tsx\nimport { sangte, useSangteCallback } from 'sangte'\n\nconst valueState = sangte('hello world!')\n\nfunction ConfirmButton() {\n  // 이 컴포넌트는 valueState가 변경되어도 리렌더링 되지 않습니다.\n  const confirm = useSangteCallback(({ get }) => {\n    const value = get(valueState)\n    console.log(value) // value 값을 가지고 어떠한 작업을 하기..\n  }, [])\n\n  return <button onClick={confirm}>Confirm</button>\n}\n```\n\n`useSangteCallback`에서 상태 업데이트 함수나 액션들을 사용할 수 있습니다.\n\n```tsx\nimport { sangte, useSangteCallback } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  add(value: number) {\n    return prev + value\n  },\n}))\n\nfunction Counter() {\n  // add 호출\n  const add2 = useSangteCallback(({ actions }) => {\n    const { add } = actions(counterState)\n    add(2)\n  }, [])\n\n  // counterState 10000으로 변경\n  const set10000 = useSangteCallback(({ set }) => {\n    set(counterState, 10000)\n  }, [])\n\n  return (\n    <div>\n      <button onClick={add2}>add 2</button>\n      <button onClick={logCount}>logCount</button>\n      <button onClick={set10000}>set 10000</button>\n    </div>\n  )\n}\n```\n"
  },
  {
    "path": "README.md",
    "content": "# sangte\n\n[![](https://img.shields.io/npm/v/sangte?style=flat-square)](https://www.npmjs.com/package/sangte)\n[![](https://img.shields.io/bundlephobia/min/sangte?style=flat-square)](https://bundlephobia.com/package/sangte)\n[![](https://img.shields.io/codecov/c/github/velopert/sangte?style=flat-square)](https://app.codecov.io/gh/velopert/sangte)\n\n[English](./README.md) | [한국어](./README-ko.md)\n\nSangte is a state management library for React. This library is inspired by [Redux Toolkit](https://redux-toolkit.js.org/) and [Recoil](https://recoiljs.org/).\n\n> Sangte means \"state\" in Korean.\n\n## Installation\n\nTo install the library, run the following command:\n\n```\nnpm install sangte\n```\n\nOr if you're using yarn:\n\n```\nyarn add sangte\n```\n\n## Why sangte?\n\n- Less boilerplate\n- Rerender only when the state you're using is updated\n- Easy to use\n- Allows multiple providers\n- TypeScript support\n\n## Usage\n\n### First create a state\n\nTo create a state you need to use the `sangte` function. A state of sangte should have a default value, and actions to update the state. Actions are optional.\n\nSangte uses [immer](https://immerjs.github.io/immer/) internally to update the state. So you can mutate the state directly while keeping the immutability.\n\n```ts\nimport { sangte } from 'sangte'\n\nconst counterState = sangte(0)\nconst textState = sangte('text')\nconst userState = sangte({ id: 1, name: 'John', email: 'john@email.com' }, (prev) => ({\n  setName(name: string) {\n    prev.name = name\n  },\n  setEmail(email: string) {\n    return {\n      ...prev,\n      email,\n    }\n  },\n}))\n\ninterface Todo {\n  id: number\n  text: string\n  done: boolean\n}\n\nconst todosState = sangte<Todo[]>([], (prev) => ({\n  add(todo: Todo) {\n    return prev.push(todo)\n  },\n  remove(id: number) {\n    return prev.filter((todo) => todo.id !== id)\n  },\n  toggle(id: number) {\n    const todo = prev.find((todo) => todo.id === id)\n    todo.done = !todo.done\n  },\n}))\n```\n\n### Use the state or actions from your components\n\nThe library provides hooks to utilize the state or actions from your components.\n\n#### useSangte\n\n`useSangte` works like `useState` from React, but it works globally. It returns the state and a setter function to update the state.\n\n```tsx\nimport { sangte, useSangte } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction Counter() {\n  const [counter, setCounter] = useSangte(counterState)\n  return (\n    <div>\n      <h1>{counter}</h1>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n      <button onClick={() => setCounter(0)}>Reset</button>\n    </div>\n  )\n}\n\nexport default Counter\n```\n\n#### useSangteValue\n\nIf you only need the value of the state, you can use `useSangteValue`.\n\n```tsx\nimport { useSangteValue } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction CounterValue() {\n  const counter = useSangteValue(counterState)\n  return <h1>{counter}</h1>\n}\n```\n\nIf you want to select a part of the state, you can pass selector function as second argument. If you select multiple fields, the component will rerender after shallow comparison. You can override the comparison function by passing a custom equality function to third argument.\n\n```tsx\nimport { sangte, useSangteValue } from 'sangte'\n\nconst userState = sangte({ id: 1, name: 'John', email: 'john@email.com' })\n\nfunction User() {\n  const { name, email } = useSangteValue(userState, (state) => ({\n    name: state.name,\n    email: state.email,\n  }))\n  return (\n    <div>\n      <h1>{name}</h1>\n      <h2>{email}</h2>\n    </div>\n  )\n}\n```\n\nIf you want to use a memoized selector that is prcessed only when its dependencies update, you can create a read-only Sangte as below.\n\n```tsx\nimport { sangte } from 'sangte'\nconst todosState = sangte([\n  { id: 1, text: 'Basic usage', done: true },\n  { id: 21, text: 'Ready-only sangte', done: false },\n])\nconst undoneTodosValue = sangte((get) => get(todosState).filter((todo) => !todo.done))\nfunction UndoneTodos() {\n  const undoneTodos = useSangteValue(undoneTodosValue)\n\n  return <div>{undoneTodos.length} todos undone.</div>\n}\n```\n\n#### useSetSangte\n\nIf you only need the updater function of the state, you can use `useSetSangte`.\n\n```tsx\nimport { sangte, useSetSangte } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction CounterButtons() {\n  const setCounter = useSetSangte(counterState)\n  return (\n    <div>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n    </div>\n  )\n}\n```\n\n#### useSangteActions\n\nIf you have defined actions for the state, you can use `useSangteActions` to get the actions.\n\n```tsx\nimport { sangte, useSangteActions } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  increase() {\n    return prev + 1\n  },\n  decreaseBy(amount: number) {\n    return prev - amount\n  },\n}))\n\nfunction CounterButtons() {\n  const { increase, decreaseBy } = useSangteActions(counterState)\n  return (\n    <div>\n      <button onClick={increase}>Increase</button>\n      <button onClick={() => decreaseBy(10)}>Decrease</button>\n    </div>\n  )\n}\n```\n\n#### useResetSangte\n\nIf you want to reset the state to its default value, you can use `useResetSangte`.\n\n```tsx\nimport { sangte, useResetSangte } from 'sangte'\n\nconst counterState = sangte(0)\n\nfunction Counter() {\n  const [counter, setCounter] = useSangte(counterState)\n  const resetCounter = useResetSangte(counterState)\n  return (\n    <div>\n      <h1>{counter}</h1>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n      <button onClick={resetCounter}>Reset</button>\n    </div>\n  )\n}\n```\n\n#### useResetAllSangte\n\nIf you want to reset all the states to their default values, you can use `useResetAllSangte`. This hook also resets sangte inside the nested providers.\n\n```tsx\nimport { sangte, useResetAllSangte } from 'sangte'\n\nconst counterState = sangte(0)\nconst textState = sangte('text')\n\nfunction Counter() {\n  const [counter, setCounter] = useSangte(counterState)\n  const [text, setText] = useSangte(textState)\n  const resetAll = useResetAllSangte()\n\n  return (\n    <div>\n      <h1>\n        {counter} | {text}\n      </h1>\n      <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>\n      <button onClick={() => setText('Hello World'}>Update Text</button>\n      <button onClick={resetAll}>Reset</button>\n    </div>\n  )\n}\n```\n\nIf you want to reset the states globally (including all parent providers), you can pass `true` as first argument.\n\n```tsx\nimport { sangte, useResetAllSangte } from 'sangte'\n\nfunction RestAll() {\n  const resetAll = useResetAllSangte()\n  return <button onClick={() => resetAll(true)}>Reset All</button>\n}\n```\n\n#### useSangteCallback\n\nIf you want to use the state in a callback but you do not want to rerender the component as the state changes, you can use `useSangteCallback`.\n\n```tsx\nimport { sangte, useSangteCallback } from 'sangte'\n\nconst valueState = sangte('hello world!')\n\nfunction ConfirmButton() {\n  // this component won't rerender even when valueState changes\n  const confirm = useSangteCallback(({ get }) => {\n    const value = get(valueState)\n    console.log(value) // do something with value..\n  }, [])\n\n  return <button onClick={confirm}>Confirm</button>\n}\n```\n\nYou can also use the setter function or actions with `useSangteCallback`.\n\n```tsx\nimport { sangte, useSangteCallback } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  add(value: number) {\n    return prev + value\n  },\n}))\n\nfunction Counter() {\n  // calls add action\n  const add2 = useSangteCallback(({ actions }) => {\n    const { add } = actions(counterState)\n    add(2)\n  }, [])\n\n  // sets counterState to 10000\n  const set10000 = useSangteCallback(({ set }) => {\n    set(counterState, 10000)\n  }, [])\n\n  return (\n    <div>\n      <button onClick={add2}>add 2</button>\n      <button onClick={logCount}>logCount</button>\n      <button onClick={set10000}>set 10000</button>\n    </div>\n  )\n}\n```\n\n## Recipe\n\n### Using multiple providers\n\n### Inheriting state from parent provider\n\n### Server side rendering\n\n> Docs are still in progress. If you have any questions, please open an issue.\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = (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": "examples/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/package.json",
    "content": "{\n  \"name\": \"vite-project\",\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    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"sangte\": \"^0.1.18\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.0.17\",\n    \"@types/react-dom\": \"^18.0.6\",\n    \"@vitejs/plugin-react\": \"^2.0.1\",\n    \"typescript\": \"^4.6.4\",\n    \"vite\": \"^3.0.7\"\n  }\n}\n"
  },
  {
    "path": "examples/src/App.css",
    "content": ".samples {\n  display: flex;\n  flex-wrap: wrap;\n}\n\n.samples > div {\n  display: flex;\n  align-items:center;\n  justify-content: center;\n  border: 1px solid black;\n  width: 25%;\n  padding: 16px;\n}"
  },
  {
    "path": "examples/src/App.tsx",
    "content": "import './App.css'\nimport Counter from './components/Counter'\nimport Initialize from './components/Initialize'\nimport Hydrate from './components/Hydrate'\nimport Selector from './components/Selector'\nimport MultiProviders from './components/MultiProviders'\nimport Inherit from './components/Inherit'\nimport GlobalState from './components/GlobalState'\nimport MemoizedSelector from './components/MemoizedSelector'\nimport Todos from './components/Todos'\nimport VisibleToggle from './components/VisibleToggle'\nimport MemoryLeakTester from './components/MemoryLeakTester'\n\nfunction App() {\n  return (\n    <div className=\"samples\">\n      <div>\n        <Counter />\n      </div>\n      <div>\n        <Initialize />\n      </div>\n      <div>\n        <Hydrate />\n      </div>\n      <div>\n        <Selector />\n      </div>\n      <div>\n        <MultiProviders />\n      </div>\n      <div>\n        <VisibleToggle>\n          <MultiProviders />\n        </VisibleToggle>\n      </div>\n      <div>\n        <Inherit />\n      </div>\n      <div>\n        <GlobalState />\n      </div>\n      <div>\n        <Todos />\n      </div>\n      <div>\n        <VisibleToggle>\n          <MemoizedSelector />\n        </VisibleToggle>\n      </div>\n      <div>\n        <VisibleToggle>\n          <MemoizedSelector />\n        </VisibleToggle>\n      </div>\n      <div>\n        <VisibleToggle>\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n          <MemoryLeakTester />\n        </VisibleToggle>\n      </div>\n    </div>\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "examples/src/components/Counter.tsx",
    "content": "import { sangte, useResetSangte, useSangteActions, useSangteValue } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  increase() {\n    return prev + 1\n  },\n  decrease(amount: number) {\n    return prev - amount\n  },\n}))\n\nfunction Counter() {\n  const counter = useSangteValue(counterState)\n  const actions = useSangteActions(counterState)\n  const reset = useResetSangte(counterState)\n\n  return (\n    <div>\n      <h1>{counter}</h1>\n      <button onClick={actions.increase}>+</button>\n      <button onClick={() => actions.decrease(10)}>-</button>\n      <button onClick={reset}>Reset</button>\n    </div>\n  )\n}\n\nexport default Counter\n"
  },
  {
    "path": "examples/src/components/GlobalState.tsx",
    "content": "import { SangteProvider, useSangteActions } from 'sangte'\nimport { useSangteValue } from 'sangte'\nimport { sangte } from 'sangte'\n\nconst globalCounterState = sangte(\n  0,\n  (prev) => ({\n    increase() {\n      return prev + 1\n    },\n  }),\n  {\n    global: true,\n  }\n)\n\nfunction Counter() {\n  const counter = useSangteValue(globalCounterState)\n  const actions = useSangteActions(globalCounterState)\n\n  return (\n    <div>\n      <h3>{counter}</h3>\n      <button onClick={actions.increase}>+1</button>\n    </div>\n  )\n}\n\nfunction GlobalState() {\n  return (\n    <SangteProvider>\n      <Counter />\n      <div style={{ padding: 16, border: '1px solid black' }}>\n        <SangteProvider>\n          <Counter />\n        </SangteProvider>\n      </div>\n    </SangteProvider>\n  )\n}\n\nexport default GlobalState\n"
  },
  {
    "path": "examples/src/components/Hydrate.tsx",
    "content": "import { sangte, SangteProvider, useSangteValue } from 'sangte'\n\nconst numberState = sangte(0, { key: 'number' })\nconst textState = sangte('hello world', { key: 'text' })\n\nfunction Values() {\n  const number = useSangteValue(numberState)\n  const text = useSangteValue(textState)\n\n  return (\n    <div>\n      <div>\n        <b>number:</b> {number}\n      </div>\n      <div>\n        <b>text:</b> {text}\n      </div>\n    </div>\n  )\n}\n\nfunction Hydrate() {\n  return (\n    <SangteProvider\n      dehydratedState={{\n        number: 10,\n        text: 'bye world',\n      }}\n    >\n      <Values />\n    </SangteProvider>\n  )\n}\n\nexport default Hydrate\n"
  },
  {
    "path": "examples/src/components/Inherit.tsx",
    "content": "import { SangteProvider, useSangteActions } from 'sangte'\nimport { useSangteValue } from 'sangte'\nimport { sangte } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  increase() {\n    return prev + 1\n  },\n}))\n\nfunction Counter() {\n  const counter = useSangteValue(counterState)\n  const actions = useSangteActions(counterState)\n\n  return (\n    <div>\n      <h3>{counter}</h3>\n      <button onClick={actions.increase}>+1</button>\n    </div>\n  )\n}\n\nfunction Inherit() {\n  return (\n    <SangteProvider>\n      <Counter />\n      <div style={{ padding: 16, border: '1px solid black' }}>\n        <SangteProvider inheritSangtes={[counterState]}>\n          <Counter />\n        </SangteProvider>\n      </div>\n    </SangteProvider>\n  )\n}\n\nexport default Inherit\n"
  },
  {
    "path": "examples/src/components/Initialize.tsx",
    "content": "import React from 'react'\nimport { sangte, SangteProvider, useSangteValue } from 'sangte'\n\nconst textState = sangte('Default text')\n\nfunction Text() {\n  const text = useSangteValue(textState)\n  return <div>{text}</div>\n}\n\nfunction Initialize() {\n  return (\n    <SangteProvider\n      initialize={({ set }) => {\n        set(textState, 'Hello World!')\n      }}\n    >\n      <Text />\n    </SangteProvider>\n  )\n}\n\nexport default Initialize\n"
  },
  {
    "path": "examples/src/components/MemoizedSelector.tsx",
    "content": "import { useState } from 'react'\nimport { resangte, sangte, useSangteActions, useSangteValue } from 'sangte'\nimport { todosState } from './Todos'\n\nconst uncompletedTodosValue = resangte((get) => {\n  console.log('filtering..')\n  return get(todosState).filter((todo) => !todo.completed)\n})\n\nfunction MemoizedSelector() {\n  const uncompletedTodos = useSangteValue(uncompletedTodosValue)\n  const actions = useSangteActions(todosState)\n  const [value, setValue] = useState(0)\n  return (\n    <div>\n      {uncompletedTodos.map((todo) => (\n        <div\n          onClick={() => {\n            actions.toggle(todo.id)\n          }}\n        >\n          {todo.text}\n        </div>\n      ))}\n      <div>{value}</div>\n      <button\n        onClick={() => {\n          setValue(value + 1)\n        }}\n      >\n        +1\n      </button>\n    </div>\n  )\n}\n\nexport default MemoizedSelector\n"
  },
  {
    "path": "examples/src/components/MemoryLeakTester.tsx",
    "content": "import { useEffect, useRef, useState } from 'react'\nimport { resangte, sangte, SangteProvider, useSangteActions, useSangteValue } from 'sangte'\n\nconst array = new Array(1000).fill(0).map((_, i) => ({ id: i, done: false }))\n\nconst itemsState = sangte(array, (prev) => ({\n  work(id: number) {\n    const item = prev.find((item) => item.id === id)\n    if (!item) return\n    item.done = true\n  },\n}))\nconst doneItemsValue = resangte((get) => get(itemsState).filter((item) => item.done))\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))\n\nfunction Work({ onHide }: { onHide(): void }) {\n  const called = useRef<boolean>(false)\n\n  const doneItems = useSangteValue(doneItemsValue)\n  const { work } = useSangteActions(itemsState)\n\n  useEffect(() => {\n    if (called.current) return\n    called.current = true\n    const random = Math.floor(Math.random() * 10)\n    const loop = async () => {\n      for (let i = 0; i < random; i++) {\n        work(i)\n        console.log('workign!')\n        await sleep(1)\n      }\n      onHide()\n    }\n    loop()\n  }, [])\n\n  return <div>{doneItems.length} items done!</div>\n}\n\nfunction Wrapper() {\n  const [visible, setVisible] = useState(true)\n\n  useEffect(() => {\n    if (!visible) {\n      setVisible(true)\n    }\n  }, [visible])\n\n  if (!visible) return null\n  return (\n    <SangteProvider>\n      <Work\n        onHide={() => {\n          setVisible(false)\n        }}\n      />\n    </SangteProvider>\n  )\n}\n\nfunction MemoryLeakTester() {\n  return <Wrapper />\n}\n\nexport default MemoryLeakTester\n"
  },
  {
    "path": "examples/src/components/MultiProviders.tsx",
    "content": "import { SangteProvider, useSangteActions } from 'sangte'\nimport { useSangteValue } from 'sangte'\nimport { sangte } from 'sangte'\n\nconst counterState = sangte(0, (prev) => ({\n  increase() {\n    return prev + 1\n  },\n}))\n\nfunction Counter() {\n  const counter = useSangteValue(counterState)\n  const actions = useSangteActions(counterState)\n\n  return (\n    <div>\n      <h3>{counter}</h3>\n      <button onClick={actions.increase}>+1</button>\n    </div>\n  )\n}\n\nfunction MultiProviders() {\n  return (\n    <SangteProvider>\n      <Counter />\n      <div style={{ padding: 16, border: '1px solid black' }}>\n        <SangteProvider>\n          <Counter />\n        </SangteProvider>\n      </div>\n    </SangteProvider>\n  )\n}\n\nexport default MultiProviders\n"
  },
  {
    "path": "examples/src/components/Selector.tsx",
    "content": "import { sangte, useSangteActions, useSangteValue } from 'sangte'\n\nconst userState = sangte(\n  {\n    id: 1,\n    username: 'velopert',\n    email: 'public.velopert@gmail.com',\n    isActive: false,\n  },\n  (prev) => ({\n    toggleActive() {\n      prev.isActive = !prev.isActive\n    },\n  })\n)\n\nfunction Value() {\n  const { username, email } = useSangteValue(userState, (state) => ({\n    username: state.username,\n    email: state.email,\n  }))\n\n  return (\n    <div>\n      <div>{username}</div>\n      <div>{email}</div>\n    </div>\n  )\n}\n\nfunction Toggle() {\n  const { toggleActive } = useSangteActions(userState)\n  return <button onClick={toggleActive}>Toggle</button>\n}\n\nfunction Active() {\n  const isActive = useSangteValue(userState, (state) => state.isActive)\n  return <div>{isActive ? 'active' : 'inactive'}</div>\n}\n\nfunction Selector() {\n  return (\n    <div>\n      <Value />\n      <Active />\n      <Toggle />\n    </div>\n  )\n}\n\nexport default Selector\n"
  },
  {
    "path": "examples/src/components/Todos.tsx",
    "content": "import { sangte, useSangteActions, useSangteValue } from 'sangte'\n\nexport const todosState = sangte(\n  [\n    {\n      id: 1,\n      text: 'Learn React',\n      completed: true,\n    },\n    {\n      id: 2,\n      text: 'Learn Sangte',\n      completed: false,\n    },\n    {\n      id: 3,\n      text: 'Learn React Router',\n      completed: false,\n    },\n  ],\n  (prev) => ({\n    toggle(id: number) {\n      const todo = prev.find((todo) => todo.id === id)\n      if (!todo) return\n      todo.completed = !todo.completed\n    },\n  })\n)\n\nfunction Todos() {\n  const todos = useSangteValue(todosState)\n  const actions = useSangteActions(todosState)\n\n  return (\n    <div>\n      {todos.map((todo) => (\n        <div\n          style={{\n            textDecoration: todo.completed ? 'line-through' : 'none',\n          }}\n          onClick={() => {\n            actions.toggle(todo.id)\n          }}\n        >\n          {todo.text}\n        </div>\n      ))}\n    </div>\n  )\n}\n\nexport default Todos\n"
  },
  {
    "path": "examples/src/components/VisibleToggle.tsx",
    "content": "import React, { useState } from 'react'\n\nfunction VisibleToggle({ children }: { children: React.ReactNode }) {\n  const [visible, setVisible] = useState(false)\n  return (\n    <div>\n      <button onClick={() => setVisible(!visible)}>{visible ? 'Hide' : 'Show'}</button>\n      {visible ? children : null}\n    </div>\n  )\n}\n\nexport default VisibleToggle\n"
  },
  {
    "path": "examples/src/index.css",
    "content": ":root {\n  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;\n  font-size: 16px;\n  line-height: 24px;\n  font-weight: 400;\n\n  color-scheme: light dark;\n  color: rgba(255, 255, 255, 0.87);\n  background-color: #242424;\n\n  font-synthesis: none;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-text-size-adjust: 100%;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\nbody {\n  margin: 0;\n  display: flex;\n\n}\n\nh1 {\n  font-size: 3.2em;\n  line-height: 1.1;\n}\n\nbutton {\n  border-radius: 8px;\n  border: 1px solid transparent;\n  padding: 0.6em 1.2em;\n  font-size: 1em;\n  font-weight: 500;\n  font-family: inherit;\n  background-color: #1a1a1a;\n  cursor: pointer;\n  transition: border-color 0.25s;\n}\nbutton:hover {\n  border-color: #646cff;\n}\nbutton:focus,\nbutton:focus-visible {\n  outline: 4px auto -webkit-focus-ring-color;\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    color: #213547;\n    background-color: #ffffff;\n  }\n  a:hover {\n    color: #747bff;\n  }\n  button {\n    background-color: #f9f9f9;\n  }\n}\n"
  },
  {
    "path": "examples/src/main.tsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>\n)\n"
  },
  {
    "path": "examples/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "examples/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\"src\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()]\n})\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/src/favicon.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "jest.config.ts",
    "content": "/*\n * For a detailed explanation regarding each configuration property and type check, visit:\n * https://jestjs.io/docs/configuration\n */\n\nexport default {\n  setupFilesAfterEnv: ['<rootDir>/src/setupTest.ts'],\n  // All imported modules in your tests should be mocked automatically\n  // automock: false,\n\n  // Stop running tests after `n` failures\n  // bail: 0,\n\n  // The directory where Jest should store its cached dependency information\n  // cacheDirectory: \"/tmp/jest_rs\",\n\n  // Automatically clear mock calls, instances, contexts and results before every test\n  clearMocks: true,\n\n  // Indicates whether the coverage information should be collected while executing the test\n  collectCoverage: true,\n\n  // An array of glob patterns indicating a set of files for which coverage information should be collected\n  // collectCoverageFrom: undefined,\n\n  // The directory where Jest should output its coverage files\n  coverageDirectory: 'coverage',\n\n  // An array of regexp pattern strings used to skip coverage collection\n  // coveragePathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // Indicates which provider should be used to instrument code for coverage\n  coverageProvider: 'v8',\n\n  // A list of reporter names that Jest uses when writing coverage reports\n  // coverageReporters: [\n  //   \"json\",\n  //   \"text\",\n  //   \"lcov\",\n  //   \"clover\"\n  // ],\n\n  // An object that configures minimum threshold enforcement for coverage results\n  // coverageThreshold: undefined,\n\n  // A path to a custom dependency extractor\n  // dependencyExtractor: undefined,\n\n  // Make calling deprecated APIs throw helpful error messages\n  // errorOnDeprecated: false,\n\n  // The default configuration for fake timers\n  // fakeTimers: {\n  //   \"enableGlobally\": false\n  // },\n\n  // Force coverage collection from ignored files using an array of glob patterns\n  // forceCoverageMatch: [],\n\n  // A path to a module which exports an async function that is triggered once before all test suites\n  // globalSetup: undefined,\n\n  // A path to a module which exports an async function that is triggered once after all test suites\n  // globalTeardown: undefined,\n\n  // A set of global variables that need to be available in all test environments\n  // globals: {},\n\n  // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n  // maxWorkers: \"50%\",\n\n  // An array of directory names to be searched recursively up from the requiring module's location\n  // moduleDirectories: [\n  //   \"node_modules\"\n  // ],\n\n  // An array of file extensions your modules use\n  // moduleFileExtensions: [\n  //   \"js\",\n  //   \"mjs\",\n  //   \"cjs\",\n  //   \"jsx\",\n  //   \"ts\",\n  //   \"tsx\",\n  //   \"json\",\n  //   \"node\"\n  // ],\n\n  // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n  // moduleNameMapper: {},\n\n  // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n  // modulePathIgnorePatterns: [],\n\n  // Activates notifications for test results\n  // notify: false,\n\n  // An enum that specifies notification mode. Requires { notify: true }\n  // notifyMode: \"failure-change\",\n\n  // A preset that is used as a base for Jest's configuration\n  // preset: undefined,\n\n  // Run tests from one or more projects\n  // projects: undefined,\n\n  // Use this configuration option to add custom reporters to Jest\n  // reporters: undefined,\n\n  // Automatically reset mock state before every test\n  // resetMocks: false,\n\n  // Reset the module registry before running each individual test\n  // resetModules: false,\n\n  // A path to a custom resolver\n  // resolver: undefined,\n\n  // Automatically restore mock state and implementation before every test\n  // restoreMocks: false,\n\n  // The root directory that Jest should scan for tests and modules within\n  // rootDir: undefined,\n\n  // A list of paths to directories that Jest should use to search for files in\n  // roots: [\n  //   \"<rootDir>\"\n  // ],\n\n  // Allows you to use a custom runner instead of Jest's default test runner\n  // runner: \"jest-runner\",\n\n  // The paths to modules that run some code to configure or set up the testing environment before each test\n  // setupFiles: [],\n\n  // A list of paths to modules that run some code to configure or set up the testing framework before each test\n  // setupFilesAfterEnv: [],\n\n  // The number of seconds after which a test is considered as slow and reported as such in the results.\n  // slowTestThreshold: 5,\n\n  // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n  // snapshotSerializers: [],\n\n  // The test environment that will be used for testing\n  testEnvironment: 'jsdom',\n\n  // Options that will be passed to the testEnvironment\n  // testEnvironmentOptions: {},\n\n  // Adds a location field to test results\n  // testLocationInResults: false,\n\n  // The glob patterns Jest uses to detect test files\n  // testMatch: [\n  //   \"**/__tests__/**/*.[jt]s?(x)\",\n  //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n  // testPathIgnorePatterns: [\n  //   \"/node_modules/\"\n  // ],\n\n  // The regexp pattern or array of patterns that Jest uses to detect test files\n  // testRegex: [],\n\n  // This option allows the use of a custom results processor\n  // testResultsProcessor: undefined,\n\n  // This option allows use of a custom test runner\n  // testRunner: \"jest-circus/runner\",\n\n  // A map from regular expressions to paths to transformers\n  // transform: undefined,\n\n  // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n  // transformIgnorePatterns: [\n  //   \"/node_modules/\",\n  //   \"\\\\.pnp\\\\.[^\\\\/]+$\"\n  // ],\n\n  // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n  // unmockedModulePathPatterns: undefined,\n\n  // Indicates whether each individual test should be reported during the run\n  // verbose: undefined,\n\n  // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n  // watchPathIgnorePatterns: [],\n\n  // Whether to use watchman for file crawling\n  // watchman: true,\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sangte\",\n  \"description\": \"Sangte is a fancy React state management library.\",\n  \"private\": false,\n  \"version\": \"0.1.26\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.mjs\",\n  \"typings\": \"dist/index.d.ts\",\n  \"scripts\": {\n    \"build\": \"rollup -c\",\n    \"test\": \"jest\"\n  },\n  \"dependencies\": {\n    \"immer\": \"^9.0.15\",\n    \"use-sync-external-store\": \"^1.2.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/plugin-transform-react-jsx\": \"^7.18.10\",\n    \"@babel/plugin-transform-typescript\": \"^7.18.12\",\n    \"@babel/preset-env\": \"^7.18.10\",\n    \"@testing-library/jest-dom\": \"^5.16.5\",\n    \"@testing-library/react\": \"^13.3.0\",\n    \"@testing-library/react-hooks\": \"^8.0.1\",\n    \"@testing-library/user-event\": \"^14.4.3\",\n    \"@types/react\": \"^18.0.0\",\n    \"@types/react-dom\": \"^18.0.0\",\n    \"@types/use-sync-external-store\": \"^0.0.3\",\n    \"esbuild\": \"^0.15.5\",\n    \"jest\": \"^29.0.1\",\n    \"jest-environment-jsdom\": \"^29.0.1\",\n    \"jsdom\": \"^20.0.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"rollup\": \"^2.75.7\",\n    \"rollup-plugin-dts\": \"^4.2.2\",\n    \"rollup-plugin-esbuild\": \"^4.9.1\",\n    \"ts-node\": \"^10.9.1\",\n    \"typescript\": \"^4.6.3\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.8\"\n  },\n  \"files\": [\n    \"/dist\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/velopert/sangte\"\n  },\n  \"author\": \"velopert\",\n  \"license\": \"MIT\",\n  \"homepage\": \"https://github.com/velopert/sangte\"\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import dts from 'rollup-plugin-dts'\nimport esbuild from 'rollup-plugin-esbuild'\n\nconst name = require('./package.json').main.replace(/\\.js$/, '')\n\nconst bundle = (config) => ({\n  ...config,\n  input: 'src/index.ts',\n  external: (id) => !/^[./]/.test(id),\n})\n\nexport default [\n  bundle({\n    plugins: [\n      esbuild({\n        jsx: 'automatic',\n      }),\n    ],\n    output: [\n      {\n        file: `${name}.js`,\n        format: 'cjs',\n        sourcemap: true,\n      },\n      {\n        file: `${name}.mjs`,\n        format: 'es',\n        sourcemap: true,\n      },\n    ],\n  }),\n  bundle({\n    plugins: [dts()],\n    output: {\n      file: `${name}.d.ts`,\n      format: 'es',\n    },\n  }),\n]\n"
  },
  {
    "path": "src/contexts/SangteProvider.tsx",
    "content": "import { createContext, useCallback, useContext, useEffect, useRef } from 'react'\nimport { Sangte } from '../lib/sangte'\nimport { SangteInitializer } from '../lib/SangteInitializer'\nimport { SangteManager } from '../lib/SangteManager'\n\nconst SangteContext = createContext<SangteManager | null>(null)\n\ninterface SangteProviderProps {\n  children: React.ReactNode\n  inheritSangtes?: Sangte<any>[]\n  initialize?: (params: { set: <T>(sangte: Sangte<T, any>, value: T) => void }) => void\n  dehydratedState?: Record<string, any>\n}\n\nexport function SangteProvider({\n  children,\n  inheritSangtes,\n  initialize,\n  dehydratedState,\n}: SangteProviderProps) {\n  const parent = useContext(SangteContext)\n  const managerRef = useRef<SangteManager | null>(null)\n  const initialized = useRef(false)\n\n  const initializeProvider = useCallback(() => {\n    if (initialized.current) return\n\n    const manager = managerRef.current ?? new SangteManager()\n    managerRef.current = manager\n\n    if (parent) {\n      manager.parent = parent\n      parent.children.add(manager)\n    }\n    manager.dehydratedState = dehydratedState\n    if (inheritSangtes) {\n      if (!parent) {\n        throw new Error(\n          'Cannot inherit sangtes from default SangteManager. Please wrap your app with SangteProvider.'\n        )\n      }\n      manager.inherit(inheritSangtes)\n    }\n    if (initialize) {\n      initialize({\n        set: manager.initializer.set.bind(manager.initializer),\n      })\n      manager.initializer.initialize()\n    }\n    initialized.current = true\n  }, [])\n\n  initializeProvider()\n\n  /**\n   * Reinitialization needed because useEffect runs twice on component mount in future React (and development mode)\n   */\n  useEffect(() => {\n    initializeProvider()\n    return () => {\n      initialized.current = false\n      if (parent && managerRef.current) {\n        parent.children.delete(managerRef.current)\n      }\n    }\n  }, [initializeProvider])\n\n  return <SangteContext.Provider value={managerRef.current}>{children}</SangteContext.Provider>\n}\n\n/**\n * Default manager used when SangteProvider is not used.\n * To\n */\nconst defaultManager = new SangteManager(true)\n\nexport function useSangteManager() {\n  const manager = useContext(SangteContext)\n  if (!manager) {\n    return defaultManager\n  }\n  return manager\n}\n"
  },
  {
    "path": "src/contexts/__tests__/SangteProvider.test.tsx",
    "content": "import { fireEvent, render, screen } from '@testing-library/react'\nimport { SangteProvider } from '../SangteProvider'\nimport { sangte } from '../../lib'\nimport { useSangte, useSangteValue } from '../../hooks'\n\ndescribe('SangteProvider', () => {\n  it('should render children', () => {\n    render(\n      <SangteProvider>\n        <div>hello</div>\n      </SangteProvider>\n    )\n    screen.getByText('hello')\n  })\n  it('should initializeState', () => {\n    const state = sangte(0)\n    function Child() {\n      const value = useSangteValue(state)\n      return <div data-testid=\"value\">{value}</div>\n    }\n\n    render(\n      <SangteProvider\n        initialize={({ set }) => {\n          set(state, 10)\n        }}\n      >\n        <Child />\n      </SangteProvider>\n    )\n    expect(screen.getByTestId('value')).toHaveTextContent('10')\n  })\n\n  it('should use closest provider', () => {\n    const state = sangte(0)\n    function Child({ testId }: { testId: string }) {\n      const [value, setState] = useSangte(state)\n      return (\n        <div>\n          <div data-testid={testId}>{value}</div>\n          <button data-testid={`button-${testId}`} onClick={() => setState(15)}>\n            Click Me\n          </button>\n        </div>\n      )\n    }\n\n    render(\n      <SangteProvider>\n        <Child testId=\"parent\" />\n        <SangteProvider\n          initialize={({ set }) => {\n            set(state, 10)\n          }}\n        >\n          <Child testId=\"child\" />\n        </SangteProvider>\n      </SangteProvider>\n    )\n\n    expect(screen.getByTestId('parent')).toHaveTextContent('0')\n    expect(screen.getByTestId('child')).toHaveTextContent('10')\n    fireEvent.click(screen.getByTestId('button-child'))\n    expect(screen.getByTestId('parent')).toHaveTextContent('0')\n    expect(screen.getByTestId('child')).toHaveTextContent('15')\n  })\n\n  it('should inherit state', () => {\n    const state = sangte(0)\n    function Child({ testId }: { testId: string }) {\n      const [value, setState] = useSangte(state)\n      return (\n        <div>\n          <div data-testid={testId}>{value}</div>\n          <button data-testid={`button-${testId}`} onClick={() => setState(15)}>\n            Click Me\n          </button>\n        </div>\n      )\n    }\n\n    render(\n      <SangteProvider>\n        <Child testId=\"parent\" />\n        <SangteProvider\n          initialize={({ set }) => {\n            set(state, 10)\n          }}\n          inheritSangtes={[state]}\n        >\n          <Child testId=\"child\" />\n        </SangteProvider>\n      </SangteProvider>\n    )\n\n    expect(screen.getByTestId('parent')).toHaveTextContent('10')\n    expect(screen.getByTestId('child')).toHaveTextContent('10')\n    fireEvent.click(screen.getByTestId('button-child'))\n    expect(screen.getByTestId('parent')).toHaveTextContent('15')\n    expect(screen.getByTestId('child')).toHaveTextContent('15')\n  })\n\n  it('should dehydrate', () => {\n    const counterState = sangte(0, { key: 'counter' })\n    const textState = sangte('hello world', { key: 'text' })\n\n    function Child() {\n      const counter = useSangteValue(counterState)\n      const text = useSangteValue(textState)\n\n      return (\n        <div>\n          <div data-testid=\"counter\">{counter}</div>\n          <div data-testid=\"text\">{text}</div>\n        </div>\n      )\n    }\n\n    render(\n      <SangteProvider\n        dehydratedState={{\n          counter: 10,\n          text: 'bye world',\n        }}\n      >\n        <Child />\n      </SangteProvider>\n    )\n\n    expect(screen.getByTestId('counter')).toHaveTextContent('10')\n    expect(screen.getByTestId('text')).toHaveTextContent('bye world')\n  })\n\n  it('cannot inherit from default SangteManager', () => {\n    const state = sangte(0)\n    expect(() => {\n      render(\n        <SangteProvider inheritSangtes={[state]}>\n          <div>hello</div>\n        </SangteProvider>\n      )\n    }).toThrowError()\n  })\n})\n"
  },
  {
    "path": "src/contexts/index.ts",
    "content": "export * from './SangteProvider'\n"
  },
  {
    "path": "src/hooks/__tests__/useResetAllSangte.test.tsx",
    "content": "import { fireEvent, render, screen } from '@testing-library/react'\nimport { act, renderHook } from '@testing-library/react-hooks'\nimport { SangteProvider } from '../../contexts'\nimport { sangte } from '../../lib'\nimport { useResetAllSangte } from '../useResetAllSangte'\nimport { useSangte } from '../useSangte'\n\ndescribe('useResetAllSangte', () => {\n  it('resets to initialState', () => {\n    const state = sangte(0)\n    const anotherState = sangte(1)\n\n    const { result } = renderHook(() => useSangte(state))\n    const { result: result2 } = renderHook(() => useSangte(anotherState))\n    act(() => {\n      result.current[1](5)\n      result2.current[1](6)\n    })\n    expect(result.current[0]).toBe(5)\n    expect(result2.current[0]).toBe(6)\n\n    const {\n      result: { current: resetAll },\n    } = renderHook(() => useResetAllSangte())\n    act(() => {\n      resetAll()\n    })\n    expect(result.current[0]).toBe(0)\n    expect(result2.current[0]).toBe(1)\n  })\n\n  it('should reset children only', () => {\n    const state = sangte(0)\n    function Child({ testId }: { testId: string }) {\n      const [value, setState] = useSangte(state)\n      const resetAll = useResetAllSangte()\n      return (\n        <div>\n          <div data-testid={testId}>{value}</div>\n          <button data-testid={`button-${testId}`} onClick={() => setState(15)}>\n            Click Me\n          </button>\n          <button onClick={() => resetAll()} data-testid={`button-reset-${testId}`}></button>\n        </div>\n      )\n    }\n\n    render(\n      <SangteProvider\n        initialize={({ set }) => {\n          set(state, 3)\n        }}\n      >\n        <Child testId=\"parent\" />\n        <SangteProvider\n          initialize={({ set }) => {\n            set(state, 10)\n          }}\n        >\n          <Child testId=\"child\" />\n          <SangteProvider\n            initialize={({ set }) => {\n              set(state, 5)\n            }}\n          >\n            <Child testId=\"grandchild\" />\n          </SangteProvider>\n        </SangteProvider>\n      </SangteProvider>\n    )\n\n    expect(screen.getByTestId('parent')).toHaveTextContent('3')\n    expect(screen.getByTestId('child')).toHaveTextContent('10')\n    expect(screen.getByTestId('grandchild')).toHaveTextContent('5')\n    fireEvent.click(screen.getByTestId('button-reset-child'))\n    expect(screen.getByTestId('parent')).toHaveTextContent('3')\n    expect(screen.getByTestId('child')).toHaveTextContent(/^0$/)\n    expect(screen.getByTestId('grandchild')).toHaveTextContent(/^0$/)\n  })\n\n  it('should reset globally', () => {\n    const state = sangte(0)\n    function Child({ testId }: { testId: string }) {\n      const [value, setState] = useSangte(state)\n      const resetAll = useResetAllSangte()\n      return (\n        <div>\n          <div data-testid={testId}>{value}</div>\n          <button data-testid={`button-${testId}`} onClick={() => setState(15)}>\n            Click Me\n          </button>\n          <button onClick={() => resetAll(true)} data-testid={`button-reset-${testId}`}></button>\n        </div>\n      )\n    }\n\n    render(\n      <SangteProvider\n        initialize={({ set }) => {\n          set(state, 3)\n        }}\n      >\n        <Child testId=\"parent\" />\n        <SangteProvider\n          initialize={({ set }) => {\n            set(state, 10)\n          }}\n        >\n          <Child testId=\"child\" />\n          <SangteProvider\n            initialize={({ set }) => {\n              set(state, 5)\n            }}\n          >\n            <Child testId=\"grandchild\" />\n          </SangteProvider>\n        </SangteProvider>\n      </SangteProvider>\n    )\n\n    expect(screen.getByTestId('parent')).toHaveTextContent('3')\n    expect(screen.getByTestId('child')).toHaveTextContent('10')\n    expect(screen.getByTestId('grandchild')).toHaveTextContent('5')\n    fireEvent.click(screen.getByTestId('button-reset-grandchild'))\n    expect(screen.getByTestId('parent')).toHaveTextContent(/^0$/)\n    expect(screen.getByTestId('child')).toHaveTextContent(/^0$/)\n    expect(screen.getByTestId('grandchild')).toHaveTextContent(/^0$/)\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useResetSangte.test.ts",
    "content": "import { act, renderHook } from '@testing-library/react-hooks'\nimport { sangte } from '../../lib'\nimport { useResetSangte } from '../useResetSangte'\nimport { useSangte } from '../useSangte'\n\ndescribe('useResetSangte', () => {\n  it('resets to initialState', () => {\n    const state = sangte(0)\n    const { result } = renderHook(() => useSangte(state))\n    act(() => {\n      result.current[1](5)\n    })\n    expect(result.current[0]).toBe(5)\n    const {\n      result: { current: reset },\n    } = renderHook(() => useResetSangte(state))\n    act(() => {\n      reset()\n    })\n    expect(result.current[0]).toBe(0)\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useSangte.test.ts",
    "content": "import { sangte } from '../../lib'\nimport { useSangte } from '..'\nimport { renderHook, act } from '@testing-library/react-hooks'\n\ndescribe('useSangte', () => {\n  it('can be called', () => {\n    const counterState = sangte(0)\n    const { result } = renderHook(() => useSangte(counterState))\n    expect(result.current[0]).toBe(0)\n    expect(typeof result.current[1]).toBe('function')\n  })\n  it('can update the state', () => {\n    const counterState = sangte(0)\n    const { result } = renderHook(() => useSangte(counterState))\n\n    act(() => {\n      result.current[1](10)\n    })\n    expect(result.current[0]).toBe(10)\n    act(() => {\n      result.current[1]((prev) => prev + 1)\n    })\n    expect(result.current[0]).toBe(11)\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useSangteActions.test.ts",
    "content": "import { renderHook, act } from '@testing-library/react-hooks'\nimport { sangte } from '../../lib'\nimport { useSangteActions } from '../useSangteActions'\nimport { useSangteValue } from '../useSangteValue'\n\ndescribe('useSangteActions', () => {\n  it('returns actions', () => {\n    const state = sangte(0, (prev) => ({\n      increase() {\n        return prev + 1\n      },\n      decrease() {\n        return prev - 1\n      },\n    }))\n    const { result } = renderHook(() => useSangteActions(state))\n    expect(typeof result.current.decrease).toBe('function')\n  })\n  it('updates value according to actions', () => {\n    const state = sangte(0, (prev) => ({\n      increase() {\n        return prev + 1\n      },\n      decrease() {\n        return prev - 1\n      },\n    }))\n    const {\n      result: { current: actions },\n    } = renderHook(() => useSangteActions(state))\n    const { result } = renderHook(() => useSangteValue(state))\n    expect(result.current).toBe(0)\n    act(() => {\n      actions.increase()\n    })\n    expect(result.current).toBe(1)\n  })\n  it('throws error when action not defined', () => {\n    const state = sangte(0)\n    expect(() => {\n      const {\n        result: { current: actions },\n        // @ts-ignore\n      } = renderHook(() => useSangteActions(state))\n    }).toThrowError()\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useSangteCallback.test.ts",
    "content": "import { act, renderHook } from '@testing-library/react-hooks'\nimport { sangte } from '../../lib'\nimport { useSangteCallback } from '../useSangteCallback'\nimport { useSangteValue } from '../useSangteValue'\n\ndescribe('useSangteCallback', () => {\n  it('get value', () => {\n    const state = sangte(5)\n\n    let value\n    const { result } = renderHook(() =>\n      useSangteCallback(({ get }) => {\n        value = get(state)\n      }, [])\n    )\n    act(() => {\n      result.current()\n    })\n    expect(value).toBe(5)\n  })\n\n  it('set value', () => {\n    const state = sangte(0)\n\n    const { result: sangteValue } = renderHook(() => useSangteValue(state))\n    expect(sangteValue.current).toBe(0)\n\n    const { result } = renderHook(() =>\n      useSangteCallback(({ set }) => {\n        set(state, 100)\n      }, [])\n    )\n    act(() => {\n      result.current()\n    })\n    expect(sangteValue.current).toBe(100)\n  })\n\n  it('update value with action', () => {\n    const state = sangte(0, (prev) => ({\n      increase() {\n        return prev + 1\n      },\n    }))\n\n    const { result: sangteValue } = renderHook(() => useSangteValue(state))\n    expect(sangteValue.current).toBe(0)\n\n    const { result } = renderHook(() =>\n      useSangteCallback(({ actions }) => {\n        const { increase } = actions(state)\n        increase()\n      }, [])\n    )\n    act(() => {\n      result.current()\n    })\n    expect(sangteValue.current).toBe(1)\n  })\n\n  it('throws error when action not defined ', () => {\n    const state = sangte(0)\n\n    const { result } = renderHook(() =>\n      useSangteCallback(({ actions }) => {\n        // @ts-ignore\n        actions(state)\n      }, [])\n    )\n\n    expect(() => {\n      act(() => {\n        result.current()\n      })\n    }).toThrowError()\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useSangteStore.test.ts",
    "content": "import { renderHook } from '@testing-library/react-hooks'\nimport { sangte } from '../../lib'\nimport { useSangteStore } from '../useSangteStore'\n\ndescribe('useSangteStore', () => {\n  it('returns store', () => {\n    const state = sangte(0)\n    const { result } = renderHook(() => useSangteStore(state))\n    expect(result.current).toHaveProperty('getState')\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useSangteValue.test.ts",
    "content": "import { renderHook } from '@testing-library/react-hooks'\nimport { sangte } from '../../lib'\nimport { useSangteValue } from '../useSangteValue'\n\ndescribe('useSangteValue', () => {\n  it('returns value', () => {\n    const state = sangte(0)\n    const { result } = renderHook(() => useSangteValue(state))\n    expect(result.current).toBe(0)\n  })\n\n  it('selects value', () => {\n    const state = sangte({ count: 0 })\n    const { result } = renderHook(() => useSangteValue(state, (state) => state.count))\n    expect(result.current).toBe(0)\n  })\n\n  it('selects memoized value', () => {\n    const numbersState = sangte([0, 1, 2, 3, 4, 5])\n    const filteredNumbersState = sangte((get) => get(numbersState).filter((number) => number > 2))\n    const { result } = renderHook(() => useSangteValue(filteredNumbersState))\n    expect(result.current).toEqual([3, 4, 5])\n  })\n\n  it('selects multiple fields', () => {\n    const state = sangte({ count: 0, name: 'foo' })\n    const { result } = renderHook(() =>\n      useSangteValue(state, (state) => ({\n        count: state.count,\n        name: state.name,\n      }))\n    )\n    expect(result.current).toEqual({ count: 0, name: 'foo' })\n  })\n})\n"
  },
  {
    "path": "src/hooks/__tests__/useSetSangte.test.ts",
    "content": "import { act, renderHook } from '@testing-library/react-hooks'\nimport { sangte } from '../../lib'\nimport { useSangteValue } from '../useSangteValue'\nimport { useSetSangte } from '../useSetSangte'\n\ndescribe('useSetSangte', () => {\n  it('updates value', () => {\n    const state = sangte(0)\n    const { result } = renderHook(() => useSangteValue(state))\n    expect(result.current).toBe(0)\n    const {\n      result: { current },\n    } = renderHook(() => useSetSangte(state))\n    act(() => {\n      current(5)\n    })\n    expect(result.current).toBe(5)\n  })\n})\n"
  },
  {
    "path": "src/hooks/index.ts",
    "content": "export * from './useResetSangte'\nexport * from './useSangte'\nexport * from './useSangteActions'\nexport * from './useSangteStore'\nexport * from './useSangteValue'\nexport * from './useSetSangte'\nexport * from './useSangteCallback'\nexport * from './useResetAllSangte'\n"
  },
  {
    "path": "src/hooks/useResetAllSangte.ts",
    "content": "import { useCallback } from 'react'\nimport { useSangteManager } from '../contexts'\n\n/**\n * Resets every sangte registered to the current SangteProvider.\n */\nexport function useResetAllSangte() {\n  const sangteManager = useSangteManager()\n  return useCallback((global?: boolean) => sangteManager.reset(global), [sangteManager])\n}\n"
  },
  {
    "path": "src/hooks/useResetSangte.ts",
    "content": "import { Sangte } from '../lib/sangte'\nimport { useSangteStore } from './useSangteStore'\n\nexport function useResetSangte<T>(sangte: Sangte<T>) {\n  const store = useSangteStore(sangte)\n  return store.reset\n}\n"
  },
  {
    "path": "src/hooks/useSangte.ts",
    "content": "import { useSyncExternalStore } from 'use-sync-external-store/shim'\nimport { Sangte } from '../lib/sangte'\nimport { useSangteStore } from './useSangteStore'\n\nexport function useSangte<T>(sangte: Sangte<T>) {\n  const store = useSangteStore(sangte)\n  const state = useSyncExternalStore(store.subscribe, store.getState, store.getState)\n  return [state, store.setState] as const\n}\n"
  },
  {
    "path": "src/hooks/useSangteActions.ts",
    "content": "import { ActionRecord, Sangte } from '../lib/sangte'\nimport { useSangteStore } from './useSangteStore'\n\nexport function useSangteActions<T, A extends ActionRecord<T>>(sangte: Sangte<T, A>) {\n  const store = useSangteStore(sangte)\n\n  if (!store.actions) {\n    throw new Error('This sangte does not have createActions')\n  }\n\n  return store.actions\n}\n"
  },
  {
    "path": "src/hooks/useSangteCallback.ts",
    "content": "import { DependencyList, useCallback } from 'react'\nimport { useSangteManager } from '../contexts/SangteProvider'\nimport { ActionRecord, Sangte } from '../lib/sangte'\n\ninterface SanteCallbackParams {\n  get: <T>(sangte: Sangte<T>) => T\n  set: <T>(sangte: Sangte<T>, value: T) => void\n  actions: <T, A extends ActionRecord<T>>(sangte: Sangte<T, A>) => A\n}\n\nexport function useSangteCallback(\n  callback: (params: SanteCallbackParams) => void,\n  deps: DependencyList\n) {\n  const sangteManager = useSangteManager()\n\n  return useCallback(() => {\n    callback({\n      get: (sangte) => sangteManager.get(sangte).getState(),\n      set: (sangte, value) => sangteManager.get(sangte).setState(value),\n      actions: <T, A extends ActionRecord<T>>(sangte: Sangte<T, A>) => {\n        const actions = sangteManager.get(sangte).actions\n        if (!actions) throw new Error('This sangte does not have createActions')\n\n        return actions\n      },\n    })\n  }, [...deps, sangteManager])\n}\n"
  },
  {
    "path": "src/hooks/useSangteStore.ts",
    "content": "import { useSangteManager } from '../contexts/SangteProvider'\nimport { Sangte } from '../lib/sangte'\n\nexport function useSangteStore<T, A>(sangte: Sangte<T, A>) {\n  const sangteManager = useSangteManager()\n  const store = sangteManager.get(sangte)\n  return store\n}\n"
  },
  {
    "path": "src/hooks/useSangteValue.ts",
    "content": "import { useSyncExternalStore } from 'use-sync-external-store/shim'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'\nimport { Sangte } from '../lib/sangte'\nimport { shallowEqual } from '../lib/shallowEqual'\nimport { useSangteStore } from './useSangteStore'\n\nexport function useSangteValue<T>(sangte: Sangte<T>): T\nexport function useSangteValue<T, S>(sangte: Sangte<T>, selector: (state: T) => S): S\n\nexport function useSangteValue<T, S>(\n  sangte: Sangte<T>,\n  selector?: (state: T) => S,\n  compare?: (a: S, b: S) => boolean\n) {\n  const store = useSangteStore(sangte)\n  if (!selector) {\n    const state = useSyncExternalStore(store.subscribe, store.getState, store.getState)\n    return state\n  }\n  const state = useSyncExternalStoreWithSelector(\n    store.subscribe,\n    store.getState,\n    store.getState,\n    selector,\n    compare ?? shallowEqual\n  )\n  return state\n}\n"
  },
  {
    "path": "src/hooks/useSetSangte.ts",
    "content": "import { Sangte } from '../lib/sangte'\nimport { useSangteStore } from './useSangteStore'\n\nexport function useSetSangte<T>(sangte: Sangte<T>) {\n  const store = useSangteStore(sangte)\n  return store.setState\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './contexts'\nexport * from './hooks'\nexport * from './lib'\n"
  },
  {
    "path": "src/lib/SangteInitializer.ts",
    "content": "import { Sangte } from './sangte'\nimport { SangteManager } from './SangteManager'\n\nexport class SangteInitializer {\n  private sangteInitialStateMap = new Map<Sangte<any, any>, any>()\n\n  constructor(private manager: SangteManager) {}\n\n  public set<T>(sangte: Sangte<T, any>, initialState: T) {\n    this.sangteInitialStateMap.set(sangte, initialState)\n  }\n\n  public initialize() {\n    this.sangteInitialStateMap.forEach((initialState, sangte) => {\n      const instance = this.manager.get(sangte)\n      if (sangte.config.isResangte) return\n      instance.setState(initialState)\n    })\n  }\n}\n"
  },
  {
    "path": "src/lib/SangteManager.ts",
    "content": "import { Sangte, SangteInstance } from './sangte'\nimport { SangteInitializer } from './SangteInitializer'\n\nexport class SangteManager {\n  private instanceMap = new Map<Sangte<any, any>, SangteInstance<any, any>>()\n  public initializer: SangteInitializer = new SangteInitializer(this)\n  public children = new Set<SangteManager>()\n\n  constructor(public isDefault: boolean = false) {}\n  public get<T, A>(sangte: Sangte<T, A>): SangteInstance<T, A> {\n    const manager = sangte.config.global ? this.getRootSangteManager() : this\n    const instance = manager.instanceMap.get(sangte)\n\n    if (instance) {\n      return instance\n    }\n    const newInstance = sangte(this)\n    if (sangte.config.key && this.dehydratedState && !sangte.config.isResangte) {\n      const selected = this.dehydratedState[sangte.config.key]\n      if (selected) {\n        newInstance.setState(selected)\n      }\n    }\n\n    manager.instanceMap.set(sangte, newInstance)\n    return newInstance\n  }\n\n  public parent: SangteManager | null = null\n  public dehydratedState?: Record<string, any> | null\n\n  public getRootSangteManager(): SangteManager {\n    let manager: SangteManager | undefined = this\n    while (manager.parent) {\n      manager = manager.parent\n    }\n    return manager\n  }\n\n  public inherit(sangtes: Sangte<any>[]) {\n    const parent = this.parent\n    if (!parent) return\n    sangtes.forEach((sangte) => {\n      const sangteInstance = parent.get(sangte)\n      this.instanceMap.set(sangte, sangteInstance)\n    })\n  }\n\n  /** resets all sangte registered to this SangteManager */\n  public reset(global: boolean = false) {\n    if (global) {\n      this.getRootSangteManager().reset()\n      return\n    }\n    Array.from(this.instanceMap.entries()).forEach(([sangte, instance]) => {\n      if (!sangte.config.isResangte) {\n        instance.reset()\n      }\n    })\n    this.children.forEach((child) => child.reset())\n  }\n}\n"
  },
  {
    "path": "src/lib/__tests__/SangteManager.test.ts",
    "content": "import { sangte } from '../sangte'\nimport { SangteManager } from '../SangteManager'\n\ndescribe('SangteManager', () => {\n  it('creates instance', () => {\n    const manager = new SangteManager()\n    expect(manager).toBeInstanceOf(SangteManager)\n  })\n  it('gets root manager', () => {\n    const grandParent = new SangteManager()\n    const parent = new SangteManager()\n    parent.parent = grandParent\n    const child = new SangteManager()\n    child.parent = parent\n    expect(child.getRootSangteManager()).toBe(grandParent)\n  })\n  it('resets all sangte', () => {\n    const counterState = sangte(0)\n    const textState = sangte('')\n    const manager = new SangteManager()\n    manager.get(counterState).setState(1)\n    manager.get(textState).setState('hello')\n    expect(manager.get(counterState).getState()).toBe(1)\n    expect(manager.get(textState).getState()).toBe('hello')\n    manager.reset()\n    expect(manager.get(counterState).getState()).toBe(0)\n    expect(manager.get(textState).getState()).toBe('')\n  })\n})\n"
  },
  {
    "path": "src/lib/__tests__/sangte.test.ts",
    "content": "import { sangte } from '../sangte'\nimport { SangteManager } from '../SangteManager'\n\ndescribe('sangte', () => {\n  it('can be created', () => {\n    const create = sangte({ count: 0 })\n    const store = create()\n    expect(store.getState().count).toBe(0)\n  })\n\n  it('can be updated with setState', () => {\n    const create = sangte({ count: 0 })\n    const store = create()\n    store.setState({ count: 1 })\n    expect(store.getState().count).toBe(1)\n    store.setState((prev) => ({ count: prev.count + 5 }))\n    expect(store.getState().count).toBe(6)\n  })\n\n  it('calls subscriptions when updated', () => {\n    const create = sangte({ count: 0 })\n    const store = create()\n    const callback = jest.fn()\n    const callback2 = jest.fn()\n    store.subscribe(callback)\n    store.subscribe(callback2)\n    store.setState({ count: 1 })\n    expect(callback).toBeCalled()\n    expect(callback2).toBeCalled()\n  })\n\n  it('unsubcribes properly', () => {\n    const create = sangte({ count: 0 })\n    const store = create()\n    const callback = jest.fn()\n    const unsubcribe = store.subscribe(callback)\n    store.setState({ count: 1 })\n    expect(callback).toBeCalled()\n    unsubcribe()\n    callback.mockReset()\n    store.setState({ count: 2 })\n    expect(callback).not.toBeCalled()\n  })\n\n  it('calls actions properly', () => {\n    const create = sangte({ count: 0 }, (prev) => ({\n      increase() {\n        prev.count += 1\n      },\n      decreaseBy(amount: number) {\n        return {\n          ...prev,\n          count: prev.count - amount,\n        }\n      },\n    }))\n    const store = create()\n    const callback = jest.fn()\n\n    store.subscribe(callback)\n\n    expect(store.getState().count).toBe(0)\n    store.actions?.increase()\n    expect(store.getState().count).toBe(1)\n    store.actions?.decreaseBy(5)\n    expect(store.getState().count).toBe(-4)\n\n    expect(callback).toBeCalledTimes(2)\n  })\n\n  it('can be reset', () => {\n    const create = sangte({ count: 0 })\n    const store = create()\n    store.setState({ count: 1 })\n    expect(store.getState().count).toBe(1)\n    store.reset()\n    expect(store.getState().count).toBe(0)\n  })\n\n  it('should keep immutability', () => {\n    const create = sangte({ count: 0 }, (prev) => ({\n      increase() {\n        prev.count += 1\n      },\n    }))\n    const store = create()\n    const prev = store.getState()\n    store.actions?.increase()\n    const next = store.getState()\n    expect(prev).not.toBe(next)\n  })\n\n  it('should have config', () => {\n    const create = sangte(\n      { count: 0 },\n      {\n        global: true,\n        key: 'counter',\n      }\n    )\n    expect(create.config).toEqual({\n      global: true,\n      key: 'counter',\n    })\n  })\n})\n\ndescribe('resangte', () => {\n  it('can be created', () => {\n    const state = sangte([1, 2, 3, 4, 5])\n    const manager = new SangteManager()\n    manager.get(state)\n    const selectedState = sangte((get) => get(state).filter((number) => number > 2))\n    const store = manager.get(selectedState)\n    expect(store.getState()).toEqual([3, 4, 5])\n  })\n  it('properly updates when dependencies change', () => {\n    const state = sangte([1, 2, 3, 4, 5])\n    const manager = new SangteManager()\n    manager.get(state)\n    const selectedState = sangte((get) => get(state).filter((number) => number > 2))\n    const store = manager.get(selectedState)\n    const callback = jest.fn()\n    store.subscribe(callback)\n    manager.get(state).setState([1, 2, 3, 4, 5, 6, 7])\n    expect(callback).toBeCalledTimes(1)\n    expect(store.getState()).toEqual([3, 4, 5, 6, 7])\n  })\n  it('properly handles unmount & remount', () => {\n    const state = sangte([1, 2, 3, 4, 5])\n    const manager = new SangteManager()\n    manager.get(state)\n    const selectedState = sangte((get) => get(state).filter((number) => number > 2))\n    const store = manager.get(selectedState)\n    const callback = jest.fn()\n    let unsubscribe = store.subscribe(callback)\n    unsubscribe()\n    manager.get(state).setState([1, 2, 3, 4, 5, 6, 7])\n    expect(callback).not.toBeCalled()\n    // still gets updated because getState calls selector when unmounted\n    expect(store.getState()).toEqual([3, 4, 5, 6, 7])\n  })\n  it('warns when calling setState or reset', () => {\n    const state = sangte([1, 2, 3, 4, 5])\n    const manager = new SangteManager()\n    manager.get(state)\n    const selectedState = sangte((get) => get(state).filter((number) => number > 2))\n    const store = manager.get(selectedState)\n    const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation()\n    store.reset()\n    store.setState([1, 2, 3])\n    expect(consoleWarnMock).toBeCalledTimes(2)\n    consoleWarnMock.mockRestore()\n  })\n  it('throws error when manager is not used', () => {\n    const state = sangte([1, 2, 3, 4, 5])\n    const selectedState = sangte((get) => get(state).filter((number) => number > 2))\n    expect(() => {\n      const store = selectedState()\n    }).toThrowError()\n  })\n})\n"
  },
  {
    "path": "src/lib/__tests__/shallowEqual.test.ts",
    "content": "import { shallowEqual } from '../shallowEqual'\n\ndescribe('shallowEqual', () => {\n  it('returns true if comparing same object', () => {\n    const obj = { a: 1, b: 2 }\n    expect(shallowEqual(obj, obj)).toBe(true)\n  })\n  it('returns true if they are shallowly equal', () => {\n    const obj1 = { a: 1, b: 2 }\n    const obj2 = { a: 1, b: 2 }\n    expect(shallowEqual(obj1, obj2)).toBe(true)\n  })\n  it('returns false if they are shallowly different', () => {\n    const obj1 = { a: 1, b: 2 }\n    const obj2 = { a: 1, b: 3 }\n    expect(shallowEqual(obj1, obj2)).toBe(false)\n  })\n  it('returns true when arrays are shallowly equal', () => {\n    const arr1 = [1, 2]\n    const arr2 = [1, 2]\n    expect(shallowEqual(arr1, arr2)).toBe(true)\n  })\n  it('returns false when arrays are shallowly different', () => {\n    const arr1 = [1, 2]\n    const arr2 = [1, 5]\n    expect(shallowEqual(arr1, arr2)).toBe(false)\n  })\n})\n"
  },
  {
    "path": "src/lib/index.ts",
    "content": "export * from './sangte'\nexport * from './SangteInitializer'\nexport * from './SangteManager'\n"
  },
  {
    "path": "src/lib/sangte.ts",
    "content": "import produce, { Draft, isDraftable } from 'immer'\nimport { SangteManager } from './SangteManager'\n\ntype Fn = () => void\ntype UpdateFn<T> = (state: T) => T\nexport type ActionRecord<T> = Record<string, (...params: any[]) => T | Draft<T> | void>\ntype Action<T, A> = A extends ActionRecord<T> ? A : never\nexport type Actions<T, A> = (prevState: Draft<T> | T) => Action<T, A>\nexport interface SangteConfig {\n  key?: string\n  global?: boolean\n  isResangte?: boolean\n}\n\nexport type Getter = {\n  <T>(sangte: Sangte<T>): T\n}\n\nexport type SangteInstance<T, A> = {\n  initialState: T\n  getState: () => T\n  setState: (update: UpdateFn<T> | T) => void\n  subscribe: (callback: Fn) => Fn\n  actions: Action<T, A> | null\n  reset: () => void\n}\nexport type Sangte<T, A = any> = {\n  (manager?: SangteManager): SangteInstance<T, A>\n  config: SangteConfig\n}\n\nexport type UnwrapSangteValue<T> = T extends Sangte<infer U> ? U : never\nexport type UnwrapSangteAction<T> = T extends Sangte<any, infer U> ? U : never\n\ntype Selector<T> = (get: Getter) => T\nfunction isSelector<T>(fn: any): fn is Selector<T> {\n  return typeof fn === 'function'\n}\n\nfunction isUpdateFn<T>(value: any): value is UpdateFn<T> {\n  return typeof value === 'function'\n}\n\nfunction createSangte<T, A>(initialState: T, createActions?: Actions<T, A>): SangteInstance<T, A> {\n  let state = initialState\n  const callbacks = new Set<Fn>()\n\n  function getState() {\n    return state\n  }\n\n  function setState(update: UpdateFn<T> | T) {\n    if (isUpdateFn<T>(update)) {\n      state = update(state)\n    } else {\n      state = update\n    }\n    callbacks.forEach((cb) => cb())\n  }\n\n  function subscribe(callback: Fn): Fn {\n    callbacks.add(callback)\n    return () => callbacks.delete(callback)\n  }\n\n  function reset() {\n    setState(initialState)\n  }\n\n  const actions = (() => {\n    if (!createActions) return null\n    type ActionKey = keyof Action<T, A>\n    const record = createActions(initialState)\n    const keys = Object.keys(record) as ActionKey[]\n    keys.forEach((key) => {\n      record[key] = ((...params: any[]) => {\n        setState((prevState) => {\n          if (!isDraftable(prevState)) {\n            const action = createActions(prevState)[key]\n            const next = action(...params)\n            return next as any\n          }\n          const produced = produce(prevState, (draft) => {\n            const action = createActions(draft)[key]\n            const result = action(...params)\n            if (result !== undefined) {\n              return result as Draft<T>\n            }\n          })\n          return produced\n        })\n      }) as Action<T, A>[ActionKey]\n    })\n    return record\n  })()\n\n  return {\n    initialState,\n    getState,\n    setState,\n    subscribe,\n    actions,\n    reset,\n  }\n}\n\nexport function sangte<T>(selector: (get: Getter) => T): Sangte<T>\nexport function sangte<T>(initialState: T): Sangte<T>\nexport function sangte<T, A>(initialState: T, config?: SangteConfig): Sangte<T, A>\nexport function sangte<T, A>(\n  initialState: T,\n  actions: Actions<T, A>,\n  config?: SangteConfig\n): Sangte<T, A>\nexport function sangte<T, A>(\n  selectorOrInitialState: T | ((get: Getter) => T),\n  actionsOrConfig?: Actions<T, A> | SangteConfig,\n  config?: SangteConfig\n) {\n  if (isSelector(selectorOrInitialState)) {\n    return resangte(selectorOrInitialState)\n  }\n\n  const hasActions = typeof actionsOrConfig === 'function'\n  const sangte = function () {\n    if (hasActions) {\n      return createSangte(selectorOrInitialState, actionsOrConfig)\n    }\n    return createSangte(selectorOrInitialState)\n  }\n  if (hasActions) {\n    sangte.config = config || {}\n  } else {\n    sangte.config = actionsOrConfig || {}\n  }\n\n  return sangte\n}\n\nfunction createResangte<T>(\n  selector: (getter: Getter) => T,\n  sangteManager: SangteManager\n): SangteInstance<T, any> {\n  let unmounted = false\n  const sangteDeps = new Set<Sangte<any, any>>()\n  const subscriptions = new Set<() => void>()\n  const getter: Getter = (sangte) => {\n    if (!sangteDeps.has(sangte)) {\n      sangteDeps.add(sangte)\n    }\n    return sangteManager.get(sangte).getState()\n  }\n  let state = selector(getter)\n  const callbacks = new Set<Fn>()\n  const getState = () => {\n    if (unmounted) {\n      unmounted = false\n      state = selector(getter)\n    }\n    return state\n  }\n\n  const update = () => {\n    state = selector(getter)\n    callbacks.forEach((cb) => cb())\n  }\n\n  const subscribe = (callback: Fn) => {\n    if (callbacks.size === 0) {\n      sangteDeps.forEach((sangte) => {\n        const unsubscribe = sangteManager.get(sangte).subscribe(update)\n        subscriptions.add(unsubscribe)\n      })\n    }\n    callbacks.add(callback)\n    return () => {\n      callbacks.delete(callback)\n      if (callbacks.size === 0) {\n        subscriptions.forEach((unsubscribe) => unsubscribe())\n        subscriptions.clear()\n        unmounted = true\n      }\n    }\n  }\n\n  const setState = () => {\n    console.warn('setState is not supported in resangte')\n  }\n\n  const reset = () => {\n    console.warn('reset is not supported in resangte')\n  }\n\n  return {\n    initialState: state,\n    actions: null,\n    getState,\n    subscribe,\n    setState,\n    reset,\n  }\n}\n\nexport function resangte<T>(selector: (getter: Getter) => T): Sangte<T> {\n  const resangte = function (sangteManager?: SangteManager) {\n    if (!sangteManager) {\n      throw new Error('Cannot create resangte without a manager')\n    }\n    return createResangte(selector, sangteManager)\n  }\n  resangte.config = {\n    isResangte: true,\n  }\n\n  return resangte\n}\n"
  },
  {
    "path": "src/lib/shallowEqual.ts",
    "content": "export function shallowEqual<T extends Record<any, any>>(a: T, b: T) {\n  if (a === b) return true\n  if (!(a instanceof Object) || !(b instanceof Object)) return false\n\n  const keys = Object.keys(a) as (keyof T)[]\n  const length = keys.length\n\n  for (let i = 0; i < length; i++) if (!(keys[i] in b)) return false\n\n  for (let i = 0; i < length; i++) if (a[keys[i]] !== b[keys[i]]) return false\n\n  return length === Object.keys(b).length\n}\n"
  },
  {
    "path": "src/setupTest.ts",
    "content": "import '@testing-library/jest-dom/extend-expect'\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\"src\"]\n}\n"
  }
]