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

rosmaro-binding-utils

v0.1.0

Published

Makes writing simple rosmaro bindings easier.

Downloads

25

Readme

Rosmaro binding utilities

A set of utilities for writing Rosmaro handlers.

Snippet

import {
  typeHandler,
  defaultHandler,
  partialReturns,
  targetedActions,
  callChildren,
  supportEntryActions,
  triggerEntryActions
} from 'rosmaro-binding-utils';

const makeHandler = handlerPlan => supportEntryActions(targetedActions()(partialReturns(typeHandler({defaultHandler})(handlerPlan))));

// ...

const model = triggerEntryActions(rosmaro({graph, bindings}));

callChildren

Allows to call all the children at once.

import {callChildren} from 'rosmaro-binding-utils';
// It's transparent for the context of leaves.
const action = {type: 'DO_YOUR_JOB'};
const context = {a: 1, b: 2};
const children = {};
expect(
  fn({context, action, children})
).toEqual({
  context: {a: 1, b: 2},
  result: undefined,
  arrows: []
});
// It extends the arrow followed by a single child node.

const action = {type: 'DO_YOUR_JOB'};
const context = {a: 1, b: 2};
const children = {
  A: ({action}) => ({
    arrows: [[['main:A', 'x']]],
    result: 'AResult',
    context: {a: 2, b: 4},
  })
};
expect(
  callChildren({context, action, children})
).toEqual({
  context: {a: 2, b: 4},
  result: 'AResult',
  arrows: [
    [['main:A', 'x'], ['main', 'x']],
  ]
});
// Merges composites.
const action = {type: 'DO_YOUR_JOB'};
const context = {a: 1, b: 2};
const children = {
  A: ({action}) => ({
    arrows: [[['main:A', 'x']]],
    result: 'AResult',
    context: {a: 2, b: 2},
  }),
  B: ({action}) => ({
    arrows: [[['main:B', 'y']]],
    result: 'BResult',
    context: {a: 1, b: 4},
  })
};
expect(
  callChildren({context, action, children})
).toEqual({
  context: {a: 2, b: 4},
  result: {A: 'AResult', B: 'BResult'},
  arrows: [
    [['main:A', 'x'], ['main', 'x']],
    [['main:B', 'y'], ['main', 'y']],
  ]
});

defaultHandler

import 'defaultHandler' from 'rosmaro-binding-utils';

If a node has just one child, this handler is simply transparent.

If a node has many children, let's say A and B, then the result looks like this:

{
  data: {A: 'AResult', B: 'BResult'},
  effect: [
    {type: 'A_EFFECT_1'},
    {type: 'A_EFFECT_2'},
    {type: 'B_EFFECT_1'}
  ]
}

triggerEntryActions

Enable entry actions for Rosmaro nodes.

Installation

Import:

import {triggerEntryActions} from 'rosmaro-binding-utils';

Wrap the model with triggerEntryActions:

const myModel = rosmaro({bindings, graph});
const aModelThatTriggersEntryActions = triggerEntryActions(myModel);

Usage

Follow arrows and return effects from the ON_ENTRY handler:

{
  ON_ENTRY: ({context}) => ({
    arrow: 'started',
    effect: {type: 'START_AN_AWESOME_PROJECT'},
  })
}

extendArrows

"Extends" the given arrows in such a away that the parent nodes also follow them.

import {extendArrows} from 'rosmaro-binding-utils';

extendArrows(
  [
    [['a:a:a', 'x']], 
    [['a:a:b', 'y']]
  ]
)
/*
[
  [['a:a:a', 'x'], ['a:a', 'x']], 
  [['a:a:b', 'y'], ['a:a', 'y']]
]
*/
extendArrows(
  [
    [['a:a:a', 'x']], 
    [['a', 'y']]
  ]
)
/*
[
  [['a:a:a', 'x'], ['a:a', 'x']], 
  [['a', 'y']]
]
*/
extendArrows(
  [
    [['a:a:a', 'x']], 
    []
  ]
)
/*
[
  [['a:a:a', 'x'], ['a:a', 'x']], 
  []
]
*/

extractParent

Extracts the parent node.

import {extractParent} from 'rosmaro-binding-utils';
extractParent('a') // null
extractParent('a:b:c') // 'a:b'

initialValueLens

Allows to set an initial value for parts which are undefined.

import {initialValueLens} from 'rosmaro-binding-utils';
// Alters the context if it is undefined.
testLens({
  lens: initialValueLens({a: 123}),
  zoomInInput: undefined, 
  zoomInOutput: {a: 123},
  zoomOutInput: {a: 123},
  zoomOutOutput: {a: 123},
})
// Does not alter empty objects.
testLens({
  lens: initialValueLens({a: 123}),
  zoomInInput: {}, 
  zoomInOutput: {},
  zoomOutInput: {},
  zoomOutOutput: {},
})

mergeArrows

Merges arrows from two sources into one set of arrows.

import {mergeContexts} from 'rosmaro-binding-utils';
mergeArrows([
  [
    [['a:a:a', 'x'], ['a:a', 'x'], ['a', 'x']],
    [['c:a:a', 'x'], ['c:a', 'x'], ['c', 'x']],
  ],
  [
    [['a:a:b', 'x'], ['a:a', 'x'], ['a', 'x']],
  ]
])
/*
[
  [['a:a:a', 'x'], ['a:a', 'x'], ['a', 'x']],
  [['c:a:a', 'x'], ['c:a', 'x'], ['c', 'x']],
  [['a:a:b', 'x'], ['a:a', 'x'], ['a', 'x']],
]
*/

mergeContexts

Applies differences between the initial context and the new contexts to the initial context.

import {mergeContexts} from 'rosmaro-binding-utils';
mergeContexts(
  {a: 123, b: 456},
  [
    {a: 123, b: 456},
    {a: 123, b: 456, c: 789},
    {a: 123, b: 456},
  ]
)
// {a: 123, b: 456, c: 789}
mergeContexts(
  {a: 123, b: 456, c: 789},
  [
    {a: 123, b: 456, c: 789},
    {a: 123, b: 456},
    {a: 123, b: 456, c: 789},
  ]
)
// {a: 123, b: 456}
mergeContexts(
  {a: 123, b: 456, c: 789},
  [
    {a: 123, b: 456, c: 789},
    {a: 123, b: 654, c: 789},
    {a: 123, b: 456, c: 789},
  ]
)
// {a: 123, b: 654, c: 789}

partialReturns

Takes a handler which may return just some of the data and returns a handler that always returns an object like {result: {data, effect}, arrows, context}.

import {partialReturns} from 'rosmaro-binding-utils';
// Allows to return just some result.
partialReturns(
  opts => ({resultOf: opts})
)({context: {a: 123}})
/*
({
  result: {
    data: {resultOf: {context: {a: 123}}},
    effect: undefined,
  },
  arrows: [],
  context: {a: 123}
})
*/
// Allows to return just an arrow.
partialReturns(
  opts => ({arrow: 'x'})
)({context: {a: 123}, node: {id: 'main:a:b'}})
/*
({
  result: {
    data: undefined,
    effect: undefined,
  },
  arrows: [[['main:a:b', 'x']]],
  context: {a: 123}
})
*/
// Allows to return just an effect.
partialReturns(
  opts => ({effect: {type: 'TICK'}})
)({context: {a: 123}})
/*
({
  result: {
    data: undefined,
    effect: {type: 'TICK'},
  },
  arrows: [],
  context: {a: 123}
})
*/
// Allows to return just some data and an effect.
partialReturns(
  opts => ({result: {theResult: opts.context}, effect: {type: 'TICK'}})
)({context: {a: 123}})
/*
({
  result: {
    data: {theResult: {a: 123}},
    effect: {type: 'TICK'},
  },
  arrows: [],
  context: {a: 123}
})
*/
// Allows to return an arrow, an effect, a context and some data
partialReturns(
  opts => ({
    arrow: 'x', 
    result: {theResult: opts.context}, 
    context: {a: 567}, 
    effect: {type: 'TICK'}
  })
)({context: {a: 123}, node: {id: 'main:a:b'}})
/*
({
  result: {
    data: {theResult: {a: 123}},
    effect: {type: 'TICK'},
  },
  arrows: [[['main:a:b', 'x']]],
  context: {a: 567}
})
*/
// Does not touch a result meeting the format requirements.
partialReturns(
  opts => ({
    arrows: [[['main:a:b', 'x']]], 
    result: {data: {theResult: opts.context}, effect: {type: 'TICK'}}, 
    context: {a: 567}, 
  })
)({context: {a: 123}})
/*
({
  arrows: [[['main:a:b', 'x']]], 
  result: {data: {theResult: {a: 123}}, effect: {type: 'TICK'}}, 
  context: {a: 567}, 
})
*/

sliceLens

Used to slice the context.

import {sliceLens} from 'rosmaro-binding-utils';
// Allows to work with just part of an object.
testLens({
  lens: sliceLens('b'),
  zoomInInput: {a: 123, b: {c: 456}}, 
  zoomInOutput: {c: 456},
  zoomOutInput: {c: 987},
  zoomOutOutput: {a: 123, b: {c: 987}}, 
})
// Returns undefined if the desired property does not exist.
testLens({
  lens: sliceLens('b'),
  zoomInInput: {a: 123}, 
  zoomInOutput: undefined,
  zoomOutInput: {c: 987},
  zoomOutOutput: {a: 123, b: {c: 987}}, 
})

targetedActions

Allows to dispatch an action targeted at a particular node.

The targetedActions function is used to modify a handler in two ways.

First, it makes it ignore an action if it has a target property that doesn't start with the node.id value passed to the handler. In other words, an action with target: 'main:a:b' is consumed by main, main:a and main:a:b handlers, but not by the main:a:c handler.

To make building targeted actions easier, every handler is injected a toNode function. It takes an action and returns a new one which is targeted at the handler's node.

import {targetedActions} from 'rosmaro-binding-utils';

const baseHandler = ({toNode}) => ({
  //...
  result: {
    'action targeted at this node': toNode({type: 'SOME_TARGETED_ACTION'})
  }
});

const handler = targetedActions()(baseHandler);

transparentLens

Does nothing to the context

import {transparentLens} from 'rosmaro-binding-utils';

testLens({
  lens: transparentLens,
  zoomInInput: {a: 123, b: {c: 987}}, 
  zoomInOutput: {a: 123, b: {c: 987}},
  zoomOutInput: {a: 123, b: {c: 987}},
  zoomOutOutput: {a: 123, b: {c: 987}},
})

typeHandler

Associated handlers with action types.

import {typeHandler} from 'rosmaro-binding-utils';

// The handler which is going to be used when the
// given action is neither FIRST_ACTION nor SECOND_ACTION.
const defaultHandler = (opts) => ({UNSUPPORTED_ACTION: opts});

// Handles actions of 'FIRST_ACTION' type.
const firstActionHandler = (opts) => ({FIRST_ACTION: opts});

// Handles actions of 'SECOND_ACTION' type.
const secondActionHandler = (opts) => ({SECOND_ACTION: opts});

// The whole handler.
const handler = typeHandler({defaultHandler})({
  // Actions of 'FIRST_ACTION' type are dispatched to this handler.
  FIRST_ACTION: firstActionHandler,
  // Actions of 'SECOND_ACTION' type are dispatched to this handler.
  SECOND_ACTION: secondActionHandler,
});

handler({action: {type: 'FIRST_ACTION'}, something: 'else'})
// {FIRST_ACTION: {action: {type: 'FIRST_ACTION'}, something: 'else'}}

handler({action: {type: 'SECOND_ACTION'}, something: 'else'})
// {SECOND_ACTION: {action: {type: 'SECOND_ACTION'}, something: 'else'}}

handler({action: {type: 'THIRD_ACTION'}, something: 'else'})
// {UNSUPPORTED_ACTION: {action: {type: 'THIRD_ACTION'}, something: 'else'}}