New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

microcosm

Package Overview
Dependencies
Maintainers
1
Versions
233
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

microcosm

A functional flux architecture

  • 6.2.2
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
154
decreased by-12%
Maintainers
1
Weekly downloads
 
Created
Source

NPM


Heads up!

This is not the next big flux framework. It is largely an attempt to simplify the authoring model for some other Viget tools. The roadmap for Microcosm is heavily influenced by the needs of those projects. We will follow semver to help maintain dependency stability, but for the time being it is probably safest to stay clear of this project if you want stability.


Travis CI Coverage Status


Microcosm

Microcosm is an experimental Flux inspired by Om, Flummox and Elm. It is an isomorphic Flux.

Specifically, it addresses the problem of isolating state between requests so that each page render does not leak information into the next. State is contained within each unique Microcosm instance with changes in state handled by pure functions.

Opinions

Microcosm inserts a couple of opinions regarding the Flux architecture:

  1. Flux uses CONSTANT values to pass messages from Actions to Stores. Microcosm automatically generates these by assigning each Action function a unique toString method.
  2. Microcosm expects immutability. When an action is fired, the associated handler in Stores are given the old state. State is updated by returning a new value.
  3. Stores do not contain data, they transform it. See the section on stores below.
  4. All Actions that return promises will wait to resolve before dispatching.
  5. Utilize language features over library abstraction as much as possible.

What problems does it attempt to solve?

  1. State isolation. Requests to render applications server-side should be as stateless as possible. Client-side libraries (such as Colonel Kurtz) need easy containment from other instances on the page.
  2. A reasonable trade-off between the simplicity of singletons and the state-isolation of class instances.
  3. Easy extension of core API and layering of features out of the framework's scope.

Design

Without getting too lofty, this is roughly the ideal scenario for a Microcosm:

                                                    |--> [Store] ---|
[app.send] ------> [Action] ------> [Dispatcher] ---+--> [Store] ---+--> [app.shouldUpdate?]
   ^                                                |--> [Store] ---|            |
   |                                                                             |
   |                                                                             v
[External Services] <--------------------------------------------------------- [YES]
   |- User Interface
   |- Router
   |- Firebase sync

Writing a Microcosm

A new app starts out as an extension of the Microcosm class:

import Microcosm from 'microcosm'

class MyApp extends Microcosm {
  // Great things await
}

A microcosm is solely responsible for managing a global state object. Although a microcosm is exclusively responsible for managing its own state, stores shape how that data is changed:

import Microcosm from 'microcosm'

let Messages = {
  getInitialState() {
    return []
  },
  toString() {
    return 'messages'
  }
}

class MyApp extends Microcosm {
  constructor() {
    super()
    this.addStore(Messages)
  }
}

Now the Messages store will be responsible for shaping the data kept within the messages key of this app's state.

Requests to change this data can be handled with Actions. An action is simply a function that has been tagged with a unique identifier. The tag module included with Microcosm can do just that:

import Microcosm, { tag } from 'microcosm'

let Actions = {
  createMessage(options) {
    // Here, we are simply returning options. However this
    // gives you an opportunity to modify parameters before they
    // are sent to stores
    return options
  }
}

let Messages = {
  getInitialState() {
    return []
  },
  [Actions.createMessage](oldState, parameters) {
    return oldState.concat(parameters)
  },
  toString() {
    return 'messages'
  }
}

class MyApp extends Microcosm {
  constructor() {
    super()
    this.addStore(Messages)
  }
}

MyApp is now setup to accept actions, filtering them through the Messages store before saving them. More information on how to trigger actions and retrieve state follows.

How Actions work

Actions are simply functions. They must implement a toString method so that stores can know when to respond to them. For those familiar with traditional flux, this toString method replaces the need to maintain constants for each action type.

Fortunately, the tag function makes this quite mangeable:

import tag from 'microcosm/tag'

let Messages = tag({
  create(message) {
    return { message, time: new Date() }
  }
})

tag returns a clone of a given object with toString() methods which returning unique identifiers. This tells the Microcosm how to process actions (and lets them stringify to unique keys in Stores, seen later).

Microcosms implement a send method. This will run execute a given action with an arbitrary number of arguments (following the first).

This works like:

app.send(Messages.create, 'This property will be passed to the dispatcher')

How Stores work

Stores are plain objects. They must implement a getInitialState and toString method. They listen to actions by providing methods at the unique signature of an Action, like:


let MessageStore = {

  getInitialState(seed) {
    return []
  },

  [Messages.create](oldState, message) {
    return oldState.concat(message)
  },

  toString() {
    return 'MessageStore'
  }
}

Each store manages a subset of a global state object owned by an individual Microcosm instance. By returning a new state object within responses to actions, they modify state.

Microcosm will use getInitialState to produce the initial value for the subset a store manages.

Unlike actions, stores must be registered with the system. There are two reason for this. First: to tell Microcosm what Stores should be responsible for managing state. Second: to dictate the priority of dispatcher multicasting (similar to waitFor in the standard Flux dispatcher)

class App extends Microcosm {
  constructor(seed) {
    super(seed)

    // Called first:
    this.addStore(Messages)

    // Called second:
    this.addStore(OtherStoreThatDependsOnMessages)
  }
}

Getting the value out of a store

Microcosms implement a pull method:

app.pull(Store)

This works because the app accesses the internal state object using Store as a key. Since the store implements a toString method, it coerces into the proper key and returns the expected value.

Listening to changes

All Microcosm instances are event emitters. They emit a single change event that you can subscribe to like:

let app = new Microcosm()

// Add a callback
app.listen(callback)

// Remove a callback
app.ignore(callback)

// Force an emission
app.emit()

Booting things up

Microcosm::start begins an application. This will setup initial state, run plugins, then execute a callback:

let app = new Microcosm()

app.start(function() {
  // Now do something
})

Adding plugins

Plugins allow microcosm instances to be extended with additional functionality outside of the scope of the framework. The only requirement is that they take the form of an object with a register method:

let Logger = {

  log(data) {
    console.log('Change: ', data)
  },

  register(app, options, next) {
    app.listen(i => log(app.toJSON()))
  }

}

// The second argument of addPlugin contains options that will be
// sent to the plugin
app.addPlugin(logger, {})

// Start executes all plugins in the order in which they are added
app.start()

Additional Notes

The philosophy behind change management is described very well in the Advanced Performance section of the React docs.

Inspiration

FAQs

Package last updated on 09 Apr 2015

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc