{"id":18084719,"url":"https://github.com/coderofsalvation/lodash-fp-composition","last_synced_at":"2025-04-12T20:09:14.732Z","repository":{"id":143886932,"uuid":"102228505","full_name":"coderofsalvation/lodash-fp-composition","owner":"coderofsalvation","description":"supercharge lodash/fp with promise- and immutable support","archived":false,"fork":false,"pushed_at":"2020-05-28T19:15:53.000Z","size":36,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T14:21:26.914Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coderofsalvation.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"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},"funding":{"custom":"https://gumroad.com/l/hGYGh"}},"created_at":"2017-09-02T23:07:23.000Z","updated_at":"2022-04-20T03:44:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"9efcd196-1765-449d-bd94-d4abf6e6a12d","html_url":"https://github.com/coderofsalvation/lodash-fp-composition","commit_stats":{"total_commits":51,"total_committers":1,"mean_commits":51.0,"dds":0.0,"last_synced_commit":"ca7463028931ca7050c0199b1feed9ecbec2009f"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderofsalvation%2Flodash-fp-composition","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderofsalvation%2Flodash-fp-composition/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderofsalvation%2Flodash-fp-composition/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderofsalvation%2Flodash-fp-composition/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coderofsalvation","download_url":"https://codeload.github.com/coderofsalvation/lodash-fp-composition/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248539957,"owners_count":21121264,"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":[],"created_at":"2024-10-31T15:07:52.632Z","updated_at":"2025-04-12T20:09:14.706Z","avatar_url":"https://github.com/coderofsalvation.png","language":"JavaScript","readme":"keep the darkside away: supercharge **lodash/fp** with promise- and immutable support to make code more readable, maintainable \u0026 composable\n\n\u003ccenter\u003e\u003cimg src='https://media1.giphy.com/media/VZovxx7W7tbu8/giphy.gif' width=\"50%\"/\u003e\u003c/center\u003e\n\n\u003e Warning: Experimental\n\n## How does this library makes my code look?\n \n```\nvar hasPassword       = _.get('password')\nvar hasNoPassword     = _.negate( hasPassword )\nvar getUser           = opts =\u003e db.find({email:opts.email, password:opts.password})\nvar gotoCatch         = err =\u003e e =\u003e throw e // optionally you can log stuff here\nvar doAnalytics       = Promise.all([logUser, logAnalytics])\nvar notifyExpiryDate  = opts =\u003e return true         // mock\nvar userAlmostExpired = opts =\u003e return true         // mock\nvar updateLastLogin   = _.set('lastlogin', Date.now )\nvar error             = opts =\u003e err =\u003e return true // mock \nvar reply             = opts =\u003e req.send(opts)\n\nvar createUser        = opts =\u003e new Promise( (resolve, reject) =\u003e {\n                           opts.password = '1234'\n                           db.create(opts)\n                           .then( resolve )\n                           .catch( resolve )\n                        })\n\n\nvar loginUser         = _.flow() // create empty flow\n                         .then( error('no email') ).when( hasNoEmail    )\n                         .then( getUser           ).when( hasPassword   )\n                         .then( createUser        ).when( hasNoPassword )\n                         .then( doAnalytics       ).fork()\n                         .then( updateLastLogin   ).fork()\n                         .then( notifyExpiryDate  ).when( userAlmostExpired )\n                         .then( saveUser          )\n                         .then( doAnalytics       ).fork()\n\n\nvar loginUserAndReply = _.flow( loginUser, _.log, reply )\n                         .catch( opts =\u003e error =\u003e reply({...opts,error}) )\n```\n\n\u003e NOTE: `fork()` doesn't wait for the execution of that line. Its triggers parallel execution, therefore it will never break the flow (=desired)\n\nSummary:\n\n* looks easy\n* easy to maintain\n* no if/else-clutter\n* no early returns (pipeline certainty)\n* immutable\n* code is not tightly coupled to webrequest \n* no temporary variables \n\n## Because you don't want this\n\n```\nvar doAnalytics       = Promise.all([logUser, logAnalytics])\nvar getUser           = db.find({email:opts.email, password:opts.password})\nvar notifyExpiryDate  = opts =\u003e return true         // mock\nvar userAlmostExpired = opts =\u003e return true         // mock\nvar reply             = opts =\u003e req.send(opts)\n\nvar createUser        = opts =\u003e new Promise( (resolve, reject) =\u003e {\n                          opts.password = '1234'\n                          db.create(opts)\n                          .then( resolve )\n                          .catch( resolve )\n                        })\n\nvar loginUser = (opts) =\u003e new Promise( (resolve, reject) =\u003e {\n  if( !opts.email ){ \n    // log stuff here\n    return req.send({err:\"no email\"})\n  }\n  var user\n  var getOrCreateUser\n  if( opts.password ){ \n    getOrCreateUser = getUser\n  }else{\n    getOrCreateUser = createUser\n  }\n  getOrCreateUser(opts)\n  .then( (u) =\u003e {\n    user = u\n    doAnalytics.then( () =\u003e false ).catch( () =\u003e false ) // ugly parallel code\n  })\n  .then( () =\u003e {\n    user.lastlogin = Date.now()\n    // PROBLEM: user is now modified..so code below will process an updated userobject \n  })\n  .then( () =\u003e {\n    if( userAlmostExpired(user) ){  // will not work because of previous problem\n      notifyExpiryDate(user)        // even if it would work..\n                                    // this could throw an exception\n                                    // an skip code execution below\n    }\n  })\n  .then( () =\u003e {\n    doAnalytics.then( () =\u003e false ).catch( () =\u003e false ) // ugly parallel code\n  })\n  .then( () =\u003e saveUser(user) )\n  .then( () =\u003e reply(user) )\n  .catch( err =\u003e {\n    if( !user ) user = opts \n    reply({ err, ...user})  \n  })  \n\n})\n```\n\nIssues: \n\n* early returns \n* needs temporary variables\n* mutability issues\n* if/else-clutter\n* unexpected halting of .then()-pipelines\n* code is tightly coupled to webrequest \n\n\n## Philosophy\n\n1. functional programming in javascript has 2 categories: the good stuff..and there's the other stuff :)\n2. `_.flow` (=reversed compose) is great, and reduces the amount of temporary variables \n3. Promises are great building blocks for async flow-control (and can be extended)\n4. mixing functions \u0026 promises should be hasslefree (lodash + promise = not hasslefree)\n5. accept javascript, therefore accept and expect mutable objects\n\n| Usage | what this lib does | comment |\n|-|-|-|\n| `_.flow(... , ...)` | adds support for automic promise-resolving | without arguments, it creates an extended promise |\n| `_.flow().then( [Function] )`           | **always** forwards processed input to next function | reduces if/else statements |\n| `_.flow().then( [Function] ).fork()` | **dont wait** for the output, just forward unprocessed input to next function | immutable data FTW |\n| `_.flow().then( ... ).when( isValid )` | **always** forwards input, but processes if isValid({..}) is true | prevents need of inline promise-code and early returns |\n\n\u003e NOTE: optionally you can define your own clonefunction like `.fork(_.cloneDeep)` e.g. \n\n\u003cimg src=\"https://cdn.shopify.com/s/files/1/0257/1675/t/147/assets/banner_ive-joined.gif?12750917494953216175\"/\u003e\n\n## Usage \n\n| type | how |\n|-|-|\n| nodejs with lodash | `var _                 = require('lodash/fp')`\n| | `_.mixin( require('lodash-fp-composition')` |\n| nodejs without lodash | `var _ = require('lodash-fp-composition')` |\n| browser without lodash | `\u003cscript src='https://unpkg.com/lodash-fp-composition'\u003e\u003c/script\u003e`|\n|                        | `\u003cscript\u003eflow(...)\u003c/script\u003e\n| browser with lodash     | `\u003cscript src='https://unpkg.com/lodash'\u003e\u003c/script\u003e`|\n|                        | `\u003cscript src='https://unpkg.com/lodash-fp-composition'\u003e\u003c/script\u003e`|\n|                        | `\u003cscript\u003e_.flow(...)\u003c/script\u003e` |\n\n---\n\n# Function Reference\n\n## _.flow( promise_or_function, ... )\n \nImproved version of _.flow, which also supports automatic resolving of promises.\nNote: modifies output. \n \n\u003e example: a = _.flow( new Promise(.....), _.trigger(alert), Object.keys )\n           a({foo:1, bar:2})\n\n```\n  input:{foo:1,bar:2} ---\u003e  promise --+--\u003e Object.keys(input) ) \n                                      |\n                                      +--\u003e alert(input)\n  output:['foo','bar'] \n\n```\n\n## _.trigger(fn)\n \ntrigger simply executes a function OR promise, but forwards original input as output.\nthis comes in handy when you don't want to break a flow/chain\n \n\u003e example:\t_.flow( _.trigger( alert ), _.trigger(console.dir) )({foo:\"bar\"})\n \n## _.when(f, g)\n \nhipster if statement, only execute function g when function f does not return null/false/undefined\n \n\u003e example: _.when( _.isString, console.log )(\"foo\")\n \n## _.lensOver(path, fn)\n \nlens over allows i/o for a nested property\n \n\u003e example: var updateBar = _.flow( -\u003e 123, _.log )\n\t\t\t_.lensOver( \"foo.bar\", updateBar )({foo:{bar:0}})  // sets 'foo.bar' to 123 (and prints in console)\n \n## _.template_es6(es6_template)\n \nsimple es6 templates for in the browser\n \n\u003e example: _.template_es6('${foo}', {foo:\"bar\"})    // outputs 'bar'\n\n## _.prefix(prefix, fn)\n \nsimple way to prefix a function which outputs a string\n \n\u003e example: _.error = _.prefix(\"error: \", _.log)\n \n## _.postfix(postfix, fn)\n \nsimple way to postfix a function which outputs a string\n \n\u003e example: _.flow( _.get('.length'), _.prefix(\"items\", _.log) )([1, 2, 3])\n \n## _.log(str)\n \nsimple log function (which forwards input to output)\n \n\u003e example: _.flow( doFoo, _log, doBar )({input:\"foo\"})\n \n## _.error(str)\n \nsimple error function (which forwards input to output)\n \n\u003e example: _.when( !hasFoo, _.prefix(\"something went wrong:\", _error ) )({input:\"foo\"})\n \n## _.mapAsync(arr, done, cb)\n \ncalls cb(data, next) for each element in arr, and continues loop \nbased on next()-calls (last element propagates done()).\nPerfect to iterate over an array synchronously,  while performing async\noperations inbetween the elements.\n \n\u003e example:\t_.mapAsync([1, 2, 3], alert, (data, next) =\u003e next() )\n \n \n","funding_links":["https://gumroad.com/l/hGYGh"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoderofsalvation%2Flodash-fp-composition","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoderofsalvation%2Flodash-fp-composition","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoderofsalvation%2Flodash-fp-composition/lists"}