{"id":16803232,"url":"https://github.com/beatfactor/repertoire","last_synced_at":"2025-10-09T06:06:29.169Z","repository":{"id":57353629,"uuid":"138226070","full_name":"beatfactor/repertoire","owner":"beatfactor","description":"A controller layer for React/Redux web apps","archived":false,"fork":false,"pushed_at":"2018-09-03T08:29:38.000Z","size":342,"stargazers_count":3,"open_issues_count":0,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-13T10:01:44.485Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/beatfactor.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}},"created_at":"2018-06-21T22:01:09.000Z","updated_at":"2020-02-29T06:55:06.000Z","dependencies_parsed_at":"2022-09-16T08:10:50.207Z","dependency_job_id":null,"html_url":"https://github.com/beatfactor/repertoire","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/beatfactor/repertoire","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatfactor%2Frepertoire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatfactor%2Frepertoire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatfactor%2Frepertoire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatfactor%2Frepertoire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beatfactor","download_url":"https://codeload.github.com/beatfactor/repertoire/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatfactor%2Frepertoire/sbom","scorecard":{"id":229647,"data":{"date":"2025-08-11","repo":{"name":"github.com/beatfactor/repertoire","commit":"ba7a94dbdf4432de15c668eda651179fdc4929ff"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"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":"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":"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":"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":"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":"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":"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":"Vulnerabilities","score":0,"reason":"84 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-c6rq-rjc2-86v2","Warn: Project is vulnerable to: GHSA-mh2h-6j8q-x246","Warn: Project is vulnerable to: GHSA-5q88-cjfq-g2mh","Warn: Project is vulnerable to: GHSA-xp63-6vf5-xf3v","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","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-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-f9cm-qmx5-m98h","Warn: Project is vulnerable to: GHSA-7wpw-2hjm-89gp","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-hxm2-r34f-qmc5","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-mvjj-gqq2-p4hw","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx","Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-662x-fhqg-9p8v","Warn: Project is vulnerable to: GHSA-394c-5j6w-4xmx","Warn: Project is vulnerable to: GHSA-78cj-fxph-m83p","Warn: Project is vulnerable to: GHSA-fhg7-m89q-25r3","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"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-17T04:37:18.869Z","repository_id":57353629,"created_at":"2025-08-17T04:37:18.869Z","updated_at":"2025-08-17T04:37:18.869Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000857,"owners_count":26082950,"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","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"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":[],"created_at":"2024-10-13T09:42:06.518Z","updated_at":"2025-10-09T06:06:29.154Z","avatar_url":"https://github.com/beatfactor.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# repertoire.js\n\n[![Build Status](https://travis-ci.org/beatfactor/repertoire.svg?branch=master)](https://travis-ci.org/beatfactor/repertoire) [![NPM version](https://badge.fury.io/js/repertoire.png)](http://badge.fury.io/js/repertoire) [![codecov](https://codecov.io/gh/beatfactor/repertoire/branch/master/graph/badge.svg)](https://codecov.io/gh/beatfactor/repertoire)\n\n***\n#### [Homepage](https://repertoire.gitbook.io/home) | [Using Repertoire](https://repertoire.gitbook.io/home/using-repertoire) | [API Reference](https://repertoire.gitbook.io/home/api-reference)\n\n# What's Repertoire\nA small utility library which aims at simplifying building React + Redux apps. \n\nIt works by simply adding the well-known Controller concept to a web application built with React as the view layer and removing the need of all the boiler plate code for actions, reducers, middlewares etc.\n\n# Installation\nRepertoire works together with React and Redux, so the following packages are needed as pre-requisites (peer dependencies) in your application:\n\n- react\n- redux\n- react-redux\n\nYou can install Repertoire from NPM, using:\n\n```sh\n$ npm install repertoire --save\n```\n\n# Anatomy of a Repertoire App\nBuilding an app with React \u0026 Redux has become much simpler. The example below is a basic user administration module, starting with the main component. We're also using the React Router to handle our application's routing needs.\n\n```\n├── modules/admin/\n|     ├── components/\n|     |      ├── UserAdd.js\n|     |      ├── UsersList.js\n|     |      └── SelectedUser.js\n|     ├── api.js\n|     ├── controller.js\n|     └── index.js\n└── index.js\n```\n\n### 1. index.js\nThis is the place where the Redux store is created and the app is being initialized.\n\n```jsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport {Provider} from 'react-redux'\nimport {\n  BrowserRouter as Router,\n  Route\n} from 'react-router-dom';\nimport {StoreManager} from 'repertoire'\n\nimport Admin from './modules/admin/index.js'\nimport Dashboard from './modules/dashboard/index.js'\n\nconst routes = [\n  {\n    path: '/',\n    exact: true,\n    controller: Dashboard\n  },\n  {\n    exact: true,\n    path: '/admin',\n    controller: Admin\n  }\n];\n\n// creating the main Redux store\nconst storeManager = new StoreManager(routes);\n\n// rendering the main component\nReactDOM.render(\u003cProvider store={storeManager.getStore()}\u003e\n  \u003cRouter\u003e\n    {routes.map((route, index) =\u003e {\n      return \u003cRoute key={index} \n                    path={route.path} \n                    exact={route.exact} \n                    component={controller.component}/\u003e\n    })}\n  \u003c/Router\u003e    \n\u003c/Provider\u003e, document.getElementById('react-view'));\n```\n\n### 2. The Admin Controller\nThe Controller is the main thing that Repertoire adds to your application's architecture. It does that by combining the individual redux pieces, such as reducers, actions and middlewares, together in one logical entity.\n\nEach public method on the controller that will be exposed on the instance will be a redux action, and each of them will have an implicit reducer associated by default.\n\nEvery action needs to return a Promise and the result of the promise will be added to the store. If an action returns a value synchronously, that value will be converted to a Promise automatically.\n\nThe controller is also the place where the Redux state properties are defined, which are passed to React as props. Use the `this.state` setter and getter to define the props to be passed to React or to inspect the current value of the Redux store.\n\n```jsx\nimport {BaseController} from 'repertoire'\nimport AdminApi from './api.js'\n\nexport default class AdminController extends BaseController {\n  // the section of the redux store which this controller will operate on\n  get stateNamespace() {\n    return 'admin';\n  }\n  \n  /**\n   *\n   * Methods that start with \"__\" are not processed as actions\n   *\n   * @param currentUser\n   * @private\n   */\n  __handleFetchUsers(currentUser) {}\n  \n  setSelectedUser (selectedUser) {\n    return {\n      selectedUser\n    };\n  }\n  \n  fetchAllUsers () {\n    return AdminApi.getUsers().then(result =\u003e ({users: result}));\n  }\n  \n  addNewUser (params) {\n    let addUserSuccess = false;\n    let lastThrownError = null;\n\n    return AdminApi.addNewUser(params)\n      .then(_ =\u003e {\n        addUserSuccess = true;\n\n        return AdminApi.getUsers();\n      })\n      .catch(error =\u003e {\n        lastThrownError = error;\n\n        // return the existing list of users if an error occurred\n        return this.state.users;\n      })\n      .then(users =\u003e ({\n        addUserSuccess,\n        lastThrownError,\n        users\n      }));\n  }\n  \n  constructor(component) {\n    super(component);\n    \n    this.state = {\n      /**\n       * Each function defined on this setter will received the namespaced \n       *  redux store value\n       */\n      users(store) {\n        return store.users || [];\n      },\n      \n      selectedUser(store) {\n        return store.selectedUser || '';\n      },\n      \n      addUserSuccess(store) {\n        return store.addUserSuccess || false;\n      }\n    };\n    \n    // Final step. This is calling the connect() utility from react-redux\n    this.connect();\n  }\n}\n```\n\nThe api.js file contains a bunch of methods which will fire HTTP requests to the backend and return a Promise. Anything that returns a Promise will work.\n\n### 3. The Admin React Component\nIn the main React component file we will have to instantiate the controller, passing the component itself,  and export that instance. Other than that it's standard react / redux stuff.\n\n```jsx\nimport React, {Component} from 'react';\nimport PropTypes from 'prop-types';\nimport Controller from './controller.js';\nimport UserList from './components/UsersList.js';\nimport UserAdd from './components/UserAdd.js';\nimport SelectedUser from './components/SelectedUser.js';\n\nclass Admin extends Component {\n  static propTypes = {\n    users: PropTypes.array.isRequired,\n    fetchUsers: PropTypes.func.isRequired\n  };\n\n  constructor(props) {\n    super(props);\n\n    this.state = {\n      // ...\n    };\n  }\n\n  componentWillMount() {\n    this.props.fetchUsers();\n  }\n\n  onUserCreateCancel(e) { /* ... */ }\n\n  onCreateUserSubmit(params) {  \n    this.props.addNewUser(params);\n  }\n\n  handleUserClick(e) {\n    const user = e.target.id;\n    if (user) {\n      this.props.setSelectedUser(user);\n    }\n  }\n\n  render() {\n    const {showUserCreateForm, addUserSuccess} = this.state;\n    const {users, selectedUser, lastThrownError} = this.props;\n    \n    return users.length \u003e 0 ? \u003cdiv\u003e\n      \u003cUserAdd showForm={showUserCreateForm}\n               onCancelAddUser={this.onUserCreateCancel.bind(this)}\n               onCreateUserSubmit={this.onCreateUserSubmit.bind(this)} /\u003e\n                       \n      \u003cUserList users={filteredUsers || users}\n                onClick={this.handleUserClick.bind(this)}\n                selectedUser={selectedUser}/\u003e\n\n      {\n        selectedUser ? \u003cSelectedUser users={users}\n                                     selectedUser={selectedUser} /\u003e : null\n      }\n    \u003c/div\u003e : null;\n  }\n}\n\nexport default new Controller(Admin);\n```\n\nThat's pretty much it - a very basic Repertoire example, not necessarily functional though. You'll need an html template and a web server of course, along with a webpack (or other package manager) build system, but we're not going to focus on that part here.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeatfactor%2Frepertoire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeatfactor%2Frepertoire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeatfactor%2Frepertoire/lists"}