The future of Avers

Over the last few days I've cleaned up Avers. There was still a bit of code left from the time when I copied parts of the event system of Backbone.js into Avers. The public API has changed slightly, but if you use TypeScript it should be straightforward to see what you need to do.

After finishing that work late at night I was lying in my bed, thinking how to position Avers and where to move next. It works reasonably well for my usecase, but I know of a few features which would make it more appealing to a broader audience.

On JavaScript type systems

A few days ago while reading a discussion on HackerNews I noticed that there is still a group of people who reject the idea of using types (norminal or even just structural) in JavaScript. Which is fine, I'm not judging. But Avers works particularly well when you use it within some form of type system.

If you don't like the idea of using types, you can still use Avers. But you should be prepared to define classes (well, functions) for the different objects which make up the data structures managed by Avers. I think that's a small price to pay for the benefit Avers provides.

More property types

Avers currently supports four property types: primitive (a JavaScript value which Avers doesn't watch, ie. it doesn't emit change events if something within that property changes), object (Avres objects whose changes are tracked), collection (a list of Avers objects) and variant (also called sum type).

It is relatively easy to add new types of properties. For example in a TypeScript project it would be nice to have support for enum properties which are represented as strings in JSON and as TypeScript enums (numbers) at runtime.

I could go even further and allow users to define their own property types. Internally the code is already structured so that it supports arbitrary properties, with the four mentionend earlier being the only ones implemented right now.

Better validation and fatal parse errors

When Avers parses JSON into an object, it initializes properties which are present in the JSON object and leaves the other properties untouched. You can optionally normalize the object by using migrateObject. But at no point does Avers generate a fatal error (ie. throw an exception) if a required property is not present.

I've tried to design my data structures so that they are always valid and can be migrated forward. Sometimes though a fatal error is a better behavior.

Immutable applyOperation

The current implementation of applyOperation modifies the object in-place. Certain JavaScript libraries (and people) prefer working with immutable data structures. I already have an idea how to implement applyOperation so that it returns a copy of the root object and leaves the original untouched.

One good example where this becomes useful is when you have values which are functionally dependent on the data represented by Avers objects. If they are expensive to compute, you want to cache them, but also invalidate when the objects change. With an immutable applyOperation it becomes very easy to manage the caches. You simply cache the value on the object instance. Because applyOperation creates a new instance of the object but only copies the properties Avers knows about, it'll automatically clear the cache.

Here is an example how it would loook in practice. The book has a property wordCount which needs to be updated everytime something in the book (in particular in the pages) changes.

class Page {
    text : string;
}

class Book {
    pages : Avers.Collection<Page>;

    private _wordCount = undefined;
    get wordCount() {
        if (this._wordCount === undefined) {
            this._wordCount = this.pages.reduce((a, page) => {
                return //...
            }, 0);
        }
        return this._wordCount;
    }
}

Synthetic change records

Avers generates two types of change records: set and splice. These map directly to the change records generated by the underlying Object.observe mechanism. If you want to use Avers in the context of an OT framework, it might be desireable to be able to emit your own types of change records. ShareJS for example supports many more operations.