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

representable-state

v1.1.0

Published

Make Illegal States Unrepresentable

Downloads

2

Readme

Make Illegal States Unrepresentable in JavaScript

Set and get your state safely, with declarative constraints

Some languages provide support for unions or enums with a value, that allow to always represent a state consistently.

This is a playground to achieve something similar in JavaScript.

Design

A Representable State (RState) class should be only settable to one of pre-defined states that are, as much as possible, close to enums with optional enum values. Plain enums are represented by arbitrary constants, such as strings or numbers, while enums with values are represented by a special class, StateType, that holds a value.

What should happen when an incorrect value is set? Typed languages would prohibit such thing in compile time. RState detects this in runtime. In such cases, it throws an exception that the calling layer needs to handle. This might be beneficial in come cases, but often it can be a burden. This is solved by an optional default value to switch (and no exception is thrown).

In cases like finite automatons, a transition from a valid state to another valid state may be invalid. Transition validity may be solved by an optional list of transitions from a value to other values.

Open questions:

  • does anybody want that?

API

RState is a class defined inside a defState(...) function scope.

get returns the current state

is checks whether RState is in a desired state

when calls a callback if RState is in a desired state and passes the state value to the callback

collect returns the value returned by when callback

set changes the current state

update updates the current state value

Except for update recursion, StateType just stores a value passed to constructor and provides it back via a value().

Examples

const ReadyState = defState('unloaded', 'loading', 'complete');
let readyState = new ReadyState('unloaded');
...
readyState.set('loading');
readyState.set('void');  // this throws an error
class HttpOk extends StateType {}
class HttpRedirect extends StateType {}
class HttpError extends StateType {}

const HttpStatus = defState(HttpOk, HttpRedirect, HttpError);
...
let httpResult = new HttpStatus(new HttpRedirect({ code: 303, redirectUrl = 'http://see.other.com' }));
...

function httpStatusCode(v) { return v.code }

const resultCode = httpResult
    .when(HttpOk, httpStatusCode)        // success status
    .when(HttpRedirect, httpStatusCode)  // redirect status
    .collect('error');                   // 'error' is the default if none of the whens triggered

console.log('Result:', resultCode);

if (httpResult.is(HttpRedirect)) {
    startRedirect( httpResult.map(x => x.redirectUrl) );
}

Multiple constraints

Let's assume a case where you are allowed to view certain sets of pages based on:

  • whether you have an active subscription
  • whether you have any friends

in an imaginary chat app. The enum-value concept does not help here.

intersectState allows multiple RState instances to sync on states they have in common.

Example

const unsubscribedRoutes = defState('subscribe', 'profile').create();
const subscribedRoutes   = defState('friends', 'chat', 'profile').create();

const friendsRoutes      = defState('subscribe', 'profile', 'chat', 'friends').create();
const noFriendsRoutes    = defState('subscribe', 'profile').create();

const validRoutes = intersectState({
   subscription: subscribed ? subscribedRoutes : unsubscribedRoutes,
   friends: friends.count() > 0 ? friendsRoutes : noFriendsRoutes
}).create('profile');

validRoutes.subscribe(state => console.log('Navigated to:', state));

unsubscribedRoutes.set('subscribe');
...
validRoutes.reset({ subscription: subscribedRoutes });

Other features

Usable with Redux / Vuex / ....