{"id":28764926,"url":"https://github.com/klen/redux-code","last_synced_at":"2026-04-27T17:33:43.280Z","repository":{"id":38848294,"uuid":"105148049","full_name":"klen/redux-code","owner":"klen","description":"Yet another creators library","archived":false,"fork":false,"pushed_at":"2023-03-07T07:52:16.000Z","size":1056,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-07-07T11:44:42.640Z","etag":null,"topics":["redux","redux-helpers","redux-thunk"],"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/klen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2017-09-28T12:55:06.000Z","updated_at":"2022-12-16T11:32:22.000Z","dependencies_parsed_at":"2024-11-11T10:34:25.700Z","dependency_job_id":"e360c2d0-18ab-4431-89dd-04eb664931bd","html_url":"https://github.com/klen/redux-code","commit_stats":{"total_commits":148,"total_committers":1,"mean_commits":148.0,"dds":0.0,"last_synced_commit":"2e7aaed9dffaf72ed9cfe916ffe22c9bf68846d6"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"purl":"pkg:github/klen/redux-code","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klen%2Fredux-code","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klen%2Fredux-code/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klen%2Fredux-code/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klen%2Fredux-code/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klen","download_url":"https://codeload.github.com/klen/redux-code/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klen%2Fredux-code/sbom","scorecard":{"id":563359,"data":{"date":"2025-08-11","repo":{"name":"github.com/klen/redux-code","commit":"2e7aaed9dffaf72ed9cfe916ffe22c9bf68846d6"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"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":"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":"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":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","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":"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":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/klen/redux-code/build.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/klen/redux-code/build.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/klen/redux-code/test.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/klen/redux-code/test.yml/develop?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 npmCommand dependencies pinned"],"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":"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":"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":"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.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md: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":"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":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'develop'"],"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":"Vulnerabilities","score":0,"reason":"18 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-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-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","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-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"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-20T14:16:58.143Z","repository_id":38848294,"created_at":"2025-08-20T14:16:58.143Z","updated_at":"2025-08-20T14:16:58.143Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32341455,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["redux","redux-helpers","redux-thunk"],"created_at":"2025-06-17T10:12:56.474Z","updated_at":"2026-04-27T17:33:43.250Z","avatar_url":"https://github.com/klen.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-code\n\n[![Tests](https://github.com/klen/redux-code/actions/workflows/test.yml/badge.svg)](https://github.com/klen/redux-code/actions/workflows/test.yml)\n[![Build](https://github.com/klen/redux-code/actions/workflows/build.yml/badge.svg)](https://github.com/klen/redux-code/actions/workflows/build.yml)\n[![npm version](https://badge.fury.io/js/redux-code.svg)](https://badge.fury.io/js/redux-code)\n\nA simple set of tools to make using Redux easier\n\nFeatures:\n\n* [createReducer](#createreducer) -- A utility to create reducers (with immer support)\n* [createActions](#createactions) -- A utility to create actions (with thunk, promises support)\n* [skipMiddleware](#skipmiddleware) -- A Redux middleware to add ability to skip actions\n* [persistReducer, persistStore](#persistReducer) -- Make state persistent\n\n## Installation\n\n```bash\nnpm install --save redux-code\n```\n\n## Usage\n\n### createActions\n\nA helper to build actions creator.\n\n```javascript\nimport {createActions} from 'redux-code'\n\nconst actions = createActions('prefix/', {\n\n    // Basic action creator without payload\n    action1: undefined,\n\n    // Basic action creator with static payload\n    action2: true,\n\n    // Basic action creator with dynamic payload\n    action3: (num) =\u003e num,\n\n    // You may return other actions as well\n    random: () =\u003e (Math.random() \u003c 0.5 ? actions.action1() : actions.action2()),\n})\n\n// Call creators to build actions\nexpect(actions.action1()).toEqual({ type: 'prefix/action1' })\nexpect(actions.action2()).toEqual({ type: 'prefix/action2', payload: true })\nexpect(actions.action3(42)).toEqual({ type: 'prefix/action3', payload: 42 })\nexpect([actions.action1(), actions.action2()]).toContainEqual(actions.random())\n\n// Get action types\nexpect(actions.action1.type).toBe('prefix/action1')\nexpect(actions.action2.type).toBe('prefix/action2')\nexpect(actions.action3.type).toBe('prefix/action3')\n\n```\n\nYou may also pass an array of strings to create simple actions\n\n```javascript\nconst actions = createActions('prefix/', ['init', 'update'])\n\nexpect(actions.init()).toEqual({type: 'prefix/init'})\nexpect(actions.update(42)).toEqual({type: 'prefix/update', payload: 42})\n\n```\n\n#### Promises and thunks\n\n`Redux-Code` supports `redux-thunk` actions as well as async actions.\n\n```javascript\nimport {SKIP} from 'redux-code'\n\nconst actions = createActions('thunk:', {\n    \n    // Just an update action\n    update: (payload) =\u003e payload,\n\n    // Use redux-thunk (return function)\n    init: () =\u003e (dispatch, getState) =\u003e {\n        const state = getState()\n        if (!state.inited) dispatch(actions.update({ inited: true }))\n    },\n\n    // Promises also supported\n    load: async () =\u003e {\n        // Just pretend we have an IO call here\n        const res = await Promise.resolve(Math.random() * 100 + 1)\n        return res\n    }\n\n})\n\nstore.dispatch(actions.init())\nawait store.dispatch(actions.load())\n```\n\n#### Mixins\n\nYou may create and use mixins with different actions.\n\n```javascript\nconst UpdateMixin = {\n    update: (payload) =\u003e payload\n}\n\nconst DisableMixin = {\n    // Pay attention that we don't use arrow function here to allow redux-code bind this to a created actions\n    disable: async function(dispatch) {\n        this.update({disabled: true})\n        await self.save()\n    }\n}\nconst usersActions = createActions('users/', UpdateMixin, DisableMixin, {\n    save: async () =\u003e {\n        // ...\n    }\n})\nconst commentsActions = createActions('comments/', UpdateMixin, DisableMixin, ['another1', 'another2'], {\n    save: async () =\u003e {\n        // ...\n    }\n})\n\nexpect(usersActions.disable.type).toEqual('users/disable')\nexpect(commentsActions.disable.type).toEqual('comments/disable')\n```\n\n### createReducer\n\n#### Basic Usage\n\nSimplifying Reducers with `createReducer`:\n\n```javascript\nimport { createReducer } from 'redux-code'\n\nconst initial = 42\n\n// instead this\nconst oldReducer(state=initial, action) {\n    switch(action.type) {\n        case 'increment':\n            return state + 1\n        case 'decrement':\n            return state - 1\n        default:\n            return state\n    }\n}\n\n// Write this\nconst newReducer = createReducer(initial, {\n    // Map actions types to handlers\n    increment: (state, action) =\u003e state + 1,\n    decrement: (state, action) =\u003e state - 1,\n})\n\n```\n\nInternally `createReducer` uses [Immer](https://github.com/mweststrate/immer)\nlibrary, which lets you write code that \"mutates\" some data, but actually\napplies the updates immutably.\n\n##### Create reducer without immer\n\nThe library provides method `createBaseReducer` which one doesn't use `immer`\ninternally.\n```javascript\nimport { createBaseReducer } from 'redux-code'\n\nconst initial = 42\n\nconst reducer = createBaseReducer(initial, {\n    // Map actions types to handlers\n    increment: (state, action) =\u003e state + 1,\n    decrement: (state, action) =\u003e state - 1,\n})\n\n```\n\n#### Using with `createActions`\n\nSure you may use `createActions` with the `createReducer` together\n\n```javascript\n    const actions = createActions('counter/', { increment: undefined, decrement: undefined })\n\n    const reducer = createReducer(initial, {\n      [actions.increment]: (state, action) =\u003e state + 1,\n\n      // For typescript use `action.type`\n      [actions.decrement.type]: (state, action) =\u003e state - 1,\n    })\n```\n\n#### Mixins\n\nYou may create and use mixins with your reducers.\n\n```javascript\n\n// Common Reducer, supports reset (reset state) and update (update state with the given payload) actions\nconst CommonMixinCreator = (initial, actions) =\u003e ({\n    [actions.reset.type]: (state) =\u003e initial,\n    [actions.update.type]: (state, { payload }) =\u003e ({ ...state, ...payload }),\n})\n\nconst actions = createActions('users/', {\n    reset: true,\n    update: (payload) =\u003e payload,\n    load: () =\u003e {\n        // ...\n    }\n})\n\nconst initial = {}\n\n// Create a reducer with your mixin\nconst reducer = createReducer(initial, CommonMixinCreator(initial, actions), {\n    // Only define new actions\n    [actions.load.type]: (state) =\u003e ({ ...state, loaded: Date.now() }),\n})\n\nlet state = reducer(initial, actions.update({ data: 42 }))\nexpect(state).toEqual({ data: 42 })\nstate = reducer(state, actions.reset())\nexpect(state).toBe(initial)\n\n```\n\n### skipMiddleware\n\nYou may use `skipMiddleware` with `SKIP` action to (surprise) skip some actions.\nJust return SKIP from actions creators every where you want to skip.\n\n```javascript\nimport { SKIP, skipMiddleware } from 'redux-code'\n\n// .. Add skipMiddleware to your store\n\nconst initial = {}\n\nconst actions = createActions('example/', {\n\n    // The actions is skipable by an condition\n    conditionalAction: () =\u003e {\n        // Skip the action\n        if (Math.round() \u003c 0.5) return SKIP\n        // Or do it as well\n        return true\n    }\n\n})\n```\n\n### Mixins\n\nThe package includes some mixins for actions and reducers\n\n#### Common\n```javascript\nimport {commonActions, commonReducer} from 'redux-code'\n\nconst actions = createActions('example/', commonActions, {\n    // .. your other action creators here\n})\nconst reducer = createReducer(initial, commonReducer(actions, initial), {\n    // .. your other actions handlers here\n})\n\n// commonActions, commonReducer supports such actions as:\n// * reset -- to reset your reducer state\n// actions.reset()\n\n// * update -- to update your reducer state from the provided payload\n// actions.update({'updates': 'here'})\n```\n\n#### Entities\n```javascript\nimport {entitiesActions, entitiesReducer} from 'redux-code'\n\nconst actions = createActions('base/', entitiesActions, {\n    // .. your other action creators here\n})\nconst reducer = createReducer(initial, entitiesReducer(actions), {\n    // .. your other actions handlers here\n})\n\n// entitiesActions supports such actions as:\n// * addOne -- to add an entity\n// * addMany -- to add many entities\n// * updateOne -- to update an entity\n// * updateMany -- to update many entities\n// * upsertOne -- to add/update an entity\n// * upsertMany -- to add/update many entities\n// * setOne -- to add/set an entity\n// * setMany -- to add/set many entities\n// * setAll -- to replace all entities\n// * removeOne -- to remove an entity\n// * removeMany -- to remove many entities\n// * removeAll -- to remove all entities\n\n```\n\n### persistReducer\n\n```javascript\nimport {commonActions, commonReducer, persistReducer, persistStore} from 'redux-code'\nimport {localStorage} from 'redux-core/persist'\n\nconst actions = createActions('example/', commonActions, {\n    // .. your other action creators here\n})\nconst reducer = persistReducer(\n    {key: 'storeKey', storage: localStorage},\n    createReducer(initial, commonReducer(actions, initial), {\n        // .. your other actions handlers here\n    }) \n)\n\nconst store = createStore(reducer, {})\nconst persistor = persistStore(store)\n\n// State will be persisted in localStorage\nstore.dispatch(actions.update({value: 42}))\n\n// Pause persistence for the storeKey\npersistor.pause()\n\n// Resume persistence for the storeKey\npersistor.persist()\n\n// Purge data\npersistor.purge()\n```\n\n## License\n\nThis project is licensed under the MIT license, Copyright (c) 2017 Kirill Klenov. For more information see `LICENSE.md`.\n\n## Acknowledgements\n\n[Dan Abramov](https://github.com/gaearon) for Redux\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklen%2Fredux-code","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklen%2Fredux-code","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklen%2Fredux-code/lists"}