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

delta-crdts

v0.10.3

Published

Delta-based State CRDTs

Downloads

65

Readme

delta-crdts

Delta state-based CRDTs in Javascript.

Install

$ npm install delta-crdts

Import

const CRDTs = require('delta-crdts')

Instantiate a type

const type = 'rga' // or any of the other supported CRDT types
const Type = CRDT(type)

Create a replica

To create a replica you need pass in a unique node id.

const replica = Type('node id')

Mutate that replica

const deltas = []
deltas.push(replica.push('some value'))
deltas.push(replica.insertAt(0, 'some other value'))

Create a second replica

const replica2 = Type('node id 2')

Apply the deltas

deltas.forEach((delta) => replica2.apply(delta))

Query the value

replica2.value() // ['some value', 'some other value']

Initialize a replica from the entire state

const replica3 = Type('node id 3')
replica3.apply(replica2.state())

Conflict management

You can do concurrent edits on both replicas:

// create 2 replicas
const replicas = [Type('id1'), Type('id2')]

// create concurrent deltas
const deltas = [[], []]

deltas[0].push(replicas[0].push('a'))
deltas[0].push(replicas[0].push('b'))

deltas[1].push(replicas[1].push('c'))
deltas[1].push(replicas[1].push('d'))

deltas[0].forEach((delta) => replicas[1].apply(delta))
deltas[1].forEach((delta) => replicas[0].apply(delta))

assert.deepEqual(replicas[0].value(), replicas[1].value())

Extend

You can extend the types, creating your own CRDT.

Example:

const Zero = {
  initial: () => 0,
  join: (s1, s2) => 0,
  value: (state) => state,
  mutators: {
    doSomething (id, state, arg1) => {
      // example mutator, returning a delta
      return 0
    }
  }
}

CRDT.define('zero', Zero)

// now you can use it

const replica = CRDT('zero')('node id')

Support for incremental value computation

It's possible to allow types to have incremental value computation. If a type supports that, the value is incrementally computed on each delta that is applied.

To add support for incremental value computation to a CRDT, the type definition should support the following function:

Type.incrementalValue = function (beforeState, newState, delta, cache = { value: <some initial value>, ... }) {
  // ...
}

As an example you can get inspiration from the RGA implementation.

Types

The following types are built-in:

(* means that the type is causal and can be embedded in an ORMap)

Counters

| Name | Identifier | Mutators | Value Type | |------|------------|----------|------------| | Increment-only Counter| gcounter | .inc() | int | | PN-Counter | pncounter | .inc(),.dec() | int | | Lex-Counter | lexcounter | .inc(),.dec() | int | | Causal Counter *| ccounter | .inc(),.dec() | int |

Flags

| Name | Identifier | Mutators | Value Type | |------|------------|----------|------------| | Enable-Wins Flag *| ewflag | .enable(), .disable() | Boolean | | Disable-Wins Flag *| dwflag | .enable(), .disable() | Boolean |

Sets

| Name | Identifier | Mutators | Value Type | |------|------------|----------|------------| | Grow-Only Set | gset | .add(element) | Set | | Two-Phase Set | 2pset | .add(element), .remove(element) | Set | | Add-Wins-Observed-Remove Set *| aworset | .add(element), .remove(element) | Set | | Remove-Wins-Observed-Remove Set *| rworset | .add(element), .remove(element) | Set | | Remove-Wins-Last-Write-Wins Set | rwlwwset | .add(element), .remove(element) | Set |

Arrays

| Name | Identifier | Mutators | Value Type | |------|------------|----------|------------| | Replicable Growable Array | rga | .push(element), .insertAt(pos, element), .removeAt(pos), updateAt(pos, element), insertAllAt(pos, elements) | Array |

Registers

| Name | Identifier | Mutators | Value Type | |------|------------|----------|------------| | Last-Write-Wins Register | lwwreg | .write(value) | Value | | Multi-Value Register *| mvreg | .write(value) | Set of concurrent values |

Maps

| Name | Identifier | Mutators | Value Type | |------|------------|----------|------------| | Observed-Remove Map *| ormap | .remove(key), applySub(key, crdt_name, mutator_name, ...args) | Object |

Embedding CRDTs in ORMaps

OR-Maps support embedding of other causal CRDTs. Example:

const ORMap = CRDT('ormap')
const m = ORMap('id1')
const delta = m.applySub('a', 'mvreg', 'write', 'A')
console.log(m.value()) // => {a: new Set(['A'])}

Of this collection, causal CRDTs are:

  • AWORSet
  • CCounter
  • DWFlag
  • EWFlag
  • MVReg
  • ORMap
  • RWORSet

Sets, uniqueness and object id

For testing uniqueness in a way that is safe when replicas are distributed, for objects we calculate the hash using the hast-it package.

If you want, you can override it by providing a hashCode attribute in your object.

For all objects where typeof object !== 'object', we use the value itself as comparison.

Static methods

You may get the static definition of a type by doing

const type = CRDT.type(typeName)

Each type has a series of static methods may need to use:

Type.initial()

Returns the initial state for the type. Example:

const GCounter = CRDT.type('gcounter')
const initial = GCounter.initial()

Type.value(state)

Returns the view value of a given state.

Type.join(s1, s2)

Joins two states (or deltas) and returns the result.

const GCounter = CRDT.type('gcounter')

const state = GCounter.join(delta1, delta)

const value = GCounter.value(state)

Example of using static methods:

const GCounter = CRDT('gcounter')

deltas = []
deltas.push(replica1.inc())
deltas.push(replica1.inc())
deltas.push(replica1.inc())

const bigDelta = deltas.reduce(GCounter.join, GCounter.initial())

replica2.apply(bigDelta)

License

MIT