{"id":15415840,"url":"https://github.com/davidje13/shared-reducer-frontend","last_synced_at":"2025-10-11T08:30:53.244Z","repository":{"id":57358006,"uuid":"259147358","full_name":"davidje13/shared-reducer-frontend","owner":"davidje13","description":"Shared state management via websockets. This has moved to https://github.com/davidje13/shared-reducer","archived":true,"fork":false,"pushed_at":"2024-09-27T21:53:01.000Z","size":158,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-09T15:55:55.773Z","etag":null,"topics":["reducer","websocket"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":false,"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/davidje13.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":"2020-04-26T22:34:15.000Z","updated_at":"2024-09-27T21:54:26.000Z","dependencies_parsed_at":"2025-01-28T18:42:45.715Z","dependency_job_id":null,"html_url":"https://github.com/davidje13/shared-reducer-frontend","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/davidje13/shared-reducer-frontend","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Fshared-reducer-frontend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Fshared-reducer-frontend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Fshared-reducer-frontend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Fshared-reducer-frontend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidje13","download_url":"https://codeload.github.com/davidje13/shared-reducer-frontend/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidje13%2Fshared-reducer-frontend/sbom","scorecard":{"id":326917,"data":{"date":"2025-08-11","repo":{"name":"github.com/davidje13/shared-reducer-frontend","commit":"39d435acab0675f6849a03e0097aef2163a4fa5c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"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":"Code-Review","score":0,"reason":"Found 0/27 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":"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":"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":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"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":"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":"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":"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":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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"}}]},"last_synced_at":"2025-08-18T02:40:45.713Z","repository_id":57358006,"created_at":"2025-08-18T02:40:45.713Z","updated_at":"2025-08-18T02:40:45.713Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006611,"owners_count":26084148,"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-11T02:00:06.511Z","response_time":55,"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":["reducer","websocket"],"created_at":"2024-10-01T17:09:56.682Z","updated_at":"2025-10-11T08:30:52.938Z","avatar_url":"https://github.com/davidje13.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shared Reducer Frontend\n\n**This project has moved**\n\nThe new project is `shared-reducer`, and can be found on\n[GitHub](https://github.com/davidje13/shared-reducer) and\n[GitLab](https://gitlab.com/davidje13/shared-reducer)\n\n---\n\nShared state management via websockets.\n\nDesigned to work with\n[shared-reducer-backend](https://github.com/davidje13/shared-reducer-backend)\nand\n[json-immutability-helper](https://github.com/davidje13/json-immutability-helper).\n\n## Install dependency\n\n```bash\nnpm install --save shared-reducer-frontend json-immutability-helper\n```\n\n(if you want to use an alternative reducer, see the instructions below).\n\nWhen using this with `shared-reducer-backend`, ensure both dependencies are\nat the same major version (e.g. both are `2.x` or both are `3.x`). The API\nmay change between major versions.\n\n## Usage\n\n```javascript\nimport SharedReducer, { actionsHandledCallback, actionsSyncedCallback } from 'shared-reducer-frontend';\nimport context from 'json-immutability-helper';\n\nconst reducer = SharedReducer\n  .for('ws://destination', (state) =\u003e {\n    console.log('latest state is', state);\n  })\n  .withReducer(context)\n  .withToken('my-token')\n  .withErrorHandler((error) =\u003e { console.log('connection lost', error); })\n  .withWarningHandler((warning) =\u003e { console.log('latest change failed', warning); })\n  .build();\n\nconst dispatch = reducer.dispatch;\n\ndispatch([\n  { a: ['=', 8] },\n]);\n\ndispatch([\n  (state) =\u003e {\n    return {\n      a: ['=', Math.pow(2, state.a)],\n    };\n  },\n]);\n\ndispatch([\n  actionsHandledCallback((state) =\u003e {\n    console.log('state after handling is', state);\n  }),\n]);\n\ndispatch([\n  actionsSyncedCallback((state) =\u003e {\n    console.log('state after syncing is', state);\n  }),\n]);\n\ndispatch([\n  { a: ['add', 1] },\n  { a: ['add', 1] },\n]);\n```\n\n### Specs\n\nThe specs need to match whichever reducer you are using. In the examples\nabove, that is\n[json-immutability-helper](https://github.com/davidje13/json-immutability-helper).\n\n## WebSocket protocol\n\nThe websocket protocol is minimal:\n\n### Messages sent\n\n`\u003ctoken\u003e`:\nThe authentication token is sent as the first message when the connection is\nestablished. This is plaintext. The server should respond by either terminating\nthe connection (if the token is deemed invalid), or with an `init` event which\ndefines the latest state in its entirety. If no token is specified using\n`withToken`, no message will be sent (when not using authentication, it is\nassumed the server will send the `init` event unprompted).\n\n`P` (ping):\nSent periodically to keep the connection alive. Expects to receive a \"Pong\"\nmessage in response.\n\n`{\"change\": \u003cspec\u003e, \"id\": \u003cid\u003e}`:\nDefines a delta. This may contain the aggregate result of many operations\nperformed on the client. The ID should be considered an opaque number which\nshould be reflected back to the same client in the confirmation message.\n\n### Messages received\n\n`p` (pong):\nReponse to a ping. The server may also decide to send this unsolicited.\n\n`{\"init\": \u003cstate\u003e}`:\nThis should be the first message sent by the server, in response to a\nsuccessful authentication.\n\n`{\"change\": \u003cspec\u003e}`:\nThis should be sent whenever another client has changed the server state.\n\n`{\"change\": \u003cspec\u003e, \"id\": \u003cid\u003e}`:\nThis should be sent whenever the current client has changed the server\nstate. Note that the spec and ID should match the client-sent values.\n\nThe IDs sent by different clients can coincide, so ensure the ID is only\nreflected to the client which sent the spec.\n\n`{\"error\": \u003cmessage\u003e, \"id\": \u003cid\u003e}`:\nThis should be sent if the server rejects a client-initiated change.\n\nThe ID should match the client-sent value.\n\nIt is assumed that if this is returned, the server state has not changed (i.e.\nthe entire spec failed).\n\n## Alternative reducer\n\nTo enable different features of `json-immutability-helper`, you can\ncustomise it before passing it to `withReducer`. For example, to\nenable list commands such as `updateWhere` and mathematical commands\nsuch as Reverse Polish Notation (`rpn`):\n\n```js\nimport SharedReducer from 'shared-reducer-frontend';\nimport listCommands from 'json-immutability-helper/commands/list';\nimport mathCommands from 'json-immutability-helper/commands/math';\nimport context from 'json-immutability-helper';\n\nconst reducer = SharedReducer\n  .for('ws://destination', (state) =\u003e {})\n  .withReducer(context.with(listCommands, mathCommands))\n  .build();\n```\n\nIf you want to use an entirely different reducer, create a wrapper\nand pass it to `withReducer`:\n\n```js\nimport SharedReducer from 'shared-reducer-frontend';\nimport context from 'json-immutability-helper';\n\nconst myReducer = {\n  update: (value, spec) =\u003e {\n    // return a new value which is the result of applying\n    // the given spec to the given value (or throw an error)\n  },\n  combine: (specs) =\u003e {\n    // return a new spec which is equivalent to applying\n    // all the given specs in order\n  },\n};\n\nconst reducer = SharedReducer\n  .for('ws://destination', (state) =\u003e {})\n  .withReducer(myReducer)\n  .build();\n```\n\nBe careful when using your own reducer to avoid introducing\nsecurity vulnerabilities; the functions will be called with\nuntrusted input, so should be careful to avoid attacks such\nas code injection or prototype pollution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidje13%2Fshared-reducer-frontend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidje13%2Fshared-reducer-frontend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidje13%2Fshared-reducer-frontend/lists"}