{"id":18552456,"url":"https://github.com/andrejewski/raj-compose","last_synced_at":"2025-06-12T06:10:40.754Z","repository":{"id":57332554,"uuid":"101514884","full_name":"andrejewski/raj-compose","owner":"andrejewski","description":"Program composition for Raj","archived":false,"fork":false,"pushed_at":"2020-05-11T17:21:46.000Z","size":105,"stargazers_count":7,"open_issues_count":4,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-04T17:10:28.960Z","etag":null,"topics":["composition","effects","programs"],"latest_commit_sha":null,"homepage":null,"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/andrejewski.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":"2017-08-26T21:43:53.000Z","updated_at":"2021-10-06T13:55:58.000Z","dependencies_parsed_at":"2022-09-21T04:41:04.480Z","dependency_job_id":null,"html_url":"https://github.com/andrejewski/raj-compose","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/andrejewski/raj-compose","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Fraj-compose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Fraj-compose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Fraj-compose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Fraj-compose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrejewski","download_url":"https://codeload.github.com/andrejewski/raj-compose/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Fraj-compose/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259410149,"owners_count":22852970,"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":["composition","effects","programs"],"created_at":"2024-11-06T21:14:16.187Z","updated_at":"2025-06-12T06:10:40.727Z","avatar_url":"https://github.com/andrejewski.png","language":"JavaScript","readme":"# Raj Compose\n\u003e Program composition for [Raj](https://github.com/andrejewski/raj)\n\n```sh\nnpm install raj-compose\n```\n\n[![npm](https://img.shields.io/npm/v/raj-compose.svg)](https://www.npmjs.com/package/raj-compose)\n[![Build Status](https://travis-ci.org/andrejewski/raj-compose.svg?branch=master)](https://travis-ci.org/andrejewski/raj-compose)\n[![Greenkeeper badge](https://badges.greenkeeper.io/andrejewski/raj-compose.svg)](https://greenkeeper.io/)\n\nThe `raj-compose` package contains utilities to reduce the boilerplate of\nbuilding up large applications from small programs.\n\n## Documentation\nThe package contains the following utilities:\n\n- [`mapEffect(effect, callback)`](#mapeffect)\n- [`batchEffects(effects)`](#batcheffects)\n- [`mapProgram(program, callback)`](#mapprogram)\n- [`batchPrograms(programs, containerView)`](#batchprograms)\n- [`assembleProgram({ data, view, logic, deps, options })`](#assembleprogram)\n\n### `mapEffect`\n\n\u003e `mapEffect(effect: function?, callback(any): any): function?`\n\nThe `mapEffect` function \"lifts\" a given `effect` so that `callback` transforms\n  each message produced by that effect before dispatch.\n\nThe `mapEffect` function accepts a `effect` function or a falsy value and a\n  `callback` function.\nIf the `effect` is truthy but not a function, an error will throw.\nIf the `callback` is not a function, an error will throw.\nThe `mapEffect` returns either the falsy `effect` value or a new effect function.\n\n#### Example\nWe want to distinguish the messages dispatched by an effect.\nWe use `mapEffect` to wrap each message in an \"important\" wrapper.\n\n```js\nimport assert from 'assert'\nimport { mapEffect } from 'raj-compose'\n\nconst effect = dispatch =\u003e {\n  dispatch('Hello')\n  dispatch('World')\n}\n\nconst importantEffect = mapEffect(effect, message =\u003e ({\n  type: 'important',\n  value: message\n}))\n\nconst messages = []\nimportantEffect(message =\u003e {\n  messages.push(message)\n})\n\nassert.deepEqual(messages, [\n  { type: 'important', value: 'Hello' },\n  { type: 'important', value: 'World' }\n])\n```\n\n### `batchEffects`\n\n\u003e `batchEffects(effects: Array\u003cfunction?\u003e): function`\n\nThe `batchEffects` function takes an array of `effects` and returns a new\n  function which will call each effect.\nIf an effect is truthy but not a function, an error will throw.\n\n#### Example\nWe have two effects we want to run together.\nWe use `batchEffects` to combine them into a single effect.\n\n```js\nimport assert from 'assert'\nimport { batchEffects } from 'raj-compose'\n\nconst one = dispatch =\u003e dispatch('Hello')\nconst two = dispatch =\u003e dispatch('World')\nconst all = batchEffects([one, two])\n\nconst messages = []\nall(message =\u003e {\n  messages.push(message)\n})\n\nassert.deepEqual(messages, [\n  'Hello',\n  'World'\n])\n```\n\n### `mapProgram`\n\n\u003e `mapProgram(program: RajProgram, callback): RajProgram`\n\nLike `mapEffect`, `mapProgram` \"lifts\" all messages from `program` with the  `callback` function and returns a new program.\nIf `program` is not shaped like a Raj program, an error will throw.\nIf `callback` is not a function, an error will throw.\n\nThe `mapProgram` function:\n- transforms the `program.init` optional effect messages with `callback`\n- transforms each `program.update()` optional effect messages with `callback`\n- transforms each `program.view()` dispatched message with `callback`\n\nThis function encapsulates all program messages for its parent program to pass down to the child.\n\nThe new program's `update()` parameters are the same as the original's.\nThe `view()` `state` is the child program's state and `dispatch` is the parent's dispatch function.\n\n### `batchPrograms`\n\n\u003e `batchPrograms(programs: Array\u003cRajProgram\u003e, containerView: function): RajProgram`\n\nThe `batchPrograms` function takes an array of `programs` and a `containerView` function and creates a new program which manages those child programs.\nIf any item in the `programs` array is not a program, an error will throw.\nIf `containerView` is not a function, an error will throw.\n\nThe `containerView` receives an array of functions which return views for each respective program.\n\n#### Example\nWe have a main view and a sidebar view that we would like to display at the same time. We are using React as our view layer so we are using JSX to describe our HTML. We use the `batchPrograms` to unite the two programs in the same app view.\n\n```js\nimport React from 'react'\nimport { batchPrograms } from 'raj-compose'\nimport mainProgram from './main'\nimport sidebarProgram from './sidebar'\n\nexport default batchPrograms(\n  [mainProgram, sidebarProgram],\n  ([mainView, sideView]) =\u003e (\n    \u003cdiv id='app'\u003e\n      \u003cdiv id='side'\u003e{sideView()}\u003c/div\u003e\n      \u003cdiv id='main'\u003e{mainView()}\u003c/div\u003e\n    \u003c/div\u003e\n  )\n)\n```\n\n### `assembleProgram`\n\n\u003e `assembleProgram({data, view, logic, dataOptions, viewOptions, logicOptions}): RajProgram`\n\nThe `assembleProgram` function takes three functions:\n\n- `data(dataOptions)`\n- `view(model, dispatch, viewOptions)`\n- `logic(data(dataOptions), logicOptions)`\n\nThe `dataOptions`, `viewOptions`, `logicOptions`, and return value of `data` can be anything that makes sense to the program.\nThis will return a program where `logic()` would return an object containing `{init, update, done, ...}` properties that merge in with `view`.\n\nThis function is good for separating the concerns common to most programs: data-fetching, views, and business logic.\nThe assemble pattern is useful as each function can be tested in isolation.\nStructuring programs in this manner is recommended when data-fetching is involved.\n\n```js\nimport { assembleProgram } from 'raj-compose'\n\nexport const data = ({ httpClient }) =\u003e ({\n  getPosts (dispatch) {\n    httpClient.get('/posts')\n      .then(data =\u003e dispatch({ data }))\n      .catch(error =\u003e dispatch({ error }))\n  }\n})\n\nexport function view (model, dispatch, options) {\n  // show a list of posts\n}\n\nexport function logic (data, options) {\n  const init = [\n    {\n      posts: [],\n      loadError: null\n    },\n    data.getPosts\n  ]\n\n  function update (msg, model) {\n    if (msg.error) {\n      return [{ ...model, loadError: model.error }]\n    } else {\n      return [{ ...model, posts: msg.data.posts }]\n    }\n  }\n\n  return { init, update }\n}\n\nexport function makeProgram (httpClient) {\n  return assembleProgram({\n    data,\n    view,\n    logic,\n    dataOptions: { httpClient }\n  })\n}\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrejewski%2Fraj-compose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrejewski%2Fraj-compose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrejewski%2Fraj-compose/lists"}