npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@sunny-g/cycle-utils

v0.2.4

Published

Higher-order components and utilities for Cycle.js

Downloads

14

Readme

cycle-utils

utilities and higher-order components for transforming Cycle components, sources and sinks

why

The popular utility library Recompose allows you to write simple React components and independently augment them with additional granular and testable layers of additional functionality.

This library provides simple higher-order component factories and other utilities to simplify Cycle.js components and allow you to write your own higher-order component libraries easily.

installation

npm install --save @sunny-g/cycle-utils

usage

import { mapSources } from '@sunny-g/cycle-utils';
// or
import mapSources from '@sunny-g/cycle-utils/es2015/mapSources';

api

A higher-order component (HOC) is a function that takes in a Cycle.js component and returns a Cycle.js component.

This pattern makes HOCs composable and allows us to use Ramda's compose or pipe functions to stitch multiple HOCs together into a single, larger HOC.

The following HOCs and utilities are provided by this library:

mapSources()

mapSources(
  sourceNames,
  sourceMapper
): HigherOrderComponent

HOC that applies the sourceMapper to sources before they've been passed into the BaseComponent.

parameters:
  • sourceNames: '*' | string | string[]: Sources you want to transform ('*' if you want to pass the entire sources object)
  • sourceMapper: (Sources | ...Sources[]) => Sources: Transform function to be applied to specified sources; the returned Sources are merged into the original Sources

Example:

// adds a fetched property to the `props` object provided by a fictional `props` source
const withNewProps = mapSources(
  [ 'props', 'HTTP' ], (propsSource, HTTP) => {
    const newProp = HTTP
      .select('newProp')
      .flatten();

    const newPropsSource = combine(propsSource, newProp)
      .map(([ props, newProp ]) => ({
        ...props,
        'newProp': newProp,
      });

    return { props: newPropsSource };
  }
);

const ComponentWithProps = withNewProps(Component);

mapSinks()

mapSinks(
  sinkNames,
  sinkMapper
): HigherOrderComponent

HOC that applies the sinkMapper to sinks after they've been returned from the BaseComponent.

parameters:
  • sinkNames: '*' | string | string[]: Sources you want to transform ('*' if you want to pass the entire sinks object)
  • sinkMapper: (Sinks | ...Sinks[]) => Sinks: Transform function to be applied to specified sinks; the returned Sinks are merged into the original Sinks

Example:

// logs all emitted HTTP requests
const logHTTPSink = mapSinks(
  'HTTP', (HTTPSink) => ({
    HTTP: HTTPSink.debug('making an HTTP request'),
  })
);

const ComponentWithLoggedHTTPSink = logHTTPSink(Component);

mapSinksWithSources()

mapSourcesAndSink(
  sinkNames,
  sourceNames,
  sinkAndSourceMapper
): HigherOrderComponent

HOC to transform a component's sinks with any desired sources after the sinks have been returned from the BaseComponent.

parameters:
  • sinkNames: '*' | string | string[]: Sinks you want to transform ('*' if you want the entire sinks object)
  • sourceNames: '*' | string | string[]: Sources you want to transform ('*' if you want the entire sources object)
  • sinkAndSourceMapper: (Sinks | ...Sinks[], Sources | ...Sources[]) => Sinks: Transform function to be applied to specified sinks and sources; the returned Sinks are merged into the original Sinks

Example:

// logs all emitted sinks values with the current props
const logAllSinks = mapSinksWithSources(
  '*', 'props', (sinks, propsSource) => {
    return Object.keys(sinks)
      .reduce((newSinks, sinkName) => ({
        ...newSinks,
        [sinkName]: sinks[sinkName]
          .compose(sampleCombine(propsSource))
          .debug('new sink emission with current props')
      }), {});
  }
);

const ComponentWithLoggedSinksWithProps = logAllSinks(Component);

mapSourcesAndSinks()

mapSourcesAndSink(
  sourceNames,
  sourceMapper,
  sinkNames,
  sinkMapper
): HigherOrderComponent

HOC to transform both a component's sources before entering the BaseComponent and a component's sinksafter they've been returned from the BaseComponent.

Uses mapSources and mapSinksWithSources under the hood, so the same requirements of those functions apply.

parameters:
  • sourceNames: '*' | string | string[]: Sources you want to transform ('*' if you want the entire sources object)
  • sourceMapper: (Sources | ...Sources[]) => Sources: Transform function to be applied to specified sources; the returned Sources are merged into the original Sources
  • sinkNames: '*' | string | string[]: Sources you want to transform ('*' if you want the entire sinks object)
  • sinkMapper: (Sinks | ...Sinks[], Sources) => Sinks: Transform function to be applied to specified sinks as well as the entire sources object; the returned Sinks are merged into the original Sinks

Example:

// TODO: ADD AN EXAMPLE HERE

isolate()

isolate(
  config: ((Sources: any) => null | string | {}) | null | string | {}
): HigherOrderComponent

HOC version of @cycle/isolate.

parameters:
  • config: ((Sources: any) => null | string | {}) | null | string | {}: Either null, a string, or an object, or a function that is given sources and returns null, a string, or an object

Example:

// any component wrapped with randomIsolation will receive a randomly-generated scope

const randomIsolation = isolate(() => Math.random().toString());

const NewComponent = randomIsolation(Component);

combineSinks()

combineSinks(
  combiners: { [sinkName: string]: sinkCombiner }
): SinksCombiner

Utility to declaratively combine multiple Sinks objects

parameters:
  • combiners: { [sinkName: string]: sinkCombiner }: Object of sinkCombiners for each sinkName to combine
    • each individual sinkCombiner has the signature (...sink) => sink and is given each Sink from each passed-in Sinks object and should return a combined Sink stream
    • NOTE: if the sinkCombiner for a given sinkName is missing and there are multiple sinks of that sinkName, the sink's native merge function is applied to the list of sinks
returns:
  • SinksCombiner: (...Sinks[]) => Sinks: A function to apply to multiple Sinks objects that:
    1. groups all Sinks of the same sinkName into an array
    2. applies each individual sinkCombiner to the destructured array of the Sinks
    3. creates and returns a new Sinks object from the combined Sinks.

Example:

// say we have multiple non-identical children component sinks
// as well as the component's own sinks...

// has only a DOM sink
const mainSinks = { DOM: ... };
// each has both DOM and HTTP sinks
const childOneSinks = childOne(sources);
const childTwoSinks = childTwo(sources);

const sinkCombiner = combineSinks({
  // some drivers only require a merge of their sinks
    // combineSinks merges by default, but is shown here
    // note that there is no placeholder argument for missing sinks (mainSinks has no HTTP sink)
  HTTP: (childOneHTTP, childTwoHTTP) => xs.merge(childOneHTTP, childTwoHTTP),

  // some sinks require custom merging/combining...
  DOM: (mainDOM$, childOneDOM$, childTwoDOM$) => xs
    .combine(mainDOM$, childOneDOM$, childTwoDOM$)
    .map(([ mainDOM, childOneDOM, childTwoDOM ]) =>
      div([
        mainDOM,
        div([
          childOneDOM,
          childTwoDOM,
        ])
      ])
    ),
});

return sinkCombiner(mainSinks, childOneSinks, childTwoSinks);

combineCycles()

combineCycles(
  combiners: { [sinkName: string]: sinkCombiner },
  ...BaseComponents: Component[]
): CombinedComponent

Utility to declaratively combine multiple Cycle components into a single Cycle component

parameters:
  • combiners: { [sinkName: string]: sinkCombiner }: Object of sinkCombiners for each sinkName to combine
    • each individual sinkCombiner has the signature (...sinks) => sink and is given each Sink from each passed-in Sinks object and should return a combined Sink stream
    • NOTE: if the sinkCombiner for a given sinkName is missing and there are multiple sinks of that sinkName, the first sink's native merge operator is applied to the list of sinks
  • ...BaseComponents: Component[]: The desired Cycle.js components to combine into a single component
returns:
  • CombinedComponent: (Sources | ...Sources[]) => Sinks: An otherwise normal Cycle.js component that differs from traditional components in that it takes in either:
    • a single Sources object to be passed into each BaseComponent
    • an list of Sources objects, each one passed into the corresponding BaseComponent by index

Example:

// assume the same `childOne` and `childTwo` components from the previous example

const ChildrenComponent = combineCycles({
  HTTP: (...httpSinks) => xs.merge(...httpSinks),
  DOM: (childOneDOM$, childTwoDOM$) => xs
    .combine(childOneDOM$, childTwoDOM$)
    .map(([ childOneDOM, childTwoDOM ]) =>
      div([
        childOneDOM,
        childTwoDOM,
      ])
    ),
}, childOne, childTwo);

const childrenSinks = ChildrenComponent(sources);

Another useful example is for chaining tasks together in a more linear format:

// the first task, makes a request
const task1 = _ => ({
  HTTP: of({
    url: 'https://google.com',
    category: 'req1',
  }),
});

// the second task
// maps the response of the first task's request into the response sink of the second task
const task2 = ({ HTTP }) => ({
  HTTP: HTTP
    .select('req1')
    .flatten()
    .map(res1 => ({
      url: res1.url,
      category: 'req2'
    })),
});

function main(sources) {
  const Task = combineCycles({}, task1, task2);
  const taskSinks = Task(sources);

  // now, we can use the result of the tasks
  const taskRes = sources.HTTP
    .select('req2')
    .flatten();

  // ... the rest of the component, returns main sinks merged with taskSinks...
}

contributing

todo

  • ensure typings are comprehensive and correct
  • explain contribution process
  • add more tests :)
  • explain why I wrote this

license

ISC