{"id":20330292,"url":"https://github.com/uralys/taverne","last_synced_at":"2026-03-09T18:40:01.012Z","repository":{"id":54148046,"uuid":"325264977","full_name":"uralys/taverne","owner":"uralys","description":"⛵ Yet another Redux alternative","archived":false,"fork":false,"pushed_at":"2025-03-20T10:49:35.000Z","size":3570,"stargazers_count":12,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-23T13:09:29.171Z","etag":null,"topics":["flux","flux-architecture","hooks","immer","mobx-react-alternative","react","react-hooks","react-state-management","react-stores","redux-alternative","use-state"],"latest_commit_sha":null,"homepage":"https://taverne.uralys.com","language":"JavaScript","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/uralys.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null},"funding":{"github":["chrisdugne"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-12-29T11:12:04.000Z","updated_at":"2025-03-20T10:49:39.000Z","dependencies_parsed_at":"2025-03-20T11:39:52.497Z","dependency_job_id":null,"html_url":"https://github.com/uralys/taverne","commit_stats":null,"previous_names":[],"tags_count":110,"template":false,"template_full_name":null,"purl":"pkg:github/uralys/taverne","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uralys%2Ftaverne","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uralys%2Ftaverne/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uralys%2Ftaverne/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uralys%2Ftaverne/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/uralys","download_url":"https://codeload.github.com/uralys/taverne/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uralys%2Ftaverne/sbom","scorecard":{"id":911686,"data":{"date":"2025-08-11","repo":{"name":"github.com/uralys/taverne","commit":"3b165f34be85231ca707dab46659d3c62f3e5d1c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: license:0","Info: FSF or OSI recognized license: MIT License: license:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":0,"reason":"27 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-px4h-xg32-q955","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-24T19:26:41.486Z","repository_id":54148046,"created_at":"2025-08-24T19:26:41.487Z","updated_at":"2025-08-24T19:26:41.487Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30307549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T17:35:44.120Z","status":"ssl_error","status_checked_at":"2026-03-09T17:35:43.707Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["flux","flux-architecture","hooks","immer","mobx-react-alternative","react","react-hooks","react-state-management","react-stores","redux-alternative","use-state"],"created_at":"2024-11-14T20:15:49.178Z","updated_at":"2026-03-09T18:40:00.991Z","avatar_url":"https://github.com/uralys.png","language":"JavaScript","funding_links":["https://github.com/sponsors/chrisdugne"],"categories":[],"sub_categories":[],"readme":"# La Taverne\n\n\u003ca href=\"https://www.npmjs.com/package/taverne\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/taverne?color=%23123\" alt=\"Current npm package version.\" /\u003e\u003c/a\u003e \u003ca href=\"https://www.npmjs.com/package/taverne\"\u003e\u003cimg src=\"https://img.shields.io/github/license/uralys/taverne\" alt=\"MIT\" /\u003e\u003c/a\u003e \u003ca href=\"https://immerjs.github.io/immer/produce\"\u003e\u003cimg src=\"https://img.shields.io/badge/immer-produce-5908d2.svg\" alt=\"immer\" /\u003e \u003c/a\u003e\n\n\u003cp align=\"center\"\u003e\u003cimg  src=\"./docs/taverne.png\"\u003e\u003c/p\u003e\n\n`La Taverne` is an elementary [Flux](https://facebook.github.io/flux/docs/in-depth-overview) implementation to manage a global app state.\n\nIt provides an optional, yet easy integration with React using custom **hooks**.\n\n![action-\u003edispatcher-\u003estore-\u003eview](https://facebookarchive.github.io/flux/img/overview/flux-simple-f8-diagram-1300w.png)\n\n## 🕵️ Demo\n\n- Try live on \u003chttps://taverne.uralys.com/\u003e\n- Demo sources: \u003chttps://github.com/uralys/taverne-website\u003e\n\n## 📦 installation\n\n```sh\n\u003e npm i --save taverne\n```\n\n## 🐿️ Instanciate your taverne with your barrels\n\nOnce your barrels are ready, you can instanciate your `taverne` and `dispatch`:\n\n```js\nimport createLaTaverne from 'taverne';\nimport books from './barrels/books';\nimport potions from './barrels/potions';\nimport handcrafts from './barrels/handcrafts';\n\nconst {dispatch, taverne} = createLaTaverne({\n  books,\n  potions,\n  handcrafts\n});\n```\n\n## 🧬 Create a barrel\n\nA \"Barrel\" is an `initialState` and a list of `reactions`.\n\n```js\nconst ADD_BOOK = 'ADD_BOOK';\n\nconst addBook = {\n  on: ADD_BOOK,\n  reduce: (state, payload) =\u003e {\n    const {book} = payload;\n    state.entities.push(book);\n  }\n};\n\nexport default {\n  initialState: {entities: []},\n  reactions: [addBook]\n};\n\nexport {ADD_BOOK};\n```\n\n## 🧚 Reactions\n\n- A `reaction` will be triggered when an action is dispatched with `action.type` === `on`.\n\n```js\nconst doSomethingWithThisBarrel = {\n  on: 'ACTION_TYPE',\n  reduce: (state, payload) =\u003e {\n    /*\n      Just update the state with your payload.\n      Here, `state` is the draftState used by `Immer.produce`\n      You taverne will then record your next immutable state.\n    */\n    state.foo = 'bar';\n  },\n  perform: (parameters, dispatch, getState) =\u003e {\n    /*\n      Optional sync or async function.\n      It will be called before `reduce`\n\n      When it is done, reduce will receive the result in\n      the `payload` parameter.\n\n      You can `dispatch` next steps from here as well\n    */\n  }\n};\n```\n\n- `reduce` is called using `Immer`, so mutate the `state` exactly as you would with the `draftState` parameter in [produce](https://immerjs.github.io/immer/docs/produce).\n\n- If you have some business to do before reducing, for example calling an API, use the `perform` function, either `sync` or `async`.\n\n  Then `reduce` will be called with the result once it's done.\n\n## 🎨 React integration\n\n\u003ca href=\"https://reactjs.org/docs/hooks-custom.html\"\u003e\u003cimg src=\"https://img.shields.io/badge/react-hooks-5908d2.svg\" alt=\"hooks\" /\u003e\u003c/a\u003e\n\n`La Taverne` has a context Provider `\u003cTaverne\u003e` which provides 2 utilities:\n\n- the `pour` hook to access your global state anywhere\n- the `dispatch` function\n\n```js\n/* src/app.js */\nimport React from 'react';\nimport {render} from 'react-dom';\nimport {Taverne} from 'taverne/hooks';\n\nrender(\n  \u003cTaverne dispatch={dispatch} taverne={taverne}\u003e\n    \u003cApp id={id} /\u003e\n  \u003c/Taverne\u003e,\n  container\n);\n```\n\n```js\n/* src/feature/books/container.js */\nimport {useTaverne} from 'taverne/hooks';\n\nconst BooksContainer = props =\u003e {\n  const {dispatch, pour} = useTaverne();\n  const books = pour('books');\n\n  return \u003cBooksComponent books={books} /\u003e;\n};\n```\n\nSee the complete React integration [steps here](docs/react.md).\n\nYou can \"pour\" specific parts of the \"taverne\", to allow [accurate local rendering](docs/react.md#-advanced-usage) from your global app state.\n\n## 🔆 Middlewares\n\nYou can create more generic middlewares to operate any actions.\n\nYour middlewares must implement `onDispatch: (action, dispatch, getState) =\u003e {}`\n\n```js\nconst customMiddleware = taverne =\u003e {\n  const instance = {\n    onDispatch: (action, dispatch, getState) =\u003e {}\n  };\n\n  return instance;\n};\n```\n\nThen instanciate `La Taverne` with your list of middlewares as 2nd parameter:\n\n```js\nconst {dispatch, taverne} = createLaTaverne(barrels, [customMiddleware]);\n```\n\nexample: plugging the [redux devtools extension](https://github.com/reduxjs/redux-devtools) with this [middleware](src/middlewares/devtools.js)\n\n## 🐛 Redux devtools\n\nUsing devtools with `La Taverne` provides debugging without losing performance:\n\n- gathered debounced actions\n- nested actions\n- optional state filtering to improve performance\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./docs/devtools.png\"\u003e\u003c/p\u003e\n\n```js\nimport createLaTaverne from 'taverne';\nimport {createDevtools} from 'taverne/middlewares';\nimport books from './barrels/books';\n\nconst devtools = createDevtools();\nconst {dispatch, taverne} = createLaTaverne({books}, [devtools]);\n```\n\nWhen your app state is too big, you'll hit performance issues with Redux dev tools.\n\nIn this case you may need to skip part of state from tracking;\n\n```js\nconst devtools = createDevtools({\n  applyStateFiltering? : state =\u003e ({\n    ...state,\n    hugeObject: '\u003cskipped\u003e'\n  })\n});\n```\n\n## 🏗️ development\n\n- 📓 Few local dev [notes](docs/dev.md) for the curious ones.\n- ✅ Issues and PR Welcomed!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Furalys%2Ftaverne","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Furalys%2Ftaverne","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Furalys%2Ftaverne/lists"}