{"id":22505395,"url":"https://github.com/azer/declarative-js","last_synced_at":"2026-03-05T01:34:19.939Z","repository":{"id":7806682,"uuid":"9177199","full_name":"azer/declarative-js","owner":"azer","description":"Declarative async programming with JavaScript ","archived":false,"fork":false,"pushed_at":"2014-02-26T09:43:30.000Z","size":169,"stargazers_count":49,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-03T11:53:27.237Z","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/azer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-04-02T18:43:41.000Z","updated_at":"2025-04-26T07:23:55.000Z","dependencies_parsed_at":"2022-09-22T06:42:39.789Z","dependency_job_id":null,"html_url":"https://github.com/azer/declarative-js","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/azer/declarative-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azer%2Fdeclarative-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azer%2Fdeclarative-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azer%2Fdeclarative-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azer%2Fdeclarative-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/azer","download_url":"https://codeload.github.com/azer/declarative-js/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azer%2Fdeclarative-js/sbom","scorecard":{"id":221083,"data":{"date":"2025-08-11","repo":{"name":"github.com/azer/declarative-js","commit":"c00257200fa7b2cc32a83e2bae2fc19cf39c024b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/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":"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":"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":"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":"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":"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":"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":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"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":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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T02:29:57.833Z","repository_id":7806682,"created_at":"2025-08-17T02:29:57.834Z","updated_at":"2025-08-17T02:29:57.834Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30104574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T01:06:53.091Z","status":"ssl_error","status_checked_at":"2026-03-05T01:02:35.679Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-12-07T00:18:13.425Z","updated_at":"2026-03-05T01:34:14.928Z","avatar_url":"https://github.com/azer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Declarative Async JavaScript\n\nThis repository explains how to benefit from\n[tiny](http://npm.im/new-partial)\n[functional](http://npm.im/join-params)\n[programming](http://npm.im/comp)\n[modules](http://npm.im/and-then) to\nhave your async JavaScript more declarative and readable.\n\n## Index\n\n* [Install \u0026 Run the Code Example](#install)\n* [First Steps](#first-steps)\n* [Fetching Data](#fetching-data)\n* [Defining Async Values](#async-values)\n* [Defining Async Lists](#async-lists)\n* [Combining Values](#combining-values)\n* [Final Value: allProfiles](#final-value)\n* [See Also](#see-also)\n\n\u003ca name=\"install\"\u003e \u003c/a\u003e\n### Install \u0026 Run the Code Example\n\nThe code example I'll be explaining is available as\n[example.js](https://github.com/azer/declarative-js/blob/master/example.js)\non this repository. You can run it as follows;\n\n```bash\n$ git clone https://github.com/azer/declarative-js.git\n$ cd declarative-js\n$ npm install\n$ node example\n```\n\n\u003ca name=\"first-steps\"\u003e\u003c/a\u003e\n### First Steps\n\nThe goal of the example code here will be defining **one value** that gives us all the user profiles, like the below piece of code;\n\n```js\nallProfiles = andThen(userIds, profiles) // function composition of `userIds` and `profiles`\n```\n\nFrom an API with following end-points:\n\n```\n=\u003e /users.json [3, 7, 19, 23, 27]\n=\u003e /users/7.json { id: 7, name: 'Smith', age: 21, posts: [3, 11, 12], photos: [19, 23, 39] }\n=\u003e /posts/11.json { id: 11, title: \"Hello World\", content: \"lorem ipsum sit dolor amet\" }\n=\u003e /photos/19.json { id: 19, path: \"/photos/19.jpg\" }\n```\n\n\u003ca name=\"fetching-data\"\u003e\u003c/a\u003e\n### Fetching Data\n\nWhat do we need first; a function to query the JSON API?\n\n```js\nvar getJSON = require('get-json');\n```\n\nAt the first step, we need the full list of users, and will send a request to `/users.json`.\nIt's a simple one, we can simply do partial application on `getJSON`:\n\n```js\nvar partial = require('new-partial');\n\nvar userIds = partial(getJSON, '/users.json');\n```\n\nThe `userIds` above is a new function. Once you call it, it'll fetch `/users.json` for you;\n\n```js\nuserIds(function(error, userIds){\n\n        userIds\n        // =\u003e [3, 7, 19, 23, 27]\n\n})\n```\n\nBut we won't need to call it actually. Only the definition of it is needed for us.\n\n\u003ca name=\"async-values\"\u003e\u003c/a\u003e\n### Defining Async Values\n\nThe definition of an async value here is the partial application of any async function, just like the `userIds` we\ndefined above.\n\nAs the next step, we'll be defining `user` to get user data from the API. This will be a partial application, \ntoo, except that we need to format the first parameter this time;\n\n```js\nvar joinParams = require('join-params');\n\nvar user = joinParams(getJSON, \"/users/{0}.json\")\n```\n\n[join-params](http://npm.im/join-params) is a fork of [new-partial](http://npm.im/new-partial)\nthat lets you join the parameters in a single, formatted parameter. See its docs for details.\n\n\u003ca name=\"async-lists\"\u003e\u003c/a\u003e\n### Defining Async Lists\n\nDefining a list of async value means creating a new partial application of\n[map](http://github.com/azer/map.js) with the async value as first parameter.\nThis abstraction will let us fetch a group of users at once;\n\n```js\nvar map = require('map');\n\nvar users = partial(map, user);\n```\n\nAt this step, we have user ids and a collection that implements a group of users. All we need is to combine these two together, using\na function composition library, [comp](http://npm.im/comp):\n\n```js\nvar comp = require('comp');\n\nvar allUsers = comp(userIds, users);\n\nallUsers(function(error, allUsers){\n\n        allUses[0].name, allUsers[2].age\n        // =\u003e Smith, 23\n\n})\n\n```\n\nAnother example, partial application of `users` ?\n\n```js\nvar adminIds = [3, 7, 9];\nvar admins = partial(users, adminIds);\n\nadmins(function(error, admins){\n\n        admins[0].name, admins[1].age, admins[1].photos\n        // =\u003e \"Smith\", 21, [7, 13, 37, 43]\n\n})\n```\n\nUntil this point, we're able to get all users with their data excluding posts and photos, since they require\nseparate API calls.\n\nLet's define `posts` and `photos`, as well.\n\n```js\nvar post   = joinParams(getJSON, \"/posts/{0}.json\"),\n    posts  = partial(map, post),\n\n    photo  = joinParams(getJSON, post),\n    photos = partial(map, photo);\n```\n\nNow we can combine these together and define a value that has everything (posts, photos) about a `user`.\n\n\u003ca name=\"combining-values\"\u003e\u003c/a\u003e\n### Combining Values Together\n\nI'll call the new value `profile`. And we'll use a function composition library called [andthen](http://npm.im/andthen)\nto combine `user`, `posts` and `photos` values:\n\n```js\nvar andThen = require('andthen');\n\nvar profile = andThen(user, '.posts', posts, '.photos', photos);\n```\n\nandthen is a fork of `comp` that allows you to bind new values to properties of previous values.\nThe code above will fetch user and pass the `posts` property to `getPosts`, and replace the same property with what\n`getPosts` returns. Same as `photos`.\n\nLet's define the plural define of it.\n\n```js\nvar profiles     = partial(map, profile);\nvar allProfiles  = comp(getUserIds, profiles);\n```\n\n\u003ca name=\"final-value\"\u003e\u003c/a\u003e\n### Final Value: `allProfiles`\n\nNow we have everything we need. Let's show the output:\n\n```js\nallProfiles(function(error, profiles){\n\n    if(error) throw error;\n\n    profiles.forEach(function(profile){\n\n            profile.name, profile.age\n            // =\u003e Smith, 21\n\n            profile.photos[0].path\n            // =\u003e  \"http://photos.foobar.com/19.jpg\"\n\n            profile.posts[0].title\n            // =\u003e Hello World\n\n    })\n\n})\n```\n\nThat's all. Check out [the example code](#install) for more experience. Thanks for reading!\n\n\u003ca name=\"see-also\"\u003e\u003c/a\u003e\n### See Also\n\n* [By example: Continuation-passing style in JavaScript](http://matt.might.net/articles/by-example-continuation-passing-style/)\n* [Functional Programming / Eloquent JavaScript](http://eloquentjavascript.net/chapter6.html)\n\n![](http://distilleryimage6.s3.amazonaws.com/b501b1409c1811e2af1622000a1fb845_6.jpg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fazer%2Fdeclarative-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fazer%2Fdeclarative-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fazer%2Fdeclarative-js/lists"}