promise-apply-spec

2.0.0 • Public • Published

promise-apply-spec

version npm build status Coveralls deps status mit license

PromiseApplySpec is a Promise utility function like Promise.all plus Ramda's applySpec for arbitrary raw data structures containing Promises and functions.

Features

  • Captures and reports all errors from promises with enough detail to trace their location in the original structure.

  • (Optionally) recursively resolves Promises within promise-resolved leaf structures.

  • (Optionally) recursively expands functions in the spec with supplied arguments.

  • Differing from Ramda, handles structures with strings and classed objects, leaving them untouched.

  • Offers 3 different API styles (traditional, fluent, functional/tacit).

  • Covered by extensive functional testing.

Basic Usage

// Standard API
const applySpecP = require('promise-apply-spec');
 
const data = [Promise.resolve(1), {a: 2, b: Promise.resolve(3)}];
applySpecP(data); // => Promise([1, {a: 2, b: 3});
 
const failing = [
    Promise.resolve(1),
    {a: Promise.reject(new Error('two'))},
    Promise.reject({c: 3})
];
applySpecP(failing).catch(function(err) {
    err; // => Error('Promises rejected:\n - two\n - [Object object]')
    err.errors; // => [Error('two'), {c: 3}]
    err.errors[0].path; // => '1.a'
    err.errors[1].path; // => `undefined` (will only mutate Errors)
});
 
// Fluent API
const applySpecP = require('promise-apply-spec');
 
applySpecP
    .withSpec(data)
    .exec(); // => Promise([1, {a: 2, b: 3});
 
// Functional API
const { all, once, applySpec, unravel, unravelOnce } = require('promise-apply-spec');
 
const spec = {
  a: Promise.resolve(1),
  b: Promise.resolve({c: Promise.resolve(3), d: x => x+1})
};
 
// expand everything, recursively
unravel(spec, [4]).then(function(data) {
  data; // => {a: 1, b: {c: 3, d: 5}}
});
 
// expand initial promises and functions only
unravelOnce(spec)([4]).then(function(data) {
  data; // => {a: 1, b: {c: Promise(3), d: 5}}
});
 
// expand promises only, recursively
all(spec).then(function(data) {
  data; // => {a: 1, b: {c: 3, d: x => x+1}}
});
 
// expand functions once only
applySpec(spec)([4]); // => spec
applySpec({a: x => x+1}, [4]); // => {a: 5}

API (traditional)

const { applySpecP } = require('promise-apply-spec');

applySpecP(spec[, arg[, options]]) ⇒ Promise(data) | data

The fully featured main function — provided as both the default export and a named export — with a traditional, data-first and non-curried signature.

Params:

  • spec: Any: any combination of nested arrays, plain objects, functions, promises, and other values ('other values' that look like plain objects containing promises or functions will get recreated as truly plain objects)

  • args: Array: arguments to pass into invoked spec functions

  • options: Configuration object

    • apply: Boolean: whether to replace spec functions with their invocation — default true
    • resolve: Boolean: whether to resolve promises — default true
    • once: Boolean: when true, unwraps promises at most once, then expands functions at most once, and returns — default false

Return:

  • when options.resolve = true, a Promise of the resolved and optionally function-applied data structure
  • when options.resolve = false, the optionally function-expanded data structure

Fluent API (building operations via method chaining)

const Fluent = require('promise-apply-spec');

The initial immutable Fluent instance with options {apply: true, resolve: true, once: false}.

Fluent.applyTo(args) ⇒ Fluent

Get a new immutable Fluent with default args provided and options.apply = true.

Fluent.withSpec(spec) ⇒ Fluent

Get a new immutable Fluent with default spec provided.

Fluent.repeating(Boolean) ⇒ Fluent

Get a new immutable Fluent with options.once reconfigured.

Fluent.applying(Boolean) ⇒ Fluent

Get a new immutable Fluent with options.apply reconfigured.

Fluent.resolving(Boolean) ⇒ Fluent

Get a new immutable Fluent with options.resolve reconfigured.

Fluent.exec([spec[, args[, once]]]) ⇒ Promise(data) | data

Execute applySpecP with all arguments defaulting to the Fluent instance configuration.

Functional API (auto-curried, data-last functions)

const { all, once, applySpec, unravel, unravelOnce } = require('promise-apply-spec');

all :: spec → Promise(data)

Recursively expands promises, ignores functions.

once :: spec → Promise(data)

Expands promises once, ignores functions.

applySpec :: spec → [arg] → data

Expands functions once, ignores promises.

unravel :: spec → [arg] → Promise(data)

Recursively expands promises and functions.

unravelOnce :: spec → [arg] → Promise(data)

Expands promises once, then functions once.

Related Libraries and Functions

  • promise-all: Turns a flat array or object of promises into a promise of the same data structure with promises unwrapped. Limitations: only supports flat data (no nested objects or arrays), cannot recursively expand promises, does not expand functions.

  • promise-all-recursive: Turns a data structure containing promises into a promise of the data structure with promises recursively unwrapped. Limitation: does not handle promise rejections or expand functions.

  • promise-traverse: Turns a data structure containing promises into a promise of the data structure with promises unwrapped. Limitations: cannot recursively expand promises, does not handle promise rejections or expand functions.

  • ramda.applySpec: Turns a spec object into a function that returns the same data structure but replacing inner functions with their invocation on the supplied argument. Limitations: only works for spec objects containing only non-string primitives and functions as leaf nodes, does not resolve promises, and does not pass more than one argument to spec functions.

Dependencies (1)

Dev Dependencies (5)

Package Sidebar

Install

npm i promise-apply-spec

Weekly Downloads

2

Version

2.0.0

License

MIT

Unpacked Size

38.7 kB

Total Files

7

Last publish

Collaborators

  • evan-king