{"id":20489294,"url":"https://github.com/kyza/nests","last_synced_at":"2025-04-13T16:32:38.100Z","repository":{"id":43939573,"uuid":"382522225","full_name":"Kyza/nests","owner":"Kyza","description":"Fast and easy state storage with a lot of control.","archived":false,"fork":false,"pushed_at":"2023-08-03T05:51:38.000Z","size":344,"stargazers_count":13,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"v2","last_synced_at":"2025-04-09T03:34:52.127Z","etag":null,"topics":["easy","flux","javascript","nests","redux","state","store"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kyza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-03T04:13:43.000Z","updated_at":"2024-10-22T11:06:00.000Z","dependencies_parsed_at":"2024-06-21T13:13:51.836Z","dependency_job_id":"f786b131-f693-40dd-89f5-f1ddb1647765","html_url":"https://github.com/Kyza/nests","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fnests","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fnests/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fnests/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kyza%2Fnests/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kyza","download_url":"https://codeload.github.com/Kyza/nests/tar.gz/refs/heads/v2","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248743997,"owners_count":21154784,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["easy","flux","javascript","nests","redux","state","store"],"created_at":"2024-11-15T17:12:13.556Z","updated_at":"2025-04-13T16:32:38.074Z","avatar_url":"https://github.com/Kyza.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eNests\u003c/h1\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n\tFast and easy state storage with a lot of control.\r\n\u003c/p\u003e\r\n\r\n---\r\n\r\n\u003cp align=\"center\"\u003e\r\n\t\u003cimg src=\"https://badgen.net/github/watchers/Kyza/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/github/stars/Kyza/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/github/forks/Kyza/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/github/issues/Kyza/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/github/prs/Kyza/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/github/license/Kyza/nests\" /\u003e\r\n\u003c/p\u003e\r\n\u003cp align=\"center\"\u003e\r\n\t\u003cimg src=\"https://badgen.net/bundlephobia/min/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/bundlephobia/minzip/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/bundlephobia/dependency-count/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/bundlephobia/tree-shaking/nests\" /\u003e\r\n\u003c/p\u003e\r\n\u003cp align=\"center\"\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/v/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/dw/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/dm/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/dy/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/dt/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/dependents/nests\" /\u003e\r\n\t\u003cimg src=\"https://badgen.net/npm/types/nests\" /\u003e\r\n\u003c/p\u003e\r\n\r\n---\r\n\r\n```js\r\nimport * as nests from \"nests\";\r\nconst nest = nests.make();\r\n\r\nnest.on(nests.Events.SET, (eventType, { path, value }) =\u003e {\r\n\tconsole.log(`set: ${path} = ${value}`);\r\n});\r\n\r\nnest.store.array = [1, 2, 3];\r\nnest.store.array.push(4);\r\nnest.store.instant.deeply.nested.state = true;\r\n\r\n/* Console Output */\r\n// set: array = 1,2,3\r\n// set: array,3 = 4\r\n// set: array,length = 4\r\n// set: instant,deeply,nested,state = true\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm i nests\r\n```\r\n\r\nWhen importing you can specify `nests/esm` or `nests/mjs` if your bundler is giving you problems.\r\n\r\n```js\r\nimport * as nests from \"nests\";\r\n```\r\n\r\n## Links\r\n\r\n- [GitHub](https://github.com/Kyza/nests)\r\n- [NPM](https://www.npmjs.com/package/nests)\r\n- [Bundlephobia](https://bundlephobia.com/package/nests@latest)\r\n\r\n## Features\r\n\r\n- [Instant Deeply Nested Objects](#instant-deeply-nested-objects)\r\n- [Automatic Events](#automatic-events)\r\n- [Ghost](#ghost)\r\n- [React](#react)\r\n- [Solid](#solid)\r\n\r\n## Concepts\r\n\r\n### Store\r\n\r\nThe store is an object that lets you store data in an instantly deeply nested structure which automatically emits events to subscribers for you.\r\n\r\n#### Instant Deeply Nested Objects\r\n\r\nHave you ever had to do something like this?\r\n\r\n```js\r\nconst storage = {};\r\n\r\nif (!storage.hasOwnProperty(\"foo\")) {\r\n\tstorage.foo = \"bar\";\r\n}\r\n```\r\n\r\nWith Nests this isn't necessary anymore.\r\n\r\n```js\r\nconst nest = nests.make();\r\n\r\nnest.store.foo = \"bar\";\r\n```\r\n\r\nThe code is even shorter and more readable.\r\n\r\nYou can go as deep as you want instantly without having to check if the property exists.\r\n\r\n```js\r\nnest.store.foo.bar.baz = \"qux\";\r\n```\r\n\r\nGetting a property that doesn't exist will return `{}` instead of `undefined`. Use [ghost](#ghost) to check for non-existent properties correctly.\r\n\r\n```js\r\nnest.store.foo.bar.baz; // {}\r\n```\r\n\r\n### Automatic Events\r\n\r\nNests comes with a small, custom, and fast, browser-ready EventEmitter.\r\n\r\n```js\r\nnest.on(nests.Events.SET, (eventType, { path, value }) =\u003e {\r\n\tconsole.log(`set: ${path} = ${value}`);\r\n});\r\n\r\nnest.store.array = [1, 2, 3];\r\nnest.store.array.push(4);\r\nnest.store.instant.deeply.nested.state = true;\r\n\r\n/* Console Output */\r\n// set: array = 1,2,3\r\n// set: array,3 = 4\r\n// set: array,length = 4\r\n// set: instant,deeply,nested,state = true\r\n```\r\n\r\n### Ghost\r\n\r\nThe ghost is an object that is dual-synced with the store but isn't instantly deeply nested, doesn't emit events and is faster.\r\n\r\nEach nest has a `ghost` property that can be used to modify the store's data without triggering events. This is super handy for doing heavy calculations with many mutations and creating transient React components easily.\r\n\r\nThe ghost of each nest is directly tied to the store so modifications to the ghost will be reflected in the store.\r\n\r\n```js\r\nnest.on(nests.Events.SET, (eventType, { path, value }) =\u003e {\r\n\tconsole.log(`set: ${path} = ${value}`);\r\n});\r\n\r\nnest.ghost.array = [1, 2, 3];\r\nnest.ghost.array.push(4);\r\n\r\nnest.store.array.push(5);\r\n\r\nconsole.log(\"The sync goes both ways:\", nest.ghost.array);\r\n\r\n/* Console Output */\r\n// set: array,4 = 5\r\n// set: array,length = 5\r\n// The sync goes both ways: 1,2,3,4,5\r\n```\r\n\r\nIf you want to trigger an event manually without using the store, you can use any of these functions.\r\n\r\n```js\r\n// Use this.\r\nnest.update();\r\n// Using these is discouraged.\r\nnest.get();\r\nnest.set();\r\nnest.delete();\r\n```\r\n\r\nThe downfall of ghost is that it doesn't support [Instant Deeply Nested Objects](#instant-deeply-nested-objects).\r\n\r\n```js\r\nnest.ghost.foo.bar.baz = \"qux\"; // Error!\r\n```\r\n\r\nThis is also a good thing. It allows you to check for non-existent properties unlike the store.\r\n\r\n```js\r\nif (!nest.ghost.foo?.bar?.baz) {\r\n\t// ...\r\n}\r\n```\r\n\r\n## React\r\n\r\nHere's an example of a React component.\r\n\r\n```js\r\nimport * as nests from \"nests\";\r\nimport { useNest } from \"nests/react\";\r\n\r\nconst settings = nests.make({\r\n\tenabled: true,\r\n\tname: \"\",\r\n});\r\n\r\nexport default function App() {\r\n\t// Automatically subscribe to changes in the store.\r\n\tuseNest(settings);\r\n\r\n\treturn (\r\n\t\t\u003c\u003e\r\n\t\t\t\u003cbutton\r\n\t\t\t\tonClick={() =\u003e {\r\n\t\t\t\t\tsettings.store.enabled = !settings.store.enabled;\r\n\t\t\t\t}}\r\n\t\t\t\u003e\r\n\t\t\t\t{settings.ghost.enabled ? \"Enabled\" : \"Disabled\"}\r\n\t\t\t\u003c/button\u003e\r\n\t\t\t\u003cinput\r\n\t\t\t\ttype=\"text\"\r\n\t\t\t\tvalue={settings.ghost.name}\r\n\t\t\t\tonInput={(event) =\u003e {\r\n\t\t\t\t\tsettings.store.name = event.target.value;\r\n\t\t\t\t}}\r\n\t\t\t/\u003e\r\n\t\t\u003c/\u003e\r\n\t);\r\n}\r\n```\r\n\r\nHere's an example of a transient React component.\r\n\r\n```js\r\nimport * as nests from \"nests\";\r\nimport { useNest } from \"nests/react\";\r\n\r\nconst counter = nests.make({\r\n\tcount: 0,\r\n});\r\n\r\nsetInterval(() =\u003e {\r\n\t// Increment using the ghost to not update the component.\r\n\tcounter.ghost.count++;\r\n}, 0);\r\n\r\nexport default function App() {\r\n\t// Automatically subscribe to changes in the store.\r\n\t// Pass true to indicate that it's a transient component.\r\n\t// Here we have a filter as well to only update the component when we want to.\r\n\t// That lets us avoid updating the component when a property we don't care about is changed.\r\n\tuseNest(\r\n\t\tcounter,\r\n\t\ttrue,\r\n\t\t(event, type) =\u003e event === \"UPDATE\" \u0026\u0026 type === \"counter\"\r\n\t);\r\n\r\n\treturn (\r\n\t\t\u003c\u003e\r\n\t\t\t{counter.ghost.count}\r\n\t\t\t\u003cbutton\r\n\t\t\t\tonClick={() =\u003e {\r\n\t\t\t\t\t// Whenever the button is clicked, cause an update on the store.\r\n\t\t\t\t\t// You can pass data to the update function which passes it to the filter in the useNest above.\r\n\t\t\t\t\tcounter.update(\"counter\");\r\n\t\t\t\t}}\r\n\t\t\t\u003e\r\n\t\t\t\tUpdate\r\n\t\t\t\u003c/button\u003e\r\n\t\t\u003c/\u003e\r\n\t);\r\n}\r\n```\r\n\r\nNotice that whenever any data is displayed the ghost is used to retrieve it. This is because the ghost is faster and doesn't emit events.\r\n\r\n## Solid\r\n\r\nHere's an example of a Solid component.\r\n\r\nNotice the key difference between Solid and React's `useNest` hooks. While both hooks return the ghost, you're required to use the ghost the Solid hook returns in order for the component to update.\r\n\r\n```js\r\nimport * as nests from \"nests\";\r\nimport { useNest } from \"nests/solid-js\";\r\n\r\nconst settings = nests.make({\r\n\tenabled: true,\r\n\tname: \"\",\r\n});\r\n\r\nexport default function App() {\r\n\t// Automatically subscribe to changes in the store.\r\n\tconst signalGhost = useNest(settings);\r\n\r\n\treturn (\r\n\t\t\u003c\u003e\r\n\t\t\t\u003cbutton\r\n\t\t\t\tonClick={() =\u003e {\r\n\t\t\t\t\tsettings.store.enabled = !settings.store.enabled;\r\n\t\t\t\t}}\r\n\t\t\t\u003e\r\n\t\t\t\t{signalGhost.enabled ? \"Enabled\" : \"Disabled\"}\r\n\t\t\t\u003c/button\u003e\r\n\t\t\t\u003cinput\r\n\t\t\t\ttype=\"text\"\r\n\t\t\t\tvalue={signalGhost.name}\r\n\t\t\t\tonInput={(event) =\u003e {\r\n\t\t\t\t\tsettings.store.name = event.target.value;\r\n\t\t\t\t}}\r\n\t\t\t/\u003e\r\n\t\t\u003c/\u003e\r\n\t);\r\n}\r\n```\r\n\r\nHere's an example of a transient Solid component.\r\n\r\n```js\r\nimport * as nests from \"nests\";\r\nimport { useNest } from \"nests/solid-js\";\r\n\r\nconst counter = nests.make({\r\n\tcount: 0,\r\n});\r\n\r\nsetInterval(() =\u003e {\r\n\t// Increment using the ghost to not update the component.\r\n\tcounter.ghost.count++;\r\n}, 0);\r\n\r\nexport default function App() {\r\n\t// Automatically subscribe to changes in the store.\r\n\t// Pass true to indicate that it's a transient component.\r\n\t// Here we have a filter as well to only update the component when we want to.\r\n\t// That lets us avoid updating the component when a property we don't care about is changed.\r\n\tconst signalGhost = useNest(\r\n\t\tcounter,\r\n\t\ttrue,\r\n\t\t(event, type) =\u003e event === \"UPDATE\" \u0026\u0026 type === \"counter\"\r\n\t);\r\n\r\n\treturn (\r\n\t\t\u003c\u003e\r\n\t\t\t{signalGhost.count}\r\n\t\t\t\u003cbutton\r\n\t\t\t\tonClick={() =\u003e {\r\n\t\t\t\t\t// Whenever the button is clicked, cause an update on the store.\r\n\t\t\t\t\t// You can pass data to the update function which passes it to the filter in the useNest above.\r\n\t\t\t\t\tcounter.update(\"counter\");\r\n\t\t\t\t}}\r\n\t\t\t\u003e\r\n\t\t\t\tUpdate\r\n\t\t\t\u003c/button\u003e\r\n\t\t\u003c/\u003e\r\n\t);\r\n}\r\n```\r\n\r\nNotice that whenever any data is displayed the ghost is used to retrieve it. This is because the ghost is faster and doesn't emit events.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyza%2Fnests","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyza%2Fnests","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyza%2Fnests/lists"}