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

@dws/muster

v6.7.2

Published

A universal data layer for components and services

Downloads

23

Readme

Muster

A universal data layer for your components and services

Muster is a state and data management library that gives you a unified, reactive, declarative, performant and powerful view of as much or as little data as you need.

Muster comes bundled with a number of [[NodeDefinition]]s split into following categories:

  • arithmetic - Nodes focusing on arithmetic operations, e.g. [[add]], [[subtract]], etc.
  • browser - Nodes used for interacting with the browser, e.g. [[location]]
  • collections - Nodes that are handling collections and all of the transformations on it, e.g. [[applyTransforms]], [[filter]], [[sort]], [[map]], etc.
  • graph - Core Muster nodes, e.g. [[ref]], [[variable]], [[value]], [[action]], etc.
  • logic - Nodes introducing logic operators, e.g. [[and]], [[eq]], [[ifElse]], etc.
  • numeric - Nodes helping with handling numbers in Muster, e.g. [[parseInt]]
  • remote - Nodes used for connecting Muster with remote Muster instances, e.g. [[proxy]].
  • string - String manipulation nodes, e.g. [[endsWith]], [[join]], [[split]], etc.

Installation

npm install --save @dws/muster @dws/muster-observable

Usage

Creating basic muster application

import muster from '@dws/muster';

const app = muster({
  firstName: 'John',
  lastName: 'Doe',
  age: 43,
});

This example creates a muster application containing three nodes:

  • firstName: [[value]] = 'John'
  • lastName: [[value]] = 'Doe'
  • age: [[value]] = 43

Accessing data from a muster graph

const app = muster({ firstName: 'John' });

First let's create a simple muster instance with a graph containing a single leaf [[value]] firstName. Now we have an instance of muster we can make queries against it:

const firstName = await app.resolve(ref('firstName'));

The [[Muster]] object exposes a resolve method which can be used for running queries against muster. In this example we've used a [[ref]]. This node is used to locate and return a graph node based on its path in the graph. The path in the graph is defined by the hierarchy of branches that exist in the muster graph. In this example we have just one branch containing a single leaf firstName. You can find out more about paths and branches in the [[ref]] and [[tree]] documentation.

app.resolve returns an object implementing both [[Observable]] and Promise APIs. In this example we have retrieved the current value of the firstName node with the use of the Promise API. Alternatively this can also be done with [[Observable]] by subscribing to the returned observable:

const unsubscribe = app.resolve(ref('firstName')).subscribe((firstName) => {
  // Do something with the first name as it changes over time
});

By nature, muster tries to execute the code synchronously when it can, so the callback of the subscribe will be called immediately with the latest value of firstName. In this example firstName resolves to 'John' string.

Notice: subscribe has one important advantage over await. It responds to changes in the subscribed query over time. Consider an example where the firstName is a node whose value changes while the application is running. Promise will emit the value as soon as it's available but won't cause your code to be re-run when the value of the subscribed node changes. On the other hand, [[Observable]] will re-emit a new value causing the subscriber (the function used to create the subscription) to be executed again with a new value.

See [[variable]], [[fromPromise]] and [[fromStream]] for more information about settable nodes.

Creating muster application with branches

import muster, { computed, ref } from '@dws/muster';

const app = muster({
  user: {
    firstName: 'John',
    lastName: 'Doe',
    fullName: computed([
      ref('user', 'firstName'),
      ref('user', 'lastName'),
    ], (firstName, lastName) => `${firstName} ${lastName}`),
  },
});

This example creates a muster application where the graph has one branch user ([[tree]]). That branch has three leaves:

  • firstName: [[value]] = 'John'
  • lastname: [[value]] = 'Doe'
  • fullName: [[computed]] = A node which computes full name of the user based on the current value of firstName and lastName.

Note the fullName [[computed]] dependencies are defined using fully qualified path of the nodes in the graph. This could have been simplified with the use of a [[relative]] helper function:

import muster, { computed, ref, relative } from '@dws/muster';

const app = muster({
  user: {
    firstName: 'John',
    lastName: 'Doe',
    fullName: computed([
      ref(relative('firstName')),
      ref(relative('lastName')),
    ], (firstName, lastName) => `${firstName} ${lastName}`),
  },
});

The benefit of using [[relative]] instead of defining the full path is apparent when refactoring the application graph. Imagine a scenario when the user branch is to be renamed to currentUser. In this scenario, when the full path is used we'd have to change the name in three places: branch definitions and both references. It would be much simpler with the use of [[relative]] helper: we'd have to only rename the branch.

You can find out more about [[relative]] helper on its documentation.

Lazy-evaluating values

import muster, { computed, ref } from '@dws/muster';

let externalValue = 'initial';

const app = muster({
  testComputed: computed([], () => {
    externalValue = 'updated';
    return true;
  }),
});

// externalValue === 'initial'
const subscription = app.resolve(ref('testComputed'));

// externalValue === 'initial'

const unsubscribe = subscription.subscribe((testComputedValue) => {
  // externalValue === 'updated'
  // testComputedValue === true
});
// externalValue === 'updated'

This example demonstrates the principle of lazy evaluation in muster. In muster the value of every node is evaluated only when a subscription is created to that node. Note how the value of externalValue changes in this example. Thanks to the synchronous code execution in muster the externalValue is set to the new value before the first line of the subscriber callback as well as after the call to the subscribe method.

Most of the Muster [[NodeDefinition]]s are synchronous, with exception of [[fromPromise]], [[fromStream]] (depending on the type of stream used), [[action]], [[proxy]] and [[remote]].

Complex queries

import muster, { key, query, root } from '@dws/muster';

const app = muster({
  user: {
    firstName: 'Bob',
    lastName: 'Johnson',
  },
});
 
const userTree = await app.resolve(query(root(), {
  user: key('user', {
    firstName: key('firstName'),
    lastName: key('lastName'),
  }),
}));
// userTree is a JS object with all of the requested branches encoded in it:
// {
//   user: {
//     firstName: 'Bob',
//     lastName: 'Johnson',
//   },
// }

In previous examples we've been requesting a single [[NodeDefinition]] at a time. In real world apps this would not be the most efficient or fastest way of writing code. For this reason Muster comes with a [[query]] which allows for building complex queries. These queries define the shape of their output data as well as the place from the graph this data is to be retrieved from. See the [[query]] documentation to learn more about queries.