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 🙏

© 2025 – Pkg Stats / Ryan Hefner

avers

v0.19.0

Published

Avers.js --------

Downloads

233

Readme

Avers.js

Avers is a JavaScript library which provides basic functionality for web applications which need to manage and synchronize objects with a server.

Motivation

My main goals were:

  • To have a library for the Model layer of an application which can be fully checked by static type checkers.
  • Reduce boilerplate when parsing JSON into proper JavaScript class instances.
  • Abstract all synchronization between client and server behind an efficient API which supports concurrent editing.

Avers is written in TypeScript. It is composed of three parts: Core, Storage and Session. The Core provides a DSL to define object types and their properties. With that information Avers can automatically parse JSON into proper class instances and track changes you make to these objects. The Storage part provides an API to manage these objects and synchronize them with the server. The Session part implements a simple concept of a client session.

Immutable data structures

While I do like immutable datastructures, the current implementations have some serious shortcomings. All libraries which use string arrays as cursors (eg. Immutable.js) are impenetrable for static type checkers. Consider the following example which has a typo in it. Can you spot it? Well, you can't and neither can TypeScript or Flow.

let a = new Immutable.Map();
let b = a.setIn(['users',userId,'age'], 42);

Avers does partially embrace immutable datastructures, but hides them behind an imperative API. Because you have to define all object types and their properties, Avers can automatically construct cursors which are guaranteed to be correct.

Modifying a property on an Avers object is a destructive operation. But those changes can be captured and it's up to you what you do with it. The Storage part for example applies the changes to a fresh copy, so to the outside it looks like the data are immutable.

let userE = Avers.lookupEditable(aversH, '956-userid-37').get(undefined);
if (userE) { // Editable may not be loaded yet on the client.
    userE.content.age = 42;
    Avers.deliverChangeRecords(userE.content);

    let x = Avers.lookupEditable(aversH, '956-userid-37').get(undefined);
    assert(userE != x);
}

Fully typed objects all the way down

The library works particularly well in a TypeScript project. When you add full type annotations to your objects then the compiler can warn you when you try to access or modify non-existing properties. This works even for arbitrary deep structures.

class Author {
    firstName : string;
    lastName  : string;
}
Avers.definePrimitive(Author, 'firstName', '');
Avers.definePrimitive(Author, 'lastName', '');

class Book {
    title  : string;
    author : Author;
}
Avers.definePrimitive(Book, 'title', '');
Avers.defineObject(Book, 'Author', {});

let book = Avers.mk<Book>(Book, {});
book.title = 'A Song of Ice and Fire';
book.author.firstName  = 'George';
book.author.middleName = 'R.R.';
book.author.lastName   = 'Martin';
// TypeScript error: non-existing property 'middleName' on 'Author'.

Object properties can be of four types:

  • Primitive values (string, number, boolean).
  • Variant properties (also sometimes called sum types).
  • Child objects (Avers objects).
  • Collections (arrays of Avers objects).

Requirements

The implementation makes heavy use of modern web technologies, such as Object.observe, Symbol, Map and Set. At this date (5015-07-18) only Chrome supports Object.observe. On other platforms you'll have to use polyfills.

Storage

The Storage part implements functionality to manage and synchronize objects with a compliant server (one implementation is available as a Haskell library library).

All data is managed in a Handle. All IO operations are abstracted away and you have to supply their implementation. The Handle expects API compatible with the whatwg fetch spec for the network interaction. Access to a clock is also abstracted, so you can supply implementation based on Date.now or prformance.now, depending on accuracy requirements.

let infoTable = new Map();
infoTable.set('book', Book);

let h = new Avers.Handle
    ( '//api.domain.tld'
    , window.fetch.bind(window)
    , window.performance.now.bind(window.performance)
    , infoTable
    );

The handle has a generationNumber property. It is a number, incremented every time any data managed by the handle changes. You can attach a watcher to be notified whenever it changes, so you know when to re-render the UI.

The module defines the type Editable<T> which wraps a top-level object with metadata needed for synchronization (such as whether it has been loaded or not, any local changes you have done to the object, or the network request in flight).

Integration with React

Use query functions (such as lookupEditable, lookupContent, lookupPatch) to extract data from the Handle. Rendering in React must be synchronous, so those functions return a Computation. This way you are forced to handle to case when the object is not loaded yet or has failed loading.

The data is fetched automatically in the background when you first try to access it. There is no need to trigger anything from the component lifecycle callbacks (eg. componentDidMount).

Never compare the Avers Handle for equality to decide if you need to re-render a component. Though you can use strict equality comparison on an Editable or its content.

This is a simple React component which shows the summary of a book. It illustrates how to extract data from the Avers Handle.

interface BookSummaryProps {
    aversH : Avers.Handle;
    bookId : string;
}
class BookSummary extends React.Component<BookSummaryProps, {}> {
    render() {
        let { aversH, bookId } = this.props;

        return Avers.lookupEditable<Book>(aversH, bookId).fmap(bookE => {
            var book   = bookE.content
              , author = book.author;

            return (
                <span>
                    {book.title} by {author.firstName} {author.lastName}
                </span>
            );

        ).get(<span>Loading book {bookId}...</span>);
    }
}

At the top level of your React application you need to wire everything up. There is very little boilerplate needed. Note that this minimal example doesn't include routing or how to store and manage data outside of the Avers Handle.

// Create the handle early during the startup.
let aversH = new Avers.Handle(...);

// The callback you want to call whenever the UI needs to be re-rendered. Give
// it the Avers Handle so it can access the data.
function render() {
    React.render(<App aversH={aversH} />, document.body);
}

// Do the initial render on application startup and install a listener on
// the Handle to re-render the UI whenever the data changes.
render();
Avers.attachGenerationListener(aversH, () => {
    render();
});