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

skylight

v2.0.2

Published

Realtime windows into your data

Downloads

14

Readme

Skylight

The (Experimental!) Skylight library simplifies the construction of real-time collections.

Build Status

Core Values

Real-time collections are difficult to do well unless you have your priorities set straight.

In order of importance, here are the goals of Skylight:

  1. Correctness

    Skylight is a thin but bulletproof interface between your database and client.

  2. Simplicity

    Simplicity allows consumers of Skylight to create understandable and well behaved collections.

  3. Performance

    Without compromising on correctness or simplicity, Skylight is fast.

Nomenclature

The Skylight class is an extension of Backbone.Collection. Think of Skylight as a window into your data -- you can see your data, but you can't change it. Whereas normal Backbone collections change when you call methods like add and sync, Skylight collections only change when your database changes.

A pair of Manager instances are required. One goes on the server, and the other on the client. They are named ServerManager and ClientManager respectively. The job of the Managers is to bridge the communication gap between server and client.

API

ServerManager

The ServerManager requires more parameters than the ClientManger because it has to communicate with your database.

var serverManager = new ServerManager(
    {
      // Passed directly to your collections to use. Backend Agnostic!
      db: db

      // Must be an EventEmitter that emits 'change' events
      // when a record changes, passing the record as the first argument
    , dbFeed: feed

      // Passed directly to your collections to use, useful for storing user
      // roles and permissions so you can use them in filters
    , context: context

      // A map of Skylight collections like:
      // {CustomCollection: require('./collections/custom')}
    , manifest: manifest
    })

// Socket must be an EventEmitter that emits ClientManager events
serverManager.setClientFeed(socket)

ClientManager

The ClientManager is simple to set up because it simply reflects what the ServerManager sees on the server.


var clientManager = new ClientManager(
    {
      // Socket must be an EventEmitter that emits ServerManager events
      serverFeed: socket
    })

Instance Methods

ClientManager.create(CollectionConstructor, optionsObject)

The create method returns a collection with the specified options. If a duplicate collection already exists, you will get an identical instance. Otherwise, a new instance will be returned.

var myInstance = clientManager.create(MyCollection, {option: 'a'})

Skylight

Skylight uses progressive enhancement to give users some control over how simple or performant they want their collections to be.

The Minimum Collection

Skylight subclasses require at minimum:

  1. An _id property that uniquely identifies this type of collection
  2. A _fetch method that gets the data for the collection
Skylight.extend({
  _id: 'Users'

, _fetch: function (db, context, cb) {

    // `Skylight` does not care what db adapter or backend you use
    // (the db object is whatever you constructed the `ServerManager` with)
    db.query('SELECT * FROM `users`', function (err, data) {

      // If nothing went wrong, call cb with an array of plain objects
      if(err)
        return cb(err)
      else
        return cb(null, data)

    })
  }
})

I was verbose with the above example so I could explain the data that the _fetch callback expects. In reality, your simplest collections would look similar to this:

Skylight.extend({
  _id: 'Users'
, _fetch: function (db, context, cb) {
    db.query('SELECT * FROM `users`', cb)
  }
})

Incremental Updates

Instead of performing a fetch for every change, you can implement an optional _onChange method that is called each time the ServerManager's dbFeed emits a change event.

You can get very creative with your _onChange method, but here is a typical one that has logic for adding, editing, and removing models.

Skylight.extend({
  _id: 'Users'

, _fetch: function (db, context, cb) {
    db.query('SELECT * FROM `users`', cb)
  }

  // `doc` is the `dbFeed` `change` event's first argument
  // here we'll assume that `doc` is a plain object like {id: 'joe', type: 'user'}
, _onChange: function (doc, db, context, cb) {

    // Find an existing model with this id
    var existing = this.get(doc.id)

    // If the document is already in the collection
    if(existing != null) {

      // If the document is deleted, remove it
      if(doc.deleted) {
        this.remove(existing)
      }
      // Otherwise, update the model's attributes
      else {
        existing.set(doc)
      }

    }
    // If the document is not already in the collection, add it if it belongs
    else if(existing.type == 'user') {
      this.add(doc)
    }

    // When done manipulating the collection, call `cb` to flush changes
    cb()
  }
})

Take a little time to understand the annotated example above. The _onChange method is a powerful and unopinionated way to perform incremental updates -- even asynchronous ones!

Since incremental updates are inherently more tricky to get right, here is the above example without the annotations so that you can use it as a template:

Skylight.extend({
  _id: 'Users'

, _fetch: function (db, context, cb) {
    db.query('SELECT * FROM `users`', cb)
  }

, _onChange: function (doc, db, context, cb) {
    var existing = this.get(doc.id)

    if(existing != null) {
      if(doc.deleted)
        this.remove(existing)
      else
        existing.set(doc)
    }
    else if(doc.type == 'user') {
      this.add(doc)
    }

    cb()
  }
})

Instance Methods

Skylight.loaded

The loaded method calls the callback when the collection is done retrieving its initial payload.

instance.loaded(function (err, inst) {
  // err is an Error if there was a problem
  // inst is the collection instance that loaded. in this case, thats `instance`.
})
Skylight.setOptions

This method allows you to change the options of a collection on-the-fly. The callback is called after the options have been set, and the new data has loaded.

instance.setOptions({}, function (err, inst) {
  // err is an Error if there was a problem
  // inst is the collection instance that finished loading. in this case, thats `instance`.
})