{"id":19248694,"url":"https://github.com/evan-king/promise-apply-spec","last_synced_at":"2026-02-11T12:32:43.842Z","repository":{"id":45048163,"uuid":"147966191","full_name":"evan-king/promise-apply-spec","owner":"evan-king","description":"a Promise utility function like `Promise.all` plus Ramda's `applySpec` for arbitrary raw data structures containing Promises and functions","archived":false,"fork":false,"pushed_at":"2022-01-12T16:56:58.000Z","size":14,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-18T11:33:10.233Z","etag":null,"topics":["async","point-free","promise","ramda","utility-function"],"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/evan-king.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-09-08T20:06:49.000Z","updated_at":"2020-06-08T11:35:09.000Z","dependencies_parsed_at":"2022-08-19T19:00:19.602Z","dependency_job_id":null,"html_url":"https://github.com/evan-king/promise-apply-spec","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/evan-king/promise-apply-spec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evan-king%2Fpromise-apply-spec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evan-king%2Fpromise-apply-spec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evan-king%2Fpromise-apply-spec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evan-king%2Fpromise-apply-spec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evan-king","download_url":"https://codeload.github.com/evan-king/promise-apply-spec/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evan-king%2Fpromise-apply-spec/sbom","scorecard":{"id":385407,"data":{"date":"2025-08-11","repo":{"name":"github.com/evan-king/promise-apply-spec","commit":"fb6a6fa410c4284f97d05263e99a600debef4de8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/8 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":"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":"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":"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":"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":"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":"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":"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-18T16:28:43.677Z","repository_id":45048163,"created_at":"2025-08-18T16:28:43.677Z","updated_at":"2025-08-18T16:28:43.677Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29332994,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T06:13:03.264Z","status":"ssl_error","status_checked_at":"2026-02-11T06:12:55.843Z","response_time":97,"last_error":"SSL_read: 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":["async","point-free","promise","ramda","utility-function"],"created_at":"2024-11-09T18:09:54.113Z","updated_at":"2026-02-11T12:32:43.807Z","avatar_url":"https://github.com/evan-king.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# promise-apply-spec\n\n[![version][version-img]][version-url]\n[![npm][npmjs-img]][npmjs-url]\n[![build status][travis-img]][travis-url]\n[![Coveralls][coveralls-img]][coveralls-url]\n[![deps status][daviddm-img]][daviddm-url]\n[![mit license][license-img]][license-url]\n\nPromiseApplySpec is a Promise utility function like `Promise.all` plus Ramda's\n`applySpec` for arbitrary raw data structures containing Promises and functions.\n\n## Features\n\n  - Captures and reports all errors from promises with enough detail to trace their\n    location in the original structure.\n  \n  - (Optionally) recursively resolves Promises within promise-resolved leaf structures.\n  \n  - (Optionally) recursively expands functions in the `spec` with supplied arguments.\n  \n  - Differing from Ramda, handles structures with strings and classed objects, leaving\n    them untouched.\n  \n  - Offers 3 different API styles (traditional, fluent, functional/tacit).\n  \n  - Covered by extensive functional testing.\n\n## Basic Usage\n\n```javascript\n// Standard API\nconst applySpecP = require('promise-apply-spec');\n\nconst data = [Promise.resolve(1), {a: 2, b: Promise.resolve(3)}];\napplySpecP(data); // =\u003e Promise([1, {a: 2, b: 3});\n\nconst failing = [\n    Promise.resolve(1),\n    {a: Promise.reject(new Error('two'))},\n    Promise.reject({c: 3})\n];\napplySpecP(failing).catch(function(err) {\n    err; // =\u003e Error('Promises rejected:\\n - two\\n - [Object object]')\n    err.errors; // =\u003e [Error('two'), {c: 3}]\n    err.errors[0].path; // =\u003e '1.a'\n    err.errors[1].path; // =\u003e `undefined` (will only mutate Errors)\n});\n\n// Fluent API\nconst applySpecP = require('promise-apply-spec');\n\napplySpecP\n    .withSpec(data)\n    .exec(); // =\u003e Promise([1, {a: 2, b: 3});\n\n// Functional API\nconst { all, once, applySpec, unravel, unravelOnce } = require('promise-apply-spec');\n\nconst spec = {\n  a: Promise.resolve(1),\n  b: Promise.resolve({c: Promise.resolve(3), d: x =\u003e x+1})\n};\n\n// expand everything, recursively\nunravel(spec, [4]).then(function(data) {\n  data; // =\u003e {a: 1, b: {c: 3, d: 5}}\n});\n\n// expand initial promises and functions only\nunravelOnce(spec)([4]).then(function(data) {\n  data; // =\u003e {a: 1, b: {c: Promise(3), d: 5}}\n});\n\n// expand promises only, recursively\nall(spec).then(function(data) {\n  data; // =\u003e {a: 1, b: {c: 3, d: x =\u003e x+1}}\n});\n\n// expand functions once only\napplySpec(spec)([4]); // =\u003e spec\napplySpec({a: x =\u003e x+1}, [4]); // =\u003e {a: 5}\n```\n\n## API (traditional)\n\n```javascript\nconst { applySpecP } = require('promise-apply-spec');\n```\n\n### applySpecP(spec[, arg[, options]]) ⇒ Promise(data) | data\n\nThe fully featured main function \u0026mdash; provided as both the default export and\na named export \u0026mdash; with a traditional, data-first and non-curried signature.\n\n**Params**:\n  - **spec**: Any: any combination of nested arrays, plain objects, functions,\n    promises, and other values ('other values' that look like plain objects\n    containing promises or functions will get recreated as truly plain objects)\n    \n  - **args**: Array: arguments to pass into invoked `spec` functions\n  \n  - **options**: Configuration object\n    - **apply**: Boolean: whether to replace `spec` functions with their\n      invocation \u0026mdash; default `true`\n    - **resolve**: Boolean: whether to resolve promises \u0026mdash; default `true`\n    - **once**: Boolean: when true, unwraps promises at most once, then expands\n      functions at most once, and returns \u0026mdash; default `false`\n\n**Return**:\n  - when `options.resolve = true`, a Promise of the resolved and optionally\n    function-applied data structure\n  - when `options.resolve = false`, the optionally function-expanded data structure\n\n\n### Fluent API (building operations via method chaining)\n\n```javascript\nconst Fluent = require('promise-apply-spec');\n```\n\nThe initial immutable Fluent instance with options `{apply: true, resolve: true, once: false}`.\n\n#### Fluent.applyTo(args) ⇒ Fluent\n\nGet a new immutable Fluent with default `args` provided and `options.apply = true`.\n\n#### Fluent.withSpec(spec) ⇒ Fluent\n\nGet a new immutable Fluent with default `spec` provided.\n\n#### Fluent.repeating(Boolean) ⇒ Fluent\n\nGet a new immutable Fluent with `options.once` reconfigured.\n\n#### Fluent.applying(Boolean) ⇒ Fluent\n\nGet a new immutable Fluent with `options.apply` reconfigured.\n\n#### Fluent.resolving(Boolean) ⇒ Fluent\n\nGet a new immutable Fluent with `options.resolve` reconfigured.\n\n#### Fluent.exec([spec[, args[, once]]]) ⇒ Promise(data) | data\n\nExecute `applySpecP` with all arguments defaulting to the Fluent instance configuration.\n\n\n### Functional API (auto-curried, data-last functions)\n\n```javascript\nconst { all, once, applySpec, unravel, unravelOnce } = require('promise-apply-spec');\n```\n\n#### all :: spec → Promise(data)\n\nRecursively expands promises, ignores functions.\n\n#### once :: spec → Promise(data)\n\nExpands promises once, ignores functions.\n\n#### applySpec :: spec → [arg] → data\n\nExpands functions once, ignores promises.\n\n#### unravel :: spec → [arg] → Promise(data)\n\nRecursively expands promises and functions.\n\n#### unravelOnce :: spec → [arg] → Promise(data)\n\nExpands promises once, then functions once.\n\n\n## Related Libraries and Functions\n\n  - [`promise-all`](https://github.com/joakimbeng/promise-all):\n    Turns a flat array or object of promises into a promise of the same data structure\n    with promises unwrapped.  **Limitations**: only supports flat data (no nested objects\n    or arrays), cannot recursively expand promises, does not expand functions.\n  \n  - [`promise-all-recursive`](https://github.com/usefulthink/promise-all-recursive):\n    Turns a data structure containing promises into a promise of the data structure\n    with promises recursively unwrapped.  **Limitation**: does not handle promise\n    rejections or expand functions.\n  \n  - [`promise-traverse`](https://github.com/mvaldesdeleon/promise-traverse):\n    Turns a data structure containing promises into a promise of the data structure\n    with promises unwrapped.  **Limitations**: cannot recursively expand promises,\n    does not handle promise rejections or expand functions.\n  \n  - [`ramda.applySpec`](https://ramdajs.com/docs/#applySpec):\n    Turns a `spec` object into a function that returns the same data structure\n    but replacing inner functions with their invocation on the supplied argument.\n    **Limitations**: only works for `spec` objects containing only non-string\n    primitives and functions as leaf nodes, does not resolve promises, and does\n    not pass more than one argument to `spec` functions.\n\n[version-url]: https://github.com/evan-king/promise-apply-spec/releases\n[version-img]: https://img.shields.io/github/tag/evan-king/promise-apply-spec.svg?style=flat\n\n[npmjs-url]: https://www.npmjs.com/package/promise-apply-spec\n[npmjs-img]: https://img.shields.io/npm/v/promise-apply-spec.svg?style=flat\n\n[coveralls-url]: https://coveralls.io/r/evan-king/promise-apply-spec?branch=master\n[coveralls-img]: https://img.shields.io/coveralls/evan-king/promise-apply-spec.svg?style=flat\n\n[license-url]: https://github.com/evan-king/promise-apply-spec/blob/master/LICENSE\n[license-img]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat\n\n[travis-url]: https://travis-ci.org/evan-king/promise-apply-spec\n[travis-img]: https://img.shields.io/travis/evan-king/promise-apply-spec.svg?style=flat\n\n[daviddm-url]: https://david-dm.org/evan-king/promise-apply-spec\n[daviddm-img]: https://img.shields.io/david/evan-king/promise-apply-spec.svg?style=flat\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevan-king%2Fpromise-apply-spec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevan-king%2Fpromise-apply-spec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevan-king%2Fpromise-apply-spec/lists"}