Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

crdts

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

crdts - npm Package Compare versions

Comparing version 0.1.3 to 0.1.4

2

package.json
{
"name": "crdts",
"version": "0.1.3",
"version": "0.1.4",
"description": "A CRDT Library for JavaScript",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -42,15 +42,9 @@ # CRDTs

```
+------------------------------------------------------------+
| || || || || |
| OR-Set || LWW-Set || 2P-Set || G-Set || G-Counter |
| || || || || |
+-------------------------| || +------------+
| || || |
| CmRDT-Set || || |
| || || |
+-----------------------------------------------+
| |
| G-Set |
| |
+-----------------------------------------------+
+-----------++-----------++----------++---------++------------+
Data Type | OR-Set || LWW-Set || 2P-Set || G-Set || G-Counter |
+-----------++-----------++----------++---------++------------+
Base Class | CmRDT-Set | -- |
|-----------------------------------------------+-------------+
CRDT Type | Operation-Based | State-based |
+-----------------------------------------------+-------------+
```

@@ -57,0 +51,0 @@

@@ -11,4 +11,4 @@ 'use strict'

*
* See base class G-Set.js for the rest of the API
* https://github.com/orbitdb/crdts/blob/master/src/G-Set.js
* See base class CmRDT-Set.js for the rest of the API
* https://github.com/orbitdb/crdts/blob/master/src/CmRDT-Set.js
*

@@ -19,3 +19,3 @@ * Sources:

*/
class TwoPSet extends GSet {
class TwoPSet extends CRDTSet {
/**

@@ -39,4 +39,7 @@ * Create a new TwoPSet instance

*/
get values () {
// Include elements that are in the add set but not in the remove set
values () {
// A value is included in the set if it's present in
// the add set and not present in the remove set. We can
// determine this by calculating the difference between
// adds and removes.
const difference = GSet.difference(this._added, this._removed)

@@ -43,0 +46,0 @@ return difference.values()

'use strict'
const GSet = require('./G-Set')
const { OperationTuple3 } = require('./utils')

@@ -9,7 +8,7 @@

*
* Base Class for Operation-Based Set CRDT
* Base Class for Operation-Based Set CRDT. Provides a Set interface.
*
* Inherits from GSet. Operations are described as:
* Operations are described as:
*
* Operation = Tuple3(value : Any, added : Set, removed : Set)
* Operation = Tuple3(value : Any, added : Set, removed : Set)
*

@@ -22,5 +21,6 @@ * This class is meant to be used as a base class for

* Used by:
* OR-Set - https://github.com/orbitdb/crdts/blob/master/src/OR-Set.js
* 2P-Set - https://github.com/orbitdb/crdts/blob/master/src/2P-Set.js
* LWW-Set - https://github.com/orbitdb/crdts/blob/master/LWW-Set.js
* G-Set - https://github.com/orbitdb/crdts/blob/master/src/G-Set.js
* OR-Set - https://github.com/orbitdb/crdts/blob/master/src/OR-Set.js
* 2P-Set - https://github.com/orbitdb/crdts/blob/master/src/2P-Set.js
* LWW-Set - https://github.com/orbitdb/crdts/blob/master/LWW-Set.js
*

@@ -31,3 +31,3 @@ * Sources:

*/
class CmRDTSet extends GSet {
class CmRDTSet extends Set {
/**

@@ -37,4 +37,2 @@ * Create a new CmRDTSet instance

*
* A CmRDT Set inherits from GSet (Grow-Only Set)
*
* The constructor should never be used directly

@@ -44,7 +42,9 @@ * but rather via `super()` call in the constructor of

*
* @param {[Iterable]} iterable [Opetional Iterable object (eg. Array, Set) to create the GSet from]
* @param {[Iterable]} iterable [Opetional Iterable object (eg. Array, Set) to create the Set from]
* @param {[Object]} options [Options to pass to the Set. Currently supported: `{ compareFunc: (a, b) => true|false }`]
*/
constructor (iterable, options) {
super(iterable)
super()
// Internal cache for tracking which values have been added to the set
this._values = new Set()
// List of operations (adds or removes of a value) for this set as

@@ -65,10 +65,10 @@ // Operation : Tuple3(value : Any, added : Set, removed : Set)

*/
get values () {
const contains = e => this._resolveState(e.added, e.removed, this._options.compareFunc)
values () {
const shouldIncludeValue = e => this._resolveValueState(e.added, e.removed, this._options.compareFunc)
const getValue = e => e.value
// Filter out values that should not be in this set
// by using the _resolveState() function to determine
// by using the _resolveValueState() function to determine
// if the value should be present
const state = this._operations
.filter(contains)
.filter(shouldIncludeValue)
.map(getValue)

@@ -79,2 +79,21 @@ return new Set(state).values()

/**
* Check if this Set has a value
* @param {[Any]} value [Value to look for]
* @return {Boolean} [True if value is in the Set, false if not]
*/
has (value) {
return new Set(this.values()).has(value)
}
/**
* Check if this Set has all values of an input array
* @param {[Array]} values [Values that should be in the Set]
* @return {Boolean} [True if all values are in the Set, false if not]
*/
hasAll (values) {
const contains = e => this.has(e)
return values.every(contains)
}
/**
* Add a value to the Set

@@ -88,12 +107,14 @@ * @override

*
* @param {[Any]} element [Value to add to the Set]
* @param {[Any]} tag [Optional tag for this add operation, eg. a clock]
* @param {[Any]} value [Value to add to the Set]
* @param {[Any]} tag [Optional tag for this add operation, eg. a clock]
*/
add (element, tag = 0) {
if (!this._values.has(element)) {
const operation = OperationTuple3.create(element, [tag], null)
this._addOperation(operation)
add (value, tag) {
// If the value is not in the set yet
if (!this._values.has(value)) {
// Create an operation for the value and apply it to this set
const addOperation = OperationTuple3.create(value, [tag], null)
this._applyOperation(addOperation)
} else {
const elm = this._findElement(element)
elm.added.add(tag)
// If the value is in the set, add a tag to its added set
this._findOperationsFor(value).map(val => val.added.add(tag))
}

@@ -111,10 +132,9 @@ }

*
* @param {[Any]} element [Value to remove from the Set]
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock]
* @param {[Any]} value [Value to remove from the Set]
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock]
*/
remove (element, tag = 0) {
if (this._values.has(element)) {
const elm = this._findElement(element)
elm.removed.add(tag)
}
remove (value, tag) {
// Add a remove tag to the value's removed set, and only
// apply the remove operation if the value was added previously
this._findOperationsFor(value).map(e => e.removed.add(tag))
}

@@ -128,11 +148,15 @@

merge (other) {
other._operations.forEach(element => {
const value = element.value
other._operations.forEach(operation => {
const value = operation.value
if (!this._values.has(value)) {
const operation = OperationTuple3.create(value, element.added, element.removed)
this._addOperation(operation)
// If we don't have the value yet, add it with all tags from other's operation
const op = OperationTuple3.create(value, operation.added, operation.removed)
this._applyOperation(op)
} else {
const elm = this._findElement(value)
element.added.forEach(e => elm.added.add(e))
element.removed.forEach(e => elm.removed.add(e))
// If this set has the value
this._findOperationsFor(value).map(op => {
// Add all add and remove tags from other's operations to value in this set
operation.added.forEach(e => op.added.add(e))
operation.removed.forEach(e => op.removed.add(e))
})
}

@@ -143,3 +167,35 @@ })

/**
* _resolveState function is used to determine if an element is present in a Set.
* CmRDT-Set as an Object that can be JSON.stringified
* @return {[Object]} [Object in the shape of `{ values: [ { value: <value>, added: [<tags>], removed: [<tags>] } ] }`]
*/
toJSON () {
const values = this._operations.map(e => {
return {
value: e.value,
added: Array.from(e.added),
removed: Array.from(e.removed),
}
})
return { values: values }
}
/**
* Create an Array of the values of this Set
* @return {[Array]} [Values of this Set as an Array]
*/
toArray () {
return Array.from(this.values())
}
/**
* Check if this Set equal another Set
* @param {[type]} other [Set to compare]
* @return {Boolean} [True if this Set is the same as the other Set]
*/
isEqual (other) {
return CmRDTSet.isEqual(this, other)
}
/**
* _resolveValueState function is used to determine if an element is present in a Set.
*

@@ -159,6 +215,7 @@ * It receives a Set of add tags and a Set of remove tags for an element as arguments.

*/
_resolveState (added, removed, compareFunc) {
_resolveValueState (added, removed, compareFunc) {
// By default, if there's an add operation present,
// we include the value in the set
return added.size > 0
// and there are no remove operations, we include
// the value in the set
return added.size > 0 && removed.size === 0
}

@@ -170,3 +227,3 @@

*/
_addOperation (operationTuple3) {
_applyOperation (operationTuple3) {
this._operations.push(operationTuple3)

@@ -177,13 +234,62 @@ this._values.add(operationTuple3.value)

/**
* Find a value from this set's internal cache
* Find a value and its operations from this set's internal cache
* @private
*
* Returns a value and all its operations as a OperationTuple3:
* Operation : Tuple3(value : Any, added : Set, removed : Set)
*
* Where 'value' is the value in the set, 'added' is all add operations
* and 'removed' are all remove operations for that value.
*
* @param {[Any]} value [Value to find]
* @return {[Any]} [Value if found, undefined if value was not found]
*/
_findElement (value) {
const compare = e => e.value === value
return this._operations.find(compare)
_findOperationsFor (value) {
let operations = []
if (this._values.has(value)) {
const isForValue = e => e.value === value
const notNull = e => e !== undefined
operations = [this._operations.find(isForValue)].filter(notNull)
}
return operations
}
/**
* Create Set from a json object
* @param {[Object]} json [Input object to create the Set from. Needs to be: '{ values: [] }']
* @return {[Set]} [new Set instance]
*/
static from (json) {
return new CmRDTSet(json.values)
}
/**
* Check if two Set are equal
*
* Two Set are equal if they both contain exactly
* the same values.
*
* @param {[Set]} a [Set to compare]
* @param {[Set]} b [Set to compare]
* @return {Boolean} [True input Set are the same]
*/
static isEqual (a, b) {
return (a.toArray().length === b.toArray().length)
&& a.hasAll(b.toArray())
}
/**
* Return the difference between the values of two Sets
*
* @param {[Set]} a [First Set]
* @param {[Set]} b [Second Set]
* @return {[Set]} [Set of values that are in Set A but not in Set B]
*/
static difference (a, b) {
const otherDoesntInclude = x => !b.has(x)
const difference = new Set([...a.values()].filter(otherDoesntInclude))
return difference
}
}
module.exports = CmRDTSet
'use strict'
const CRDTSet = require('./CmRDT-Set')
/**

@@ -8,8 +10,10 @@ * G-Set

*
* The G-Set works as a base class for many other Set CRDTs.
* G stands for "Grow-Only" which means that values can only
* ever be added to the set, they can never be removed.
*
* See base class CmRDT-Set.js for the rest of the API
* https://github.com/orbitdb/crdts/blob/master/src/CmRDT-Set.js
*
* Used by:
* CmRDT-Set - https://github.com/orbitdb/crdts/blob/master/src/CmRDT-Set.js
* 2P-Set - https://github.com/orbitdb/crdts/blob/master/src/2P-Set.js
*

@@ -20,3 +24,3 @@ * Sources:

*/
class GSet {
class GSet extends CRDTSet {
/**

@@ -27,2 +31,3 @@ * Create a G-Set CRDT instance

constructor (iterable) {
super()
this._values = new Set(iterable)

@@ -35,3 +40,3 @@ }

*/
get values () {
values () {
return this._values.values()

@@ -56,3 +61,3 @@ }

remove (value) {
throw new Error('G-Set doesn\'t allow removing values')
throw new Error(`G-Set doesn't allow removing values`)
}

@@ -70,23 +75,2 @@

/**
* Check if this GSet has a value
* @param {[Any]} value [Value to look for]
* @return {Boolean} [True if value is in the GSet, false if not]
*/
has (value) {
return new Set(this.values).has(value)
}
/**
* Check if this GSet has all values of an input array
* @param {[Array]} values [Values that should be in the GSet]
* @return {Boolean} [True if all values are in the GSet, false if not]
*/
hasAll (values) {
const contains = e => this.has(e)
return values.length > 0
? values.every(contains)
: this._values.size === 0
}
/**
* GSet as an Object that can be JSON.stringified

@@ -102,19 +86,2 @@ * @return {[Object]} [Object in the shape of `{ values: [<values>] }`]

/**
* Create an Array of the values of this GSet
* @return {[Array]} [Values of this GSet as an Array]
*/
toArray () {
return Array.from(this.values)
}
/**
* Check if this GSet equal another GSet
* @param {[type]} other [GSet to compare]
* @return {Boolean} [True if this GSet is the same as the other GSet]
*/
isEqual (other) {
return GSet.isEqual(this, other)
}
/**
* Create GSet from a json object

@@ -127,32 +94,4 @@ * @param {[Object]} json [Input object to create the GSet from. Needs to be: '{ values: [] }']

}
/**
* Check if two GSets are equal
*
* Two GSet are equal if they both contain exactly
* the same values.
*
* @param {[GSet]} a [GSet to compare]
* @param {[GSet]} b [GSet to compare]
* @return {Boolean} [True input GSets are the same]
*/
static isEqual (a, b) {
return (a.toArray().length === b.toArray().length)
&& a.hasAll(b.toArray())
}
/**
* Return the difference between the values of two GSets
*
* @param {[GSet]} a [First GSet]
* @param {[GSet]} b [Second GSet]
* @return {[Set]} [Set of values that are in GSet A but not in GSet B]
*/
static difference (a, b) {
const otherIncludes = x => !b.has(x)
const difference = new Set(a.toArray().filter(otherIncludes))
return difference
}
}
module.exports = GSet
// Export all supported CRDTs
exports.GCounter = GCounter = require('./G-Counter.js')
exports.GSet = GSet = require('./G-Set.js')
exports.TwoPSet = TwoPSet = require('./2P-Set.js')
exports.ORSet = ORSet = require('./OR-Set.js')
exports.LWWSet = LWWSet = require('./LWW-Set.js')
exports.GCounter = GCounter = require('./G-Counter')
exports.CmRDTSet = CmRDTSet = require('./CmRDT-Set')
exports.GSet = GSet = require('./G-Set')
exports.TwoPSet = TwoPSet = require('./2P-Set')
exports.ORSet = ORSet = require('./OR-Set')
exports.LWWSet = LWWSet = require('./LWW-Set')
exports.PNCounter = PNCounter = require('./PN-Counter.js')

@@ -23,2 +24,4 @@

GCounter: GCounter,
PNCounter: PNCounter,
CmRDTSet: CmRDTSet,
GSet: GSet,

@@ -28,3 +31,2 @@ TwoPSet: TwoPSet,

LWWSet: LWWSet,
PNCounter: PNCounter
}

@@ -21,3 +21,3 @@ 'use strict'

*
* _resolveState function is used to determine if an element is present in a Set.
* _resolveValueState function is used to determine if an element is present in a Set.
*

@@ -37,3 +37,3 @@ * It receives a Set of add tags and a Set of remove tags for an element as arguments.

*/
_resolveState (added, removed, compareFunc) {
_resolveValueState (added, removed, compareFunc) {
// Sort both sets with the given comparison function

@@ -40,0 +40,0 @@ // or use "distance" sort by default

@@ -30,10 +30,10 @@ 'use strict'

*
* @param {[Any]} element [Value to remove from the Set]
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock]
* @param {[Any]} value [Value to remove from the Set]
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock]
*/
remove (element) {
if (this._values.has(element)) {
const elm = this._findElement(element)
elm.removed = new Set(elm.added)
}
remove (value) {
// Add all observed (known) add tags to the removed tags
const removeObserved = e => e.removed = new Set([...e.added, ...e.removed])
// Create a remove operation for the value if it exists
this._findOperationsFor(value).map(removeObserved)
}

@@ -44,3 +44,3 @@

*
* _resolveState function is used to determine if an element is present in a Set.
* _resolveValueState function is used to determine if an element is present in a Set.
*

@@ -60,23 +60,18 @@ * It receives a Set of add tags and a Set of remove tags for an element as arguments.

*/
_resolveState (added, removed, compareFunc) {
_resolveValueState (added, removed, compareFunc) {
// Check if a tag is included in the remove set
const removesDontIncludeGreater = e => {
const hasMatchingRemoveOperation = addTag => {
// Check if remove tags includes the add tag, ie. check for
// equality for the tags using a provided comparison function
if (compareFunc) {
// If remove set has any elements, check it
if (removed.size > 0) {
// If remove set doesn't include the tag,
// return true to include the value in the state
return Array.from(removed).some(f => compareFunc(e, f) === -1)
}
return !Array.from(removed).some(removeTag => compareFunc(removeTag, addTag))
}
return true
}
// If remove set doesn't have the tag,
// return true to include the value in the state
return !removed.has(e)
return !removed.has(addTag)
}
// If the remove set doesn't include the add tag,
// return true to include the value in the state
return Array.from(added).filter(removesDontIncludeGreater).length > 0
return Array.from(added).filter(hasMatchingRemoveOperation).length > 0
}

@@ -83,0 +78,0 @@

'use strict'
const assert = require('assert')
const { GSet, TwoPSet } = require('../src')
const { TwoPSet, GSet, CmRDTSet } = require('../src')

@@ -10,21 +10,24 @@ describe('2P-Set', () => {

it('creates a set', () => {
const gset = new TwoPSet()
assert.notEqual(gset, null)
assert.notEqual(gset._added, null)
assert.equal(gset._added instanceof GSet, true)
assert.notEqual(gset._removed, null)
assert.equal(gset._removed instanceof GSet, true)
const crdt = new TwoPSet()
assert.notEqual(crdt, null)
})
it('is a GSet', () => {
const gset = new TwoPSet()
assert.notEqual(gset._values, null)
assert.equal(gset._values instanceof Set, true)
it('has two GSets', () => {
const crdt = new TwoPSet()
assert.notEqual(crdt._added, null)
assert.equal(crdt._added instanceof GSet, true)
assert.notEqual(crdt._removed, null)
assert.equal(crdt._removed instanceof GSet, true)
})
it('is a CmRDT Set', () => {
const crdt = new TwoPSet()
assert.equal(crdt instanceof CmRDTSet, true)
})
it('creates a set from values', () => {
const gset = new TwoPSet(['A', 'B'])
assert.notEqual(gset, null)
assert.equal(gset._added instanceof GSet, true)
assert.deepEqual(new Set(gset._added.values), new Set(['B', 'A']))
const crdt = new TwoPSet(['A', 'B'])
assert.notEqual(crdt, null)
assert.equal(crdt._added instanceof GSet, true)
assert.deepEqual(new Set(crdt._added.values()), new Set(['B', 'A']))
})

@@ -35,11 +38,11 @@ })

it('is an Iterator', () => {
const gset = new TwoPSet()
assert.equal(gset.values.toString(), '[object Set Iterator]')
const crdt = new TwoPSet()
assert.equal(crdt.values().toString(), '[object Set Iterator]')
})
it('returns an Iterator', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
const iterator = gset.values
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
const iterator = crdt.values()
assert.equal(iterator.next().value, 'A')

@@ -52,26 +55,26 @@ assert.equal(iterator.next().value, 'B')

it('adds an element to the set', () => {
const gset = new TwoPSet()
gset.add('A')
assert.deepEqual(new Set(gset.values), new Set(['A']))
const crdt = new TwoPSet()
crdt.add('A')
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('adds three elements to the set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.add('C')
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C']))
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C']))
})
it('contains only unique values', () => {
const gset = new TwoPSet()
gset.add(1)
gset.add('A')
gset.add('A')
gset.add(1)
gset.add('A')
const crdt = new TwoPSet()
crdt.add(1)
crdt.add('A')
crdt.add('A')
crdt.add(1)
crdt.add('A')
const obj = { hello: 'ABC' }
gset.add(obj)
gset.add(obj)
gset.add({ hello: 'ABCD' })
crdt.add(obj)
crdt.add(obj)
crdt.add({ hello: 'ABCD' })

@@ -85,3 +88,3 @@ const expectedResult = [

assert.deepEqual(new Set(gset.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})

@@ -93,6 +96,6 @@

const expectedResult = ['B', 'C', 0, 4]
const gset = new TwoPSet(addedValues, removedValues)
assert.equal(gset._added instanceof GSet, true)
assert.deepEqual(new Set(gset._added.values), new Set(addedValues))
assert.deepEqual(new Set(gset.values), new Set(expectedResult))
const crdt = new TwoPSet(addedValues, removedValues)
assert.equal(crdt._added instanceof GSet, true)
assert.deepEqual(new Set(crdt._added.values()), new Set(addedValues))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})

@@ -103,14 +106,14 @@ })

it('removes an element from the set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.remove('A')
assert.deepEqual(new Set(gset.values), new Set([]))
const crdt = new TwoPSet()
crdt.add('A')
crdt.remove('A')
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('element can only be removed if it is present in the set', () => {
const gset = new TwoPSet()
gset.remove('A')
gset.add('A')
assert.deepEqual(new Set(gset._removed.values), new Set([]))
assert.deepEqual(new Set(gset.values), new Set(['A']))
const crdt = new TwoPSet()
crdt.remove('A')
crdt.add('A')
assert.deepEqual(new Set(crdt._removed.values()), new Set([]))
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})

@@ -122,17 +125,17 @@

const expectedResult = ['B', 'C', 0, 4]
const gset = new TwoPSet(addedValues, removedValues)
assert.equal(gset._removed instanceof GSet, true)
assert.deepEqual(new Set(gset._removed.values), new Set(removedValues))
assert.deepEqual(new Set(gset.values), new Set(expectedResult))
const crdt = new TwoPSet(addedValues, removedValues)
assert.equal(crdt._removed instanceof GSet, true)
assert.deepEqual(new Set(crdt._removed.values()), new Set(removedValues))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})
it('adds removed elements to the internal removed set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.remove('A')
gset.remove('B')
gset.remove('A')
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.remove('A')
crdt.remove('B')
crdt.remove('A')
const expectedResult = ['A', 'B']
assert.deepEqual(new Set(gset._removed.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt._removed.values()), new Set(expectedResult))
})

@@ -143,73 +146,73 @@ })

it('merges two sets with same id', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset2.add('B')
gset2.add('C')
gset2.add('D')
gset2.remove('D')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['A', 'B', 'C'])
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt2.add('B')
crdt2.add('C')
crdt2.add('D')
crdt2.remove('D')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C'])
})
it('merges two sets with same values', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset2.add('B')
gset2.add('A')
gset2.remove('B')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['A'])
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt2.add('B')
crdt2.add('A')
crdt2.remove('B')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A'])
})
it('merges two sets with removed values', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset1.remove('A')
gset2.remove('A')
gset2.add('A')
gset1.add('AAA')
gset2.add('AAA')
gset1.add('A')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['AAA'])
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt1.remove('A')
crdt2.remove('A')
crdt2.add('A')
crdt1.add('AAA')
crdt2.add('AAA')
crdt1.add('A')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['AAA'])
})
it('merge four different sets', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
const gset3 = new TwoPSet()
const gset4 = new TwoPSet()
gset1.add('A')
gset2.add('B')
gset3.add('C')
gset4.add('D')
gset2.add('BB')
gset2.remove('BB')
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
const crdt3 = new TwoPSet()
const crdt4 = new TwoPSet()
crdt1.add('A')
crdt2.add('B')
crdt3.add('C')
crdt4.add('D')
crdt2.add('BB')
crdt2.remove('BB')
gset1.merge(gset2)
gset1.merge(gset3)
gset1.remove('C')
gset1.merge(gset4)
crdt1.merge(crdt2)
crdt1.merge(crdt3)
crdt1.remove('C')
crdt1.merge(crdt4)
assert.deepEqual(gset1.toArray(), ['A', 'B', 'D'])
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'D'])
})
it('doesn\'t overwrite other\'s values on merge', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset2.add('C')
gset2.add('B')
gset2.remove('C')
gset1.merge(gset2)
gset1.add('AA')
gset2.add('CC')
gset2.add('BB')
gset2.remove('CC')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(gset2.toArray(), ['B', 'BB'])
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt2.add('C')
crdt2.add('B')
crdt2.remove('C')
crdt1.merge(crdt2)
crdt1.add('AA')
crdt2.add('CC')
crdt2.add('BB')
crdt2.remove('CC')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(crdt2.toArray(), ['B', 'BB'])
})

@@ -220,24 +223,24 @@ })

it('returns true if given element is in the set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.add(1)
gset.add(13)
gset.remove(13)
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.add(1)
crdt.add(13)
crdt.remove(13)
const obj = { hello: 'world' }
gset.add(obj)
assert.equal(gset.has('A'), true)
assert.equal(gset.has('B'), true)
assert.equal(gset.has(1), true)
assert.equal(gset.has(13), false)
assert.equal(gset.has(obj), true)
crdt.add(obj)
assert.equal(crdt.has('A'), true)
assert.equal(crdt.has('B'), true)
assert.equal(crdt.has(1), true)
assert.equal(crdt.has(13), false)
assert.equal(crdt.has(obj), true)
})
it('returns false if given element is not in the set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.add('nothere')
gset.remove('nothere')
assert.equal(gset.has('nothere'), false)
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.add('nothere')
crdt.remove('nothere')
assert.equal(crdt.has('nothere'), false)
})

@@ -248,19 +251,19 @@ })

it('returns true if all given elements are in the set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.remove('C')
gset.add('D')
assert.equal(gset.hasAll(['D', 'A', 'B']), true)
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.remove('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'B']), true)
})
it('returns false if any of the given elements are not in the set', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.remove('C')
gset.add('D')
assert.equal(gset.hasAll(['D', 'A', 'C', 'B']), false)
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.remove('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), false)
})

@@ -271,4 +274,4 @@ })

it('returns the values of the set as an Array', () => {
const gset = new TwoPSet()
const array = gset.toArray()
const crdt = new TwoPSet()
const array = crdt.toArray()
assert.equal(Array.isArray(array), true)

@@ -278,8 +281,8 @@ })

it('returns values', () => {
const gset = new TwoPSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.remove('C')
const array = gset.toArray()
const crdt = new TwoPSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.remove('C')
const array = crdt.toArray()
assert.equal(array.length, 2)

@@ -293,25 +296,25 @@ assert.equal(array[0], 'A')

it('returns the set as JSON object', () => {
const gset = new TwoPSet()
gset.add('A')
assert.equal(gset.toJSON().values.added.length, 1)
assert.equal(gset.toJSON().values.added[0], 'A')
assert.equal(gset.toJSON().values.removed.length, 0)
gset.remove('A')
assert.equal(gset.toJSON().values.removed.length, 1)
assert.equal(gset.toJSON().values.removed[0], 'A')
const crdt = new TwoPSet()
crdt.add('A')
assert.equal(crdt.toJSON().values.added.length, 1)
assert.equal(crdt.toJSON().values.added[0], 'A')
assert.equal(crdt.toJSON().values.removed.length, 0)
crdt.remove('A')
assert.equal(crdt.toJSON().values.removed.length, 1)
assert.equal(crdt.toJSON().values.removed[0], 'A')
})
it('returns a JSON object after a merge', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset2.add('B')
gset2.remove('B')
gset1.merge(gset2)
gset2.merge(gset1)
assert.equal(gset1.toJSON().values.added.length, 2)
assert.equal(gset1.toJSON().values.added[0], 'A')
assert.equal(gset1.toJSON().values.added[1], 'B')
assert.equal(gset1.toJSON().values.removed.length, 1)
assert.equal(gset1.toJSON().values.removed[0], 'B')
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt2.add('B')
crdt2.remove('B')
crdt1.merge(crdt2)
crdt2.merge(crdt1)
assert.equal(crdt1.toJSON().values.added.length, 2)
assert.equal(crdt1.toJSON().values.added[0], 'A')
assert.equal(crdt1.toJSON().values.added[1], 'B')
assert.equal(crdt1.toJSON().values.removed.length, 1)
assert.equal(crdt1.toJSON().values.removed[0], 'B')
})

@@ -322,24 +325,24 @@ })

it('returns true for sets with same values', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset2.add('A')
assert.equal(gset1.isEqual(gset2), true)
assert.equal(gset2.isEqual(gset1), true)
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt2.add('A')
assert.equal(crdt1.isEqual(crdt2), true)
assert.equal(crdt2.isEqual(crdt1), true)
})
it('returns true for empty sets', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
assert.equal(gset1.isEqual(gset2), true)
assert.equal(gset2.isEqual(gset1), true)
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
assert.equal(crdt1.isEqual(crdt2), true)
assert.equal(crdt2.isEqual(crdt1), true)
})
it('returns false for sets with different values', () => {
const gset1 = new TwoPSet()
const gset2 = new TwoPSet()
gset1.add('A')
gset2.add('B')
assert.equal(gset1.isEqual(gset2), false)
assert.equal(gset2.isEqual(gset1), false)
const crdt1 = new TwoPSet()
const crdt2 = new TwoPSet()
crdt1.add('A')
crdt2.add('B')
assert.equal(crdt1.isEqual(crdt2), false)
assert.equal(crdt2.isEqual(crdt1), false)
})

@@ -346,0 +349,0 @@ })

@@ -7,2 +7,3 @@ 'use strict'

const { GCounter, GSet, TwoPSet, ORSet, LWWSet } = CRDTs
const CmRDTSet = require('../src/CmRDT-Set')

@@ -52,2 +53,35 @@ const added = [1, 2, 3]

{
type: 'CmRDT-Set',
class: CmRDTSet,
added: added,
removed: removed,
diff: diff,
inputData: {
values: [
{
value: 'A',
added: [1],
removed: [],
},
{
value: 'B',
added: [1],
removed: [1],
},
{
value: 'C',
added: [1, 2],
removed: [2, 3],
},
],
},
expectedValues: ['A'],
expectedValuesWithDiff: added.slice(1, added.length).concat(diff),
create: (input) => new CmRDTSet(input && input.values ? input.values : []),
from: (json) => new CmRDTSet(json && json.values ? json.values : []),
remove: (crdt, tag) => crdt.remove(tag),
isEqual: (a, b) => CmRDTSet.isEqual(a, b),
difference: (a, b) => CmRDTSet.difference(a, b),
},
{
type: 'OR-Set',

@@ -124,16 +158,48 @@ class: ORSet,

it('creates a new ' + CRDT.type + ' from a JSON object', () => {
const orset1 = CRDT.create(CRDT.inputData)
const orset2 = CRDT.from(CRDT.inputData)
assert.deepEqual(new Set(orset2.values), new Set(CRDT.expectedValues))
const crdt1 = CRDT.create(CRDT.inputData)
const crdt2 = CRDT.from(CRDT.inputData)
assert.deepEqual(new Set(crdt2.values()), new Set(CRDT.expectedValues))
})
it('is a Set', () => {
const crdt = CRDT.create()
assert.equal(crdt instanceof Set, true)
})
it('provides a JSON object and creates itself from it', () => {
const crdt1 = CRDT.create()
crdt1.add('A')
crdt1.add('B')
crdt1.add('C')
const crdt2 = CRDT.class.from(crdt1.toJSON())
assert.deepEqual(new Set(crdt2.values()), new Set(crdt1.values()))
})
it('toArray() returns the values in the set as an array', () => {
const crdt1 = CRDT.create()
crdt1.add('A')
crdt1.add('B')
crdt1.add('C')
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C'])
})
it('returns true if two Sets are equal', () => {
const orset1 = CRDT.create(CRDT.inputData)
const orset2 = CRDT.create(CRDT.inputData)
const orset3 = CRDT.create([])
assert.equal(CRDT.isEqual(orset1, orset2), true)
const isEqual = CRDT.isEqual(orset1, orset3)
const crdt1 = CRDT.create(CRDT.inputData)
const crdt2 = CRDT.create(CRDT.inputData)
const crdt3 = CRDT.create([])
assert.equal(CRDT.isEqual(crdt1, crdt2), true)
const isEqual = CRDT.isEqual(crdt1, crdt3)
assert.equal(isEqual, false)
})
it('returns true if set has all values', () => {
const crdt1 = CRDT.create(CRDT.inputData)
assert.equal(crdt1.hasAll(CRDT.expectedValues), true)
})
it('returns false if set doesn\'t have all values', () => {
const crdt1 = CRDT.create(CRDT.inputData)
assert.equal(crdt1.hasAll(CRDT.expectedValues.concat(['extra', 1])), false)
})
it('returns a Set of values from Set A that are not in Set B', () => {

@@ -145,12 +211,12 @@ const addedValues = added

const orset1 = CRDT.create()
const orset2 = CRDT.create()
const orset3 = CRDT.create()
const crdt1 = CRDT.create()
const crdt2 = CRDT.create()
const crdt3 = CRDT.create()
addedValues.concat(expectedDiff).forEach((e, idx) => orset1.add(e, idx))
removedValues.forEach(e => CRDT.remove(orset1, e, 10))
addedValues.forEach(e => orset2.add(e, 100))
addedValues.concat(expectedDiff).forEach((e, idx) => crdt1.add(e, idx))
removedValues.forEach(e => CRDT.remove(crdt1, e, 10))
addedValues.forEach(e => crdt2.add(e, 100))
assert.deepEqual(CRDT.difference(orset1, orset2), new Set(expectedDiff))
assert.deepEqual(CRDT.difference(orset1, orset3), new Set(expectedValues))
assert.deepEqual(CRDT.difference(crdt1, crdt2), new Set(expectedDiff))
assert.deepEqual(CRDT.difference(crdt1, crdt3), new Set(expectedValues))
})

@@ -157,0 +223,0 @@ })

@@ -7,2 +7,3 @@ 'use strict'

const { GCounter, GSet, TwoPSet, ORSet, LWWSet } = CRDTs
const CmRDTSet = require('../src/CmRDT-Set')

@@ -25,3 +26,3 @@ const crdts = [

merge: (crdt, other) => crdt.merge(other),
query: (crdt) => new Set(crdt.values),
query: (crdt) => new Set(crdt.values()),
getExpectedMergedValue: (values) => new Set(values),

@@ -35,3 +36,3 @@ },

merge: (crdt, other) => crdt.merge(other),
query: (crdt) => new Set(crdt.values),
query: (crdt) => new Set(crdt.values()),
getExpectedMergedValue: (values) => new Set(values),

@@ -45,3 +46,3 @@ },

merge: (crdt, other) => crdt.merge(other),
query: (crdt) => new Set(crdt.values),
query: (crdt) => new Set(crdt.values()),
getExpectedMergedValue: (values) => new Set(values),

@@ -55,3 +56,3 @@ },

merge: (crdt, other) => crdt.merge(other),
query: (crdt) => new Set(crdt.values),
query: (crdt) => new Set(crdt.values()),
getExpectedMergedValue: (values) => new Set(values),

@@ -58,0 +59,0 @@ },

'use strict'
const assert = require('assert')
const { GSet } = require('../src')
const { GSet, CmRDTSet } = require('../src')

@@ -10,13 +10,18 @@ describe('G-Set', () => {

it('creates a set', () => {
const gset = new GSet()
assert.notEqual(gset, null)
assert.notEqual(gset._values, null)
assert.equal(gset._values instanceof Set, true)
const crdt = new GSet()
assert.notEqual(crdt, null)
assert.notEqual(crdt._values, null)
assert.equal(crdt._values instanceof Set, true)
})
it('is a CmRDT Set', () => {
const crdt = new GSet()
assert.equal(crdt instanceof CmRDTSet, true)
})
it('creates a set from values', () => {
const gset = new GSet(['A', 'B'])
assert.notEqual(gset, null)
assert.equal(gset._values instanceof Set, true)
assert.deepEqual(gset._values, new Set(['B', 'A']))
const crdt = new GSet(['A', 'B'])
assert.notEqual(crdt, null)
assert.equal(crdt._values instanceof Set, true)
assert.deepEqual(crdt._values, new Set(['B', 'A']))
})

@@ -27,11 +32,11 @@ })

it('is an Iterator', () => {
const gset = new GSet()
assert.equal(gset.values.toString(), '[object Set Iterator]')
const crdt = new GSet()
assert.equal(crdt.values().toString(), '[object Set Iterator]')
})
it('returns an Iterator', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
const iterator = gset.values
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
const iterator = crdt.values()
assert.equal(iterator.next().value, 'A')

@@ -44,26 +49,26 @@ assert.equal(iterator.next().value, 'B')

it('adds an element to the set', () => {
const gset = new GSet()
gset.add('A')
assert.deepEqual(new Set(gset.values), new Set(['A']))
const crdt = new GSet()
crdt.add('A')
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('adds three elements to the set', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
gset.add('C')
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C']))
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C']))
})
it('contains only unique values', () => {
const gset = new GSet()
gset.add(1)
gset.add('A')
gset.add('A')
gset.add(1)
gset.add('A')
const crdt = new GSet()
crdt.add(1)
crdt.add('A')
crdt.add('A')
crdt.add(1)
crdt.add('A')
const obj = { hello: 'ABC' }
gset.add(obj)
gset.add(obj)
gset.add({ hello: 'ABCD' })
crdt.add(obj)
crdt.add(obj)
crdt.add({ hello: 'ABCD' })

@@ -77,54 +82,69 @@ const expectedResult = [

assert.deepEqual(new Set(gset.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})
})
describe('remove', () => {
it('doesn\'t allow removing values', () => {
let crdt, err
try {
crdt = new GSet()
crdt.add('A')
crdt.remove('A')
} catch (e) {
err = e.toString()
}
assert.equal(err, `Error: G-Set doesn't allow removing values`)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
})
describe('merge', () => {
it('merges two sets with same id', () => {
const gset1 = new GSet()
const gset2 = new GSet()
gset1.add('A')
gset2.add('B')
gset2.add('C')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['A', 'B', 'C'])
const crdt1 = new GSet()
const crdt2 = new GSet()
crdt1.add('A')
crdt2.add('B')
crdt2.add('C')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C'])
})
it('merges two sets with same values', () => {
const gset1 = new GSet()
const gset2 = new GSet()
gset1.add('A')
gset2.add('A')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['A'])
const crdt1 = new GSet()
const crdt2 = new GSet()
crdt1.add('A')
crdt2.add('A')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A'])
})
it('merge four different sets', () => {
const gset1 = new GSet()
const gset2 = new GSet()
const gset3 = new GSet()
const gset4 = new GSet()
gset1.add('A')
gset2.add('B')
gset3.add('C')
gset4.add('D')
const crdt1 = new GSet()
const crdt2 = new GSet()
const crdt3 = new GSet()
const crdt4 = new GSet()
crdt1.add('A')
crdt2.add('B')
crdt3.add('C')
crdt4.add('D')
gset1.merge(gset2)
gset1.merge(gset3)
gset1.merge(gset4)
crdt1.merge(crdt2)
crdt1.merge(crdt3)
crdt1.merge(crdt4)
assert.deepEqual(gset1.toArray(), ['A', 'B', 'C', 'D'])
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C', 'D'])
})
it('doesn\'t overwrite other\'s values on merge', () => {
const gset1 = new GSet()
const gset2 = new GSet()
gset1.add('A')
gset2.add('B')
gset1.merge(gset2)
gset1.add('AA')
gset2.add('BB')
gset1.merge(gset2)
assert.deepEqual(gset1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(gset2.toArray(), ['B', 'BB'])
const crdt1 = new GSet()
const crdt2 = new GSet()
crdt1.add('A')
crdt2.add('B')
crdt1.merge(crdt2)
crdt1.add('AA')
crdt2.add('BB')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(crdt2.toArray(), ['B', 'BB'])
})

@@ -135,19 +155,19 @@ })

it('returns true if given element is in the set', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
gset.add(1)
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
crdt.add(1)
const obj = { hello: 'world' }
gset.add(obj)
assert.equal(gset.has('A'), true)
assert.equal(gset.has('B'), true)
assert.equal(gset.has(1), true)
assert.equal(gset.has(obj), true)
crdt.add(obj)
assert.equal(crdt.has('A'), true)
assert.equal(crdt.has('B'), true)
assert.equal(crdt.has(1), true)
assert.equal(crdt.has(obj), true)
})
it('returns false if given element is not in the set', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
assert.equal(gset.has('nothere'), false)
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
assert.equal(crdt.has('nothere'), false)
})

@@ -158,17 +178,17 @@ })

it('returns true if all given elements are in the set', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.add('D')
assert.equal(gset.hasAll(['D', 'A', 'C', 'B']), true)
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), true)
})
it('returns false if any of the given elements are not in the set', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.add('D')
assert.equal(gset.hasAll(['D', 'A', 'C', 'B', 'nothere']), false)
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B', 'nothere']), false)
})

@@ -179,4 +199,4 @@ })

it('returns the values of the set as an Array', () => {
const gset = new GSet()
const array = gset.toArray()
const crdt = new GSet()
const array = crdt.toArray()
assert.equal(Array.isArray(array), true)

@@ -186,6 +206,6 @@ })

it('returns values', () => {
const gset = new GSet()
gset.add('A')
gset.add('B')
const array = gset.toArray()
const crdt = new GSet()
crdt.add('A')
crdt.add('B')
const array = crdt.toArray()
assert.equal(array[0], 'A')

@@ -198,18 +218,18 @@ assert.equal(array[1], 'B')

it('returns the set as JSON object', () => {
const gset = new GSet()
gset.add('A')
assert.equal(gset.toJSON().values.length, 1)
assert.equal(gset.toJSON().values[0], 'A')
const crdt = new GSet()
crdt.add('A')
assert.equal(crdt.toJSON().values.length, 1)
assert.equal(crdt.toJSON().values[0], 'A')
})
it('returns a JSON object after a merge', () => {
const gset1 = new GSet()
const gset2 = new GSet()
gset1.add('A')
gset2.add('B')
gset1.merge(gset2)
gset2.merge(gset1)
assert.equal(gset1.toJSON().values.length, 2)
assert.equal(gset1.toJSON().values[0], 'A')
assert.equal(gset1.toJSON().values[1], 'B')
const crdt1 = new GSet()
const crdt2 = new GSet()
crdt1.add('A')
crdt2.add('B')
crdt1.merge(crdt2)
crdt2.merge(crdt1)
assert.equal(crdt1.toJSON().values.length, 2)
assert.equal(crdt1.toJSON().values[0], 'A')
assert.equal(crdt1.toJSON().values[1], 'B')
})

@@ -220,24 +240,24 @@ })

it('returns true for sets with same values', () => {
const gset1 = new GSet()
const gset2 = new GSet()
gset1.add('A')
gset2.add('A')
assert.equal(gset1.isEqual(gset2), true)
assert.equal(gset2.isEqual(gset1), true)
const crdt1 = new GSet()
const crdt2 = new GSet()
crdt1.add('A')
crdt2.add('A')
assert.equal(crdt1.isEqual(crdt2), true)
assert.equal(crdt2.isEqual(crdt1), true)
})
it('returns true for empty sets', () => {
const gset1 = new GSet()
const gset2 = new GSet()
assert.equal(gset1.isEqual(gset2), true)
assert.equal(gset2.isEqual(gset1), true)
const crdt1 = new GSet()
const crdt2 = new GSet()
assert.equal(crdt1.isEqual(crdt2), true)
assert.equal(crdt2.isEqual(crdt1), true)
})
it('returns false for sets with different values', () => {
const gset1 = new GSet()
const gset2 = new GSet()
gset1.add('A')
gset2.add('B')
assert.equal(gset1.isEqual(gset2), false)
assert.equal(gset2.isEqual(gset1), false)
const crdt1 = new GSet()
const crdt2 = new GSet()
crdt1.add('A')
crdt2.add('B')
assert.equal(crdt1.isEqual(crdt2), false)
assert.equal(crdt2.isEqual(crdt1), false)
})

@@ -244,0 +264,0 @@ })

'use strict'
const assert = require('assert')
const { LWWSet, GSet } = require('../src')
const { LWWSet, CmRDTSet } = require('../src')
const LamportClock = require('./lamport-clock')

@@ -11,4 +11,4 @@

it('creates a set', () => {
const lwwset = new LWWSet()
assert.notEqual(lwwset, null)
const crdt = new LWWSet()
assert.notEqual(crdt, null)
})

@@ -18,4 +18,3 @@

const gset = new LWWSet()
assert.notEqual(gset._operations, null)
assert.equal(gset._operations instanceof Array, true)
assert.equal(gset instanceof CmRDTSet, true)
})

@@ -26,11 +25,11 @@ })

it('is an Iterator', () => {
const lwwset = new LWWSet()
assert.equal(lwwset.values.toString(), '[object Set Iterator]')
const crdt = new LWWSet()
assert.equal(crdt.values().toString(), '[object Set Iterator]')
})
it('returns an Iterator', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
const iterator = lwwset.values
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
const iterator = crdt.values()
assert.equal(iterator.next().value, 'A')

@@ -43,30 +42,30 @@ assert.equal(iterator.next().value, 'B')

it('adds an element to the set', () => {
const lwwset = new LWWSet()
const crdt = new LWWSet()
const uid = new Date().getTime()
lwwset.add('A', uid)
lwwset.add('A', uid + 1)
assert.deepEqual(new Set(lwwset.values), new Set(['A']))
crdt.add('A', uid)
crdt.add('A', uid + 1)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('adds three elements to the set', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
lwwset.add('C')
assert.deepEqual(new Set(lwwset.values), new Set(['A', 'B', 'C']))
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C']))
})
it('contains only unique values', () => {
const lwwset = new LWWSet()
lwwset.add(1)
lwwset.add('A')
lwwset.add('A')
lwwset.add(1)
lwwset.add('A')
const crdt = new LWWSet()
crdt.add(1)
crdt.add('A')
crdt.add('A')
crdt.add(1)
crdt.add('A')
const obj = { hello: 'ABC' }
lwwset.add(obj)
lwwset.add(obj)
lwwset.add({ hello: 'ABCD' })
crdt.add(obj)
crdt.add(obj)
crdt.add({ hello: 'ABCD' })
lwwset.add(9, 11111)
crdt.add(9, 11111)

@@ -81,3 +80,3 @@ const expectedResult = [

assert.deepEqual(new Set(lwwset.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})

@@ -89,6 +88,6 @@ })

let uid = new LamportClock('A')
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare })
lwwset.add('A', uid)
lwwset.add('A', uid.tick())
assert.deepEqual(new Set(lwwset.values), new Set(['A']))
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare })
crdt.add('A', uid)
crdt.add('A', uid.tick())
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})

@@ -98,7 +97,7 @@

let uid = new LamportClock('A')
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare })
lwwset.add('A', uid)
lwwset.add('B', uid)
lwwset.add('C', uid)
assert.deepEqual(new Set(lwwset.values), new Set(['A', 'B', 'C']))
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare })
crdt.add('A', uid)
crdt.add('B', uid)
crdt.add('C', uid)
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C']))
})

@@ -108,14 +107,14 @@

let uid = new LamportClock('A')
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare })
lwwset.add(1, uid.tick())
lwwset.add('A', uid.tick())
lwwset.add('A', uid.tick())
lwwset.add(1, uid.tick())
lwwset.add('A', uid.tick())
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare })
crdt.add(1, uid.tick())
crdt.add('A', uid.tick())
crdt.add('A', uid.tick())
crdt.add(1, uid.tick())
crdt.add('A', uid.tick())
const obj = { hello: 'ABC' }
lwwset.add(obj, uid.tick())
lwwset.add(obj, uid)
lwwset.add({ hello: 'ABCD' }, uid.tick())
crdt.add(obj, uid.tick())
crdt.add(obj, uid)
crdt.add({ hello: 'ABCD' }, uid.tick())
lwwset.add(9, uid)
crdt.add(9, uid)

@@ -130,3 +129,3 @@ const expectedResult = [

assert.deepEqual(new Set(lwwset.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})

@@ -138,15 +137,15 @@ })

let tag = 1
const lwwset = new LWWSet()
lwwset.add('A', tag)
lwwset.remove('A', tag + 1)
assert.deepEqual(new Set(lwwset.values), new Set([]))
const crdt = new LWWSet()
crdt.add('A', tag)
crdt.remove('A', tag + 1)
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('removes an element from the set when element has multiple tags', () => {
const lwwset = new LWWSet()
lwwset.add('A', 1)
lwwset.add('A', 2)
lwwset.add('A', 3)
lwwset.remove('A', 4)
assert.deepEqual(new Set(lwwset.values), new Set([]))
const crdt = new LWWSet()
crdt.add('A', 1)
crdt.add('A', 2)
crdt.add('A', 3)
crdt.remove('A', 4)
assert.deepEqual(new Set(crdt.values()), new Set([]))
})

@@ -156,6 +155,6 @@

const compareFunc = (a, b) => b - a
const lwwset = new LWWSet()
lwwset.add('A', 2)
lwwset.remove('A', 1)
assert.deepEqual(new Set(lwwset.values), new Set(['A']))
const crdt = new LWWSet()
crdt.add('A', 2)
crdt.remove('A', 1)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})

@@ -165,33 +164,33 @@

const compareFunc = (a, b) => a.time === 'bigger' ? 1 : -1
const lwwset = new LWWSet(null, { compareFunc: compareFunc })
lwwset.add('A', { time: 'bigger' })
lwwset.remove('A', { time: 'smaller' })
assert.deepEqual(new Set(lwwset.values), new Set(['A']))
const crdt = new LWWSet(null, { compareFunc: compareFunc })
crdt.add('A', { time: 'bigger' })
crdt.remove('A', { time: 'smaller' })
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('removes an element from the set if all add tags are in removed tags', () => {
const lwwset = new LWWSet()
const crdt = new LWWSet()
const uid = new Date().getTime()
lwwset.add('A', uid)
lwwset.remove('A', uid)
lwwset.add('A', uid + 1)
lwwset.add('A', uid + 2)
lwwset.remove('A', uid + 3)
assert.deepEqual(new Set(lwwset.values), new Set([]))
crdt.add('A', uid)
crdt.remove('A', uid)
crdt.add('A', uid + 1)
crdt.add('A', uid + 2)
crdt.remove('A', uid + 3)
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('doesn\'t remove an element from the set if add and remove were concurrent', () => {
const lwwset = new LWWSet()
const crdt = new LWWSet()
const uid = new Date().getTime()
lwwset.remove('A', uid)
lwwset.add('A', uid)
assert.deepEqual(new Set(lwwset.values), new Set(['A']))
crdt.remove('A', uid)
crdt.add('A', uid)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('doesn\'t remove an element from the set if element wasn\'t in the set', () => {
const lwwset = new LWWSet()
const crdt = new LWWSet()
const uid = new Date().getTime()
lwwset.remove('A', uid - 1)
lwwset.add('A', uid)
assert.deepEqual(new Set(lwwset.values), new Set(['A']))
crdt.remove('A', uid - 1)
crdt.add('A', uid)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})

@@ -203,6 +202,6 @@ })

let tag = new LamportClock('A')
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare })
lwwset.add('A', tag)
lwwset.remove('A', tag.tick())
assert.deepEqual(new Set(lwwset.values), new Set([]))
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare })
crdt.add('A', tag)
crdt.remove('A', tag.tick())
assert.deepEqual(new Set(crdt.values()), new Set([]))
})

@@ -213,11 +212,11 @@

let clock2 = new LamportClock('A')
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare })
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare })
clock1 = clock1.tick()
lwwset.add('A', clock1)
crdt.add('A', clock1)
clock2 = clock2.tick()
lwwset.add('A', clock1)
crdt.add('A', clock1)
clock2 = clock2.tick()
clock2 = clock2.tick()
lwwset.remove('A', clock2)
assert.deepEqual(new Set(lwwset.values), new Set([]))
crdt.remove('A', clock2)
assert.deepEqual(new Set(crdt.values()), new Set([]))
})

@@ -228,74 +227,74 @@ })

it('merges two sets with same id', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A')
orset2.add('B')
orset2.add('C')
orset2.add('D', 33)
orset2.remove('D', 34)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['A', 'B', 'C'])
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A')
lwwset2.add('B')
lwwset2.add('C')
lwwset2.add('D', 33)
lwwset2.remove('D', 34)
lwwset1.merge(lwwset2)
assert.deepEqual(lwwset1.toArray(), ['A', 'B', 'C'])
})
it('merges two sets with same values', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A')
orset2.add('B', 13)
orset2.add('A')
orset2.remove('B', 14)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['A'])
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A')
lwwset2.add('B', 13)
lwwset2.add('A')
lwwset2.remove('B', 14)
lwwset1.merge(lwwset2)
assert.deepEqual(lwwset1.toArray(), ['A'])
})
it('merges two sets with removed values', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A', 1)
orset1.remove('A', 2)
orset2.remove('A', 3)
orset2.add('A', 4)
orset1.add('AAA', 5)
orset2.add('AAA', 6)
orset1.add('A', 7)
orset1.remove('A', 8)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['AAA'])
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A', 1)
lwwset1.remove('A', 2)
lwwset2.remove('A', 3)
lwwset2.add('A', 4)
lwwset1.add('AAA', 5)
lwwset2.add('AAA', 6)
lwwset1.add('A', 7)
lwwset1.remove('A', 8)
lwwset1.merge(lwwset2)
assert.deepEqual(lwwset1.toArray(), ['AAA'])
})
it('merge four different sets', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
const orset3 = new LWWSet()
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
const lwwset3 = new LWWSet()
const lwwset4 = new LWWSet()
orset1.add('A', 1)
orset2.add('B', 1)
orset3.add('C', 1)
lwwset1.add('A', 1)
lwwset2.add('B', 1)
lwwset3.add('C', 1)
lwwset4.add('D', 1)
orset2.add('BB', 2)
orset2.remove('BB', 3)
lwwset2.add('BB', 2)
lwwset2.remove('BB', 3)
orset1.merge(orset2)
orset1.merge(orset3)
orset1.remove('C', 2)
orset1.merge(lwwset4)
lwwset1.merge(lwwset2)
lwwset1.merge(lwwset3)
lwwset1.remove('C', 2)
lwwset1.merge(lwwset4)
assert.deepEqual(orset1.toArray(), ['A', 'B', 'D'])
assert.deepEqual(lwwset1.toArray(), ['A', 'B', 'D'])
})
it('doesn\'t overwrite other\'s values on merge', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A', 1)
orset2.add('C', 1)
orset2.add('B', 1)
orset2.remove('C', 2)
orset1.merge(orset2)
orset1.add('AA', 3)
orset2.add('CC', 3)
orset2.add('BB', 3)
orset2.remove('CC', 4)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(orset2.toArray(), ['B', 'BB'])
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A', 1)
lwwset2.add('C', 1)
lwwset2.add('B', 1)
lwwset2.remove('C', 2)
lwwset1.merge(lwwset2)
lwwset1.add('AA', 3)
lwwset2.add('CC', 3)
lwwset2.add('BB', 3)
lwwset2.remove('CC', 4)
lwwset1.merge(lwwset2)
assert.deepEqual(lwwset1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(lwwset2.toArray(), ['B', 'BB'])
})

@@ -306,24 +305,24 @@ })

it('returns true if given element is in the set', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
lwwset.add(1)
lwwset.add(13, 'A')
lwwset.remove(13, 'B')
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
crdt.add(1)
crdt.add(13, 'A')
crdt.remove(13, 'B')
const obj = { hello: 'world' }
lwwset.add(obj)
assert.equal(lwwset.has('A'), true)
assert.equal(lwwset.has('B'), true)
assert.equal(lwwset.has(1), true)
assert.equal(lwwset.has(13), false)
assert.equal(lwwset.has(obj), true)
crdt.add(obj)
assert.equal(crdt.has('A'), true)
assert.equal(crdt.has('B'), true)
assert.equal(crdt.has(1), true)
assert.equal(crdt.has(13), false)
assert.equal(crdt.has(obj), true)
})
it('returns false if given element is not in the set', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
lwwset.add('nothere', 666)
lwwset.remove('nothere', 777)
assert.equal(lwwset.has('nothere'), false)
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
crdt.add('nothere', 666)
crdt.remove('nothere', 777)
assert.equal(crdt.has('nothere'), false)
})

@@ -334,19 +333,19 @@ })

it('returns true if all given elements are in the set', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
lwwset.add('C')
lwwset.remove('C')
lwwset.add('D')
assert.equal(lwwset.hasAll(['D', 'A', 'B']), true)
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.remove('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'B']), true)
})
it('returns false if any of the given elements are not in the set', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
lwwset.add('C', 1)
lwwset.remove('C', 2)
lwwset.add('D')
assert.equal(lwwset.hasAll(['D', 'A', 'C', 'B']), false)
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
crdt.add('C', 1)
crdt.remove('C', 2)
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), false)
})

@@ -357,4 +356,4 @@ })

it('returns the values of the set as an Array', () => {
const lwwset = new LWWSet()
const array = lwwset.toArray()
const crdt = new LWWSet()
const array = crdt.toArray()
assert.equal(Array.isArray(array), true)

@@ -364,8 +363,8 @@ })

it('returns values', () => {
const lwwset = new LWWSet()
lwwset.add('A')
lwwset.add('B')
lwwset.add('C', 1)
lwwset.remove('C', 2)
const array = lwwset.toArray()
const crdt = new LWWSet()
crdt.add('A')
crdt.add('B')
crdt.add('C', 1)
crdt.remove('C', 2)
const array = crdt.toArray()
assert.equal(array.length, 2)

@@ -379,21 +378,25 @@ assert.equal(array[0], 'A')

it('returns the set as JSON object', () => {
const lwwset = new LWWSet()
lwwset.add('A', 1)
assert.equal(lwwset.toJSON().values.length, 1)
assert.equal(lwwset.toJSON().values[0], 'A')
lwwset.remove('A', 2)
assert.equal(lwwset.toJSON().values.length, 0)
const crdt = new LWWSet()
crdt.add('A', 1)
assert.equal(crdt.toJSON().values.length, 1)
assert.equal(crdt.toJSON().values[0].value, 'A')
assert.equal(crdt.toJSON().values[0].added[0], 1)
crdt.remove('A', 2)
assert.equal(crdt.toJSON().values.length, 1)
assert.equal(crdt.toJSON().values[0].value, 'A')
assert.equal(crdt.toJSON().values[0].removed[0], 2)
})
it('returns a JSON object after a merge', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A', 1)
orset2.add('B', 1)
orset2.remove('B', 2)
orset1.add('C', 3)
orset1.merge(orset2)
assert.equal(orset1.toJSON().values.length, 2)
assert.equal(orset1.toJSON().values[0], 'A')
assert.equal(orset1.toJSON().values[1], 'C')
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A', 1)
lwwset2.add('B', 1)
lwwset2.remove('B', 2)
lwwset1.add('C', 3)
lwwset1.merge(lwwset2)
assert.equal(lwwset1.toJSON().values.length, 3)
assert.equal(lwwset1.toJSON().values[0].value, 'A')
assert.equal(lwwset1.toJSON().values[1].value, 'C')
assert.equal(lwwset1.toJSON().values[2].value, 'B')
})

@@ -404,24 +407,24 @@ })

it('returns true for sets with same values', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A', 1)
orset2.add('A', 1)
assert.equal(orset1.isEqual(orset2), true)
assert.equal(orset2.isEqual(orset1), true)
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A', 1)
lwwset2.add('A', 1)
assert.equal(lwwset1.isEqual(lwwset2), true)
assert.equal(lwwset2.isEqual(lwwset1), true)
})
it('returns true for empty sets', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
assert.equal(orset1.isEqual(orset2), true)
assert.equal(orset2.isEqual(orset1), true)
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
assert.equal(lwwset1.isEqual(lwwset2), true)
assert.equal(lwwset2.isEqual(lwwset1), true)
})
it('returns false for sets with different values', () => {
const orset1 = new LWWSet()
const orset2 = new LWWSet()
orset1.add('A')
orset2.add('B')
assert.equal(orset1.isEqual(orset2), false)
assert.equal(orset2.isEqual(orset1), false)
const lwwset1 = new LWWSet()
const lwwset2 = new LWWSet()
lwwset1.add('A')
lwwset2.add('B')
assert.equal(lwwset1.isEqual(lwwset2), false)
assert.equal(lwwset2.isEqual(lwwset1), false)
})

@@ -428,0 +431,0 @@ })

'use strict'
const assert = require('assert')
const { ORSet, GSet } = require('../src')
const { ORSet, CmRDTSet } = require('../src')
const LamportClock = require('./lamport-clock')

@@ -11,10 +11,9 @@

it('creates a set', () => {
const gset = new ORSet()
assert.notEqual(gset, null)
const crdt = new ORSet()
assert.notEqual(crdt, null)
})
it('is a CmRDT Set', () => {
const gset = new ORSet()
assert.notEqual(gset._operations, null)
assert.equal(gset._operations instanceof Array, true)
const crdt = new ORSet()
assert.equal(crdt instanceof CmRDTSet, true)
})

@@ -25,11 +24,11 @@ })

it('is an Iterator', () => {
const gset = new ORSet()
assert.equal(gset.values.toString(), '[object Set Iterator]')
const crdt = new ORSet()
assert.equal(crdt.values().toString(), '[object Set Iterator]')
})
it('returns an Iterator', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
const iterator = gset.values
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
const iterator = crdt.values()
assert.equal(iterator.next().value, 'A')

@@ -42,30 +41,30 @@ assert.equal(iterator.next().value, 'B')

it('adds an element to the set', () => {
const gset = new ORSet()
const crdt = new ORSet()
const uid = new Date().getTime()
gset.add('A', uid)
gset.add('A', uid + 1)
assert.deepEqual(new Set(gset.values), new Set(['A']))
crdt.add('A', uid)
crdt.add('A', uid + 1)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('adds three elements to the set', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
gset.add('C')
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C']))
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C']))
})
it('contains only unique values', () => {
const gset = new ORSet()
gset.add(1)
gset.add('A')
gset.add('A')
gset.add(1)
gset.add('A')
const crdt = new ORSet()
crdt.add(1)
crdt.add('A')
crdt.add('A')
crdt.add(1)
crdt.add('A')
const obj = { hello: 'ABC' }
gset.add(obj)
gset.add(obj)
gset.add({ hello: 'ABCD' })
crdt.add(obj)
crdt.add(obj)
crdt.add({ hello: 'ABCD' })
gset.add(9, 'ok')
crdt.add(9, 'ok')

@@ -80,3 +79,3 @@ const expectedResult = [

assert.deepEqual(new Set(gset.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})

@@ -87,7 +86,7 @@ })

it('adds an element to the set', () => {
let uid = new LamportClock('A')
const gset = new ORSet(null, { compareFunc: LamportClock.compare })
gset.add('A', uid)
gset.add('A', uid.tick())
assert.deepEqual(new Set(gset.values), new Set(['A']))
let clock = new LamportClock('A')
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual })
crdt.add('A', clock)
crdt.add('A', clock.tick())
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})

@@ -97,7 +96,7 @@

let uid = new LamportClock('A')
const gset = new ORSet(null, { compareFunc: LamportClock.compare })
gset.add('A', uid)
gset.add('B', uid)
gset.add('C', uid)
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C']))
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual })
crdt.add('A', uid)
crdt.add('B', uid)
crdt.add('C', uid)
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C']))
})

@@ -107,14 +106,14 @@

let uid = new LamportClock('A')
const gset = new ORSet(null, { compareFunc: LamportClock.compare })
gset.add(1, uid.tick())
gset.add('A', uid.tick())
gset.add('A', uid.tick())
gset.add(1, uid.tick())
gset.add('A', uid.tick())
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual })
crdt.add(1, uid.tick())
crdt.add('A', uid.tick())
crdt.add('A', uid.tick())
crdt.add(1, uid.tick())
crdt.add('A', uid.tick())
const obj = { hello: 'ABC' }
gset.add(obj, uid.tick())
gset.add(obj, uid)
gset.add({ hello: 'ABCD' }, uid.tick())
crdt.add(obj, uid.tick())
crdt.add(obj, uid)
crdt.add({ hello: 'ABCD' }, uid.tick())
gset.add(9, 'ok')
crdt.add(9, 'ok')

@@ -129,3 +128,3 @@ const expectedResult = [

assert.deepEqual(new Set(gset.values), new Set(expectedResult))
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult))
})

@@ -137,34 +136,34 @@ })

let tag = 1
const gset = new ORSet()
gset.add('A', tag)
gset.remove('A', tag)
assert.deepEqual(new Set(gset.values), new Set([]))
const crdt = new ORSet()
crdt.add('A', tag)
crdt.remove('A', tag)
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('removes an element from the set when element has multiple tags', () => {
const gset = new ORSet()
gset.add('A', 1)
gset.add('A', 2)
gset.add('A', 3)
gset.remove('A')
assert.deepEqual(new Set(gset.values), new Set([]))
const crdt = new ORSet()
crdt.add('A', 1)
crdt.add('A', 2)
crdt.add('A', 3)
crdt.remove('A')
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('removes an element from the set if all add tags are in removed tags', () => {
const gset = new ORSet()
const crdt = new ORSet()
const uid = new Date().getTime()
gset.add('A', uid)
gset.remove('A')
gset.add('A', uid + 1)
gset.add('A', uid + 2)
gset.remove('A')
assert.deepEqual(new Set(gset.values), new Set([]))
crdt.add('A', uid)
crdt.remove('A')
crdt.add('A', uid + 1)
crdt.add('A', uid + 2)
crdt.remove('A')
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('doesn\'t remove an element from the set if element wasn\'t in the set', () => {
const gset = new ORSet()
const crdt = new ORSet()
const uid = new Date().getTime()
gset.remove('A')
gset.add('A', uid)
assert.deepEqual(new Set(gset.values), new Set(['A']))
crdt.remove('A')
crdt.add('A', uid)
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})

@@ -175,20 +174,68 @@ })

it('removes an element from the set', () => {
let tag = new LamportClock('A')
const gset = new ORSet(null, { compareFunc: LamportClock.compare })
gset.add('A', tag)
gset.remove('A')
assert.deepEqual(new Set(gset.values), new Set([]))
let clock = new LamportClock('A')
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual })
crdt.add('A', clock)
clock = clock.tick()
crdt.remove('A')
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('doesn\'t remove an element from the set if element was added with a new tag', () => {
const crdt = new ORSet()
const clock1 = new LamportClock('A')
const clock2 = new LamportClock('B')
crdt.add('A', clock1)
crdt.remove('A')
crdt.add('A', clock1.tick())
assert.deepEqual(new Set(crdt.values()), new Set(['A']))
})
it('removes an element from the set with the same tag', () => {
let clock1 = new LamportClock('A')
let clock2 = new LamportClock('A')
const gset = new ORSet(null, { compareFunc: LamportClock.compare })
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual })
clock1 = clock1.tick()
gset.add('A', clock1)
crdt.add('A', clock1)
clock2 = clock2.tick()
gset.add('A', clock1)
gset.remove('A')
assert.deepEqual(new Set(gset.values), new Set([]))
crdt.add('A', clock1)
crdt.remove('A')
assert.deepEqual(new Set(crdt.values()), new Set([]))
})
it('doesn\'t remove an element from the set if element was added with different tags', () => {
const crdt1 = new ORSet()
const crdt2 = new ORSet()
const clock1 = new LamportClock('A')
const clock2 = new LamportClock('B')
crdt1.add('A', clock1)
crdt1.remove('A')
crdt2.add('A', clock2)
crdt2.remove('A')
assert.deepEqual(new Set(crdt1.values()), new Set([]))
assert.deepEqual(new Set(crdt2.values()), new Set([]))
crdt1.merge(crdt2)
assert.deepEqual(new Set(crdt1.values()), new Set([]))
crdt1.add('A', clock1.tick())
assert.deepEqual(new Set(crdt1.values()), new Set(['A']))
crdt2.merge(crdt1)
assert.deepEqual(new Set(crdt2.values()), new Set(['A']))
})
it('doesn\'t remove an element from the set if not all add tags are observed', () => {
const crdt1 = new ORSet()
const crdt2 = new ORSet()
const clock1 = new LamportClock('A')
const clock2 = new LamportClock('B')
crdt1.add('A', clock1)
crdt2.add('A', clock2)
crdt2.remove('A')
assert.deepEqual(new Set(crdt1.values()), new Set(['A']))
assert.deepEqual(new Set(crdt2.values()), new Set([]))
crdt2.merge(crdt1)
assert.deepEqual(new Set(crdt1.values()), new Set(['A']))
})
})

@@ -198,73 +245,73 @@

it('merges two sets with same id', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A')
orset2.add('B')
orset2.add('C')
orset2.add('D')
orset2.remove('D')
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['A', 'B', 'C'])
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A')
crdt2.add('B')
crdt2.add('C')
crdt2.add('D')
crdt2.remove('D')
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C'])
})
it('merges two sets with same values', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A')
orset2.add('B', 13)
orset2.add('A')
orset2.remove('B', 13)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['A'])
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A')
crdt2.add('B', 13)
crdt2.add('A')
crdt2.remove('B', 13)
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A'])
})
it('merges two sets with removed values', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A', 1)
orset1.remove('A', 1)
orset2.add('A', 1)
orset2.remove('A', 2)
orset1.add('AAA')
orset2.add('AAA')
orset1.add('A', 1)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['AAA'])
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A', 1)
crdt1.remove('A', 1)
crdt2.add('A', 1)
crdt2.remove('A', 2)
crdt1.add('AAA')
crdt2.add('AAA')
crdt1.add('A', 1)
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['AAA'])
})
it('merge four different sets', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
const crdt1 = new ORSet()
const crdt2 = new ORSet()
const orset3 = new ORSet()
const gset4 = new ORSet()
orset1.add('A')
orset2.add('B')
const crdt4 = new ORSet()
crdt1.add('A')
crdt2.add('B')
orset3.add('C', 7)
gset4.add('D')
orset2.add('BB', 1)
orset2.remove('BB', 1)
crdt4.add('D')
crdt2.add('BB', 1)
crdt2.remove('BB', 1)
orset1.merge(orset2)
orset1.merge(orset3)
orset1.remove('C', 8)
orset1.merge(gset4)
crdt1.merge(crdt2)
crdt1.merge(orset3)
crdt1.remove('C', 8)
crdt1.merge(crdt4)
assert.deepEqual(orset1.toArray(), ['A', 'B', 'D'])
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'D'])
})
it('doesn\'t overwrite other\'s values on merge', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A')
orset2.add('C')
orset2.add('B')
orset2.remove('C')
orset1.merge(orset2)
orset1.add('AA')
orset2.add('CC', 1)
orset2.add('BB')
orset2.remove('CC', 1)
orset1.merge(orset2)
assert.deepEqual(orset1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(orset2.toArray(), ['B', 'BB'])
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A')
crdt2.add('C')
crdt2.add('B')
crdt2.remove('C')
crdt1.merge(crdt2)
crdt1.add('AA')
crdt2.add('CC', 1)
crdt2.add('BB')
crdt2.remove('CC', 1)
crdt1.merge(crdt2)
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'AA', 'BB'])
assert.deepEqual(crdt2.toArray(), ['B', 'BB'])
})

@@ -275,24 +322,24 @@ })

it('returns true if given element is in the set', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
gset.add(1)
gset.add(13)
gset.remove(13)
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
crdt.add(1)
crdt.add(13)
crdt.remove(13)
const obj = { hello: 'world' }
gset.add(obj)
assert.equal(gset.has('A'), true)
assert.equal(gset.has('B'), true)
assert.equal(gset.has(1), true)
assert.equal(gset.has(13), false)
assert.equal(gset.has(obj), true)
crdt.add(obj)
assert.equal(crdt.has('A'), true)
assert.equal(crdt.has('B'), true)
assert.equal(crdt.has(1), true)
assert.equal(crdt.has(13), false)
assert.equal(crdt.has(obj), true)
})
it('returns false if given element is not in the set', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
gset.add('nothere')
gset.remove('nothere')
assert.equal(gset.has('nothere'), false)
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
crdt.add('nothere')
crdt.remove('nothere')
assert.equal(crdt.has('nothere'), false)
})

@@ -303,19 +350,19 @@ })

it('returns true if all given elements are in the set', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.remove('C')
gset.add('D')
assert.equal(gset.hasAll(['D', 'A', 'B']), true)
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.remove('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'B']), true)
})
it('returns false if any of the given elements are not in the set', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
gset.add('C')
gset.remove('C')
gset.add('D')
assert.equal(gset.hasAll(['D', 'A', 'C', 'B']), false)
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
crdt.add('C')
crdt.remove('C')
crdt.add('D')
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), false)
})

@@ -326,4 +373,4 @@ })

it('returns the values of the set as an Array', () => {
const gset = new ORSet()
const array = gset.toArray()
const crdt = new ORSet()
const array = crdt.toArray()
assert.equal(Array.isArray(array), true)

@@ -333,8 +380,8 @@ })

it('returns values', () => {
const gset = new ORSet()
gset.add('A')
gset.add('B')
gset.add('C', 1)
gset.remove('C', 1)
const array = gset.toArray()
const crdt = new ORSet()
crdt.add('A')
crdt.add('B')
crdt.add('C', 1)
crdt.remove('C', 1)
const array = crdt.toArray()
assert.equal(array.length, 2)

@@ -348,22 +395,28 @@ assert.equal(array[0], 'A')

it('returns the set as JSON object', () => {
const gset = new ORSet()
gset.add('A', 1)
assert.equal(gset.toJSON().values.length, 1)
assert.equal(gset.toJSON().values[0], 'A')
gset.remove('A', 1)
assert.equal(gset.toJSON().values.length, 0)
const crdt = new ORSet()
crdt.add('A', 1)
assert.equal(crdt.toJSON().values.length, 1)
assert.equal(crdt.toJSON().values[0].added.length, 1)
assert.equal(crdt.toJSON().values[0].value, 'A')
crdt.remove('A', 1)
assert.equal(crdt.toJSON().values.length, 1)
assert.equal(crdt.toJSON().values[0].removed.length, 1)
assert.equal(crdt.toJSON().values[0].value, 'A')
})
it('returns a JSON object after a merge', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A')
orset2.add('B', 1)
orset2.remove('B', 1)
orset1.add('C')
orset1.merge(orset2)
orset2.merge(orset1)
assert.equal(orset1.toJSON().values.length, 2)
assert.equal(orset1.toJSON().values[0], 'A')
assert.equal(orset1.toJSON().values[1], 'C')
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A')
crdt2.add('B', 1)
crdt2.remove('B', 1)
crdt1.add('C')
crdt1.merge(crdt2)
crdt2.merge(crdt1)
assert.equal(crdt1.toJSON().values.length, 3)
assert.equal(crdt1.toJSON().values[0].value, 'A')
assert.equal(crdt1.toJSON().values[1].value, 'C')
assert.equal(crdt1.toJSON().values[2].value, 'B')
assert.equal(crdt1.toJSON().values[2].added.length, 1)
assert.equal(crdt1.toJSON().values[2].removed.length, 1)
})

@@ -374,24 +427,24 @@ })

it('returns true for sets with same values', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A')
orset2.add('A')
assert.equal(orset1.isEqual(orset2), true)
assert.equal(orset2.isEqual(orset1), true)
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A')
crdt2.add('A')
assert.equal(crdt1.isEqual(crdt2), true)
assert.equal(crdt2.isEqual(crdt1), true)
})
it('returns true for empty sets', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
assert.equal(orset1.isEqual(orset2), true)
assert.equal(orset2.isEqual(orset1), true)
const crdt1 = new ORSet()
const crdt2 = new ORSet()
assert.equal(crdt1.isEqual(crdt2), true)
assert.equal(crdt2.isEqual(crdt1), true)
})
it('returns false for sets with different values', () => {
const orset1 = new ORSet()
const orset2 = new ORSet()
orset1.add('A')
orset2.add('B')
assert.equal(orset1.isEqual(orset2), false)
assert.equal(orset2.isEqual(orset1), false)
const crdt1 = new ORSet()
const crdt2 = new ORSet()
crdt1.add('A')
crdt2.add('B')
assert.equal(crdt1.isEqual(crdt2), false)
assert.equal(crdt2.isEqual(crdt1), false)
})

@@ -398,0 +451,0 @@ })

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