diff --git a/README.md b/README.md index 107c398..5c6d3ae 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,37 @@ # js-crdt [![Build Status](https://travis-ci.org/widmogrod/js-crdt.svg?branch=master)](https://travis-ci.org/widmogrod/js-crdt) ## Introduction -CRDT Conflict-free Replication Data Type in JavaScript. +Real time collaboration is a fascinating concept. +Main purpose of this libriary it to explore applications of data structure called `CRDT` in context of real time collaboration +and learn answers to questions like: -## Development -``` -npm install -npm run build -npm test -npm run lint -npm run dist -``` +- How to write collaborative applications? +- How to ensure strong eventual consistency? +- What are limirations of CRDTs? -## How `crdt/Text` type with `order/discrete` should work? (WIP) -Consider two actors `a` and `b` working concurrently on some document. -Actor `a` and `b` have __same origin__ of the document `origin:0`. -For simplification, origin is not shown here. +> CRDT stands for Conflict-free Replication Data Type in JavaScript. -``` -a:0 -b:0 -``` +## Components +Compoents that can be found in this libriary: -First actor started working on version `a:0`, and did some changes `xxx`. -Actor `b` did not start working on document. -``` -a:0 xxx -b:0 -``` +- Data structures like `Immutable SortedSet`, `Immutable Lists`, `Immutable Maps` +- Partial ordering algorithms like `Vector clock` +- Higher order component `text` that encapsulates operation on plain text that can be used in collaborative editing. -Actor `a` decided to send his version `a:1` to actor `b`, -but before that, he want to be sure that regardless if `b` receives his changes, he can continue on working his version of document. -so he created a snapshot `a:1` of his version of document and send changes to actor `b`. +## Applications +- https://github.com/widmogrod/notepad-app - Collaborative notepad app (demo). -``` -a:0 xxx a:1 -b:0 -``` - -Actor `b` sees incoming changes `a:0` of the actor `a`, -so he merge them with his current state and create new version it `b:1 a:0` -``` -a:0 xxx a:1 - | -b:0 b:1 a:0 -``` - -Actor `b` decides to add something to document `yyy`, and send his changes to actor `a`. -Actor `b` in the same manner, before sending his changes to actor `a` -he creates snapshot of his version of document `b:2 a:0` and send changes `b:1 a:0` to `a`. +## Development +Basic development requires at least `nodejs@8`. +To quickly start developmen run: ``` -a:0 xxx a:1 - | -b:0 b:1 a:0 yyy b:2 a:0 +npm install --only=dev +npm test ``` -When actor `a` sees incoming changes, he merge them with his document. +Before pull request run following commands to ensure code quality: ``` -a:0 xxx a:1 a:2 b:1 - | | -b:0 b:1 a:0 yyy b:2 a:0 +npm test +npm run lint +npm run dist ``` diff --git a/build/index.d.ts b/build/index.d.ts index b8036f6..e863d05 100644 --- a/build/index.d.ts +++ b/build/index.d.ts @@ -4,4 +4,12 @@ import * as utils from './utils'; import * as order from './order'; import * as text from './text'; import * as structures from './structures'; -export { text, order, utils, functions, increment, structures }; +declare const _default: { + text: typeof text; + order: typeof order; + utils: typeof utils; + functions: typeof functions; + increment: typeof increment; + structures: typeof structures; +}; +export default _default; diff --git a/build/index.js b/build/index.js index d425009..1ed3b35 100644 --- a/build/index.js +++ b/build/index.js @@ -1,15 +1,17 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const functions = require("./functions"); -exports.functions = functions; const increment = require("./increment"); -exports.increment = increment; const utils = require("./utils"); -exports.utils = utils; const order = require("./order"); -exports.order = order; const text = require("./text"); -exports.text = text; const structures = require("./structures"); -exports.structures = structures; +exports.default = { + text, + order, + utils, + functions, + increment, + structures +}; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/index.js.map b/build/index.js.map index 86a4adf..a0c8559 100644 --- a/build/index.js.map +++ b/build/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yCAAyC;AAWvC,8BAAS;AAVX,yCAAyC;AAWvC,8BAAS;AAVX,iCAAiC;AAQ/B,sBAAK;AAPP,iCAAiC;AAM/B,sBAAK;AALP,+BAA+B;AAI7B,oBAAI;AAHN,2CAA2C;AAQzC,gCAAU"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yCAAyC;AACzC,yCAAyC;AACzC,iCAAiC;AACjC,iCAAiC;AACjC,+BAA+B;AAC/B,2CAA2C;AAE3C,kBAAe;IACb,IAAI;IACJ,KAAK;IACL,KAAK;IACL,SAAS;IACT,SAAS;IACT,UAAU;CACX,CAAA"} \ No newline at end of file diff --git a/dist/crdt.js b/dist/crdt.js index 7becdaa..94d5f84 100644 --- a/dist/crdt.js +++ b/dist/crdt.js @@ -50,17 +50,19 @@ exports.Increment = Increment; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const functions = require("./functions"); -exports.functions = functions; const increment = require("./increment"); -exports.increment = increment; const utils = require("./utils"); -exports.utils = utils; const order = require("./order"); -exports.order = order; const text = require("./text"); -exports.text = text; const structures = require("./structures"); -exports.structures = structures; +exports.default = { + text, + order, + utils, + functions, + increment, + structures +}; },{"./functions":1,"./increment":2,"./order":4,"./structures":7,"./text":15,"./utils":19}],4:[function(require,module,exports){ "use strict"; diff --git a/dist/crdt.min.js b/dist/crdt.min.js index c8ebee2..b491e18 100644 --- a/dist/crdt.min.js +++ b/dist/crdt.min.js @@ -1 +1 @@ -!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).crdt=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n||e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o(vector[key]=Math.max(this.vector[key]||0,b.vector[key]||0),vector),{});return new VectorClock(this.id,vector)}equal(b){return 0===this.compare(b)}compare(b){const position=utils_1.common(this.vector,b.vector).reduce((result,key)=>result+(this.vector[key]-b.vector[key]),0);if(0!==position)return position;const dif=utils_1.diff(this.vector,b.vector).length-utils_1.diff(b.vector,this.vector).length;if(0!==dif)return dif;const tipPosition=this.vector[this.id]-b.vector[b.id];if(0!==tipPosition)return tipPosition;const ha=b.vector.hasOwnProperty(this.id),hb=this.vector.hasOwnProperty(b.id);return ha||hb?ha&&!hb?-1:hb&&!ha?1:0:this.idvalue.version&&(result=vector.remove(value).result.add(id).result),this.vector=result}toString(){const a=this.vector.reduce((r,i)=>r+i.toString(),"");return`VectorClock2(${this.id},${a})`}next(){return new VectorClock2(this.id.next(),this.vector.remove(this.id).result.add(this.id.next()).result)}equal(b){return this.vector.size()===b.vector.size()&&this.vector.reduce((eq,item)=>{if(eq){const{result:result,value:value}=b.vector.add(item);if(result===b.vector)return value.version===item.version}return!1},!0)}compare(b){return this.lessThan(b)?-1:b.lessThan(this)?1:this.equal(b)?0:this.id.compare(b.id)}lessThan(b){const{everyLEQ:everyLEQ,anyLT:anyLT}=this.vector.intersect(b.vector).reduce(({everyLEQ:everyLEQ,anyLT:anyLT},item)=>{const rA=this.vector.add(item).value,rB=b.vector.add(item).value;return anyLT=anyLT||rA.version{const{result:result,value:value}=b.vector.add(item);return result===b.vector&&value.version>item.version?vector.add(value).result:vector.add(item).result},this.vector.mempty());return new VectorClock2(this.id,vector.union(b.vector))}}exports.VectorClock2=VectorClock2},{}],7:[function(require,module,exports){"use strict";function __export(m){for(var p in m)exports.hasOwnProperty(p)||(exports[p]=m[p])}Object.defineProperty(exports,"__esModule",{value:!0}),__export(require("./naive-array-list")),__export(require("./naive-immutable-map")),__export(require("./set-axioms")),__export(require("./set-map")),__export(require("./sorted-set-array"))},{"./naive-array-list":8,"./naive-immutable-map":9,"./set-axioms":10,"./set-map":11,"./sorted-set-array":12}],8:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class NaiveArrayList{constructor(array){this.array=array||[]}insert(at,item){const clone=this.array.slice(0);return clone.splice(at,0,item),new NaiveArrayList(clone)}remove(at){const clone=this.array.slice(0);return clone.splice(at,1),new NaiveArrayList(clone)}get(at){return this.array[at]}size(){return this.array.length}reduce(fn,aggregator){return this.array.reduce(fn,aggregator)}mempty(){return new NaiveArrayList}}exports.NaiveArrayList=NaiveArrayList},{}],9:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class NaiveImmutableMap{constructor(data){this.data=data,this.data=data||{}}set(key,value){const clone=Object.keys(this.data).reduce((clone,k)=>(clone[k]=this.data[k],clone),{});return clone[key]=value,new NaiveImmutableMap(clone)}get(key){return this.data[key]}}exports.NaiveImmutableMap=NaiveImmutableMap},{}],10:[function(require,module,exports){"use strict";function equal(a,b){return a.equal(b)}function union(a,b){return a.union(b)}function intersect(a,b){return a.intersect(b)}function difference(a,b){return a.difference(b)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.axioms=function(assert,a,b,c){assert(equal(union(union(a,b),c),union(a,union(b,c))),"associative union"),assert(equal(intersect(intersect(a,b),c),intersect(a,intersect(b,c))),"associative intersect"),assert(equal(union(a,intersect(b,c)),union(intersect(a,b),intersect(a,c))),"union distributes over intersection"),assert(equal(intersect(a,union(b,c)),intersect(union(a,b),union(a,c))),"intersection distributes over union"),assert(equal(difference(a,union(b,c)),intersect(difference(a,b),difference(a,c))),"De Morgan's law for union"),assert(equal(difference(a,intersect(b,c)),union(difference(a,b),difference(a,c))),"De Morgan's law for intersect")}},{}],11:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class Indexed{constructor(value,index){this.value=value,this.index=index}compare(b){return this.value.compare(b.value)}}exports.Indexed=Indexed;class SetMap{constructor(keys,values){this.keys=keys,this.values=values}set(key,value){const result=this.keys.add(new Indexed(key,this.keys.size()));return new SetMap(result.result,this.values.set(result.value.index,value))}get(key){const result=this.keys.add(new Indexed(key,this.keys.size()));return result.result!==this.keys?null:this.values.get(result.value.index)}merge(b){return b.reduce((aggregator,item,key)=>aggregator.set(key,item),this)}reduce(fn,aggregator){return this.keys.reduce((aggregator,key)=>fn(aggregator,this.values.get(key.index),key.value),aggregator)}}exports.SetMap=SetMap},{}],12:[function(require,module,exports){"use strict";function divide(lower,upper,elements,item,onNew,onExists){const step=upper-lower;if(step<1)return onNew(item,elements,lower);const half=step/2|0,idx=lower+half,elm=elements.get(idx),cmp=elm.compare(item);return cmp<0?divide(half?lower+half:upper,upper,elements,item,onNew,onExists):cmp>0?divide(lower,half?upper-half:lower,elements,item,onNew,onExists):onExists(elm,elements,idx)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.divide=divide;class Tuple{constructor(result,value){this.result=result,this.value=value}}exports.Tuple=Tuple;class SortedSetArray{constructor(elements){this.elements=elements}mempty(){return new SortedSetArray(this.elements.mempty())}size(){return this.elements.size()}add(value){return divide(0,this.elements.size(),this.elements,value,(item,elements,lower)=>new Tuple(new SortedSetArray(elements.insert(lower,item)),item),(item,elements)=>new Tuple(this,item))}remove(value){return divide(0,this.elements.size(),this.elements,value,(item,elements,lower)=>new Tuple(this,value),(item,elements,index)=>new Tuple(new SortedSetArray(elements.remove(index)),item))}has(value){return divide(0,this.elements.size(),this.elements,value,()=>!1,()=>!0)}union(b){return b.reduce((result,item)=>result.add(item).result,this)}intersect(b){return this.reduce((result,item)=>b.has(item)?result.add(item).result:result,this.mempty())}difference(b){return this.reduce((result,item)=>b.has(item)?result:result.add(item).result,this.mempty())}equal(b){return this.size()==b.size()&&b.reduce((equal,item)=>equal?this.has(item):equal,!0)}reduce(fn,accumulator){return this.elements.reduce(fn,accumulator)}}exports.SortedSetArray=SortedSetArray},{}],13:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class Delete{constructor(at,length){this.at=at,this.length=length,this.at=at,this.length=length}}exports.Delete=Delete},{}],14:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const text_1=require("./text"),set_map_1=require("../structures/set-map"),naive_immutable_map_1=require("../structures/naive-immutable-map"),sorted_set_array_1=require("../structures/sorted-set-array"),naive_array_list_1=require("../structures/naive-array-list");exports.createFromOrderer=function(order){return new text_1.Text(order,new set_map_1.SetMap(new sorted_set_array_1.SortedSetArray(new naive_array_list_1.NaiveArrayList([])),new naive_immutable_map_1.NaiveImmutableMap))}},{"../structures/naive-array-list":8,"../structures/naive-immutable-map":9,"../structures/set-map":11,"../structures/sorted-set-array":12,"./text":17}],15:[function(require,module,exports){"use strict";function __export(m){for(var p in m)exports.hasOwnProperty(p)||(exports[p]=m[p])}Object.defineProperty(exports,"__esModule",{value:!0}),__export(require("./insert")),__export(require("./delete")),__export(require("./text")),__export(require("./utils")),__export(require("./factory"))},{"./delete":13,"./factory":14,"./insert":16,"./text":17,"./utils":18}],16:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class Insert{constructor(at,value){this.at=at,this.value=value,this.at=at<0?0:at,this.value=String(value)}}exports.Insert=Insert},{}],17:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const functions_1=require("../functions");class Text{constructor(order,setMap){this.order=order,this.setMap=setMap}next(){return new Text(this.order.next(),this.setMap)}apply(operation){let value=this.setMap.get(this.order);value||(value=[]),value.push(operation),this.setMap=this.setMap.set(this.order,value)}merge(b){return new Text(functions_1.merge(this.order,b.order),this.setMap.merge(b.setMap))}equal(b){return functions_1.equal(this.order,b.order)}reduce(fn,accumulator){return this.setMap.reduce((accumulator,operations,order)=>fn(accumulator,operations,order),accumulator)}}exports.Text=Text},{"../functions":1}],18:[function(require,module,exports){"use strict";function toArray(text){return text.reduce((accumulator,operations)=>operations.reduce(operationToArray,accumulator),[])}function operationToArray(data,op){if(op instanceof insert_1.Insert){let copy=data.slice(0);return(copy=utils_1.ensureArrayLength(copy,op.at)).splice(op.at,0,...op.value.split("")),copy}{if(op.at<0)return data;let copy=data.slice(0);return(copy=utils_1.ensureArrayLength(copy,op.at)).splice(op.at,op.length),copy}}function toString(value){return value.join("")}Object.defineProperty(exports,"__esModule",{value:!0});const insert_1=require("./insert");exports.snapshot=function(text){return text.next()},exports.toArray=toArray;const utils_1=require("../utils");exports.operationToArray=operationToArray,exports.toString=toString,exports.renderString=function(text){return toString(toArray(text))}},{"../utils":19,"./insert":16}],19:[function(require,module,exports){"use strict";function keyToMap(r,i){return r[i]=!0,r}Object.defineProperty(exports,"__esModule",{value:!0}),exports.between=function(value,min,max){return!(value<=min||value>=max)},exports.ensureArrayLength=function(array,len){return array.length(b.hasOwnProperty(k)&&r.push(k),r),[]).sort()},exports.diff=function(a,b){return Object.keys(a).reduce((r,k)=>(b.hasOwnProperty(k)||r.push(k),r),[])}},{}]},{},[3])(3)}); \ No newline at end of file +!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).crdt=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n||e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o(vector[key]=Math.max(this.vector[key]||0,b.vector[key]||0),vector),{});return new VectorClock(this.id,vector)}equal(b){return 0===this.compare(b)}compare(b){const position=utils_1.common(this.vector,b.vector).reduce((result,key)=>result+(this.vector[key]-b.vector[key]),0);if(0!==position)return position;const dif=utils_1.diff(this.vector,b.vector).length-utils_1.diff(b.vector,this.vector).length;if(0!==dif)return dif;const tipPosition=this.vector[this.id]-b.vector[b.id];if(0!==tipPosition)return tipPosition;const ha=b.vector.hasOwnProperty(this.id),hb=this.vector.hasOwnProperty(b.id);return ha||hb?ha&&!hb?-1:hb&&!ha?1:0:this.idvalue.version&&(result=vector.remove(value).result.add(id).result),this.vector=result}toString(){const a=this.vector.reduce((r,i)=>r+i.toString(),"");return`VectorClock2(${this.id},${a})`}next(){return new VectorClock2(this.id.next(),this.vector.remove(this.id).result.add(this.id.next()).result)}equal(b){return this.vector.size()===b.vector.size()&&this.vector.reduce((eq,item)=>{if(eq){const{result:result,value:value}=b.vector.add(item);if(result===b.vector)return value.version===item.version}return!1},!0)}compare(b){return this.lessThan(b)?-1:b.lessThan(this)?1:this.equal(b)?0:this.id.compare(b.id)}lessThan(b){const{everyLEQ:everyLEQ,anyLT:anyLT}=this.vector.intersect(b.vector).reduce(({everyLEQ:everyLEQ,anyLT:anyLT},item)=>{const rA=this.vector.add(item).value,rB=b.vector.add(item).value;return anyLT=anyLT||rA.version{const{result:result,value:value}=b.vector.add(item);return result===b.vector&&value.version>item.version?vector.add(value).result:vector.add(item).result},this.vector.mempty());return new VectorClock2(this.id,vector.union(b.vector))}}exports.VectorClock2=VectorClock2},{}],7:[function(require,module,exports){"use strict";function __export(m){for(var p in m)exports.hasOwnProperty(p)||(exports[p]=m[p])}Object.defineProperty(exports,"__esModule",{value:!0}),__export(require("./naive-array-list")),__export(require("./naive-immutable-map")),__export(require("./set-axioms")),__export(require("./set-map")),__export(require("./sorted-set-array"))},{"./naive-array-list":8,"./naive-immutable-map":9,"./set-axioms":10,"./set-map":11,"./sorted-set-array":12}],8:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class NaiveArrayList{constructor(array){this.array=array||[]}insert(at,item){const clone=this.array.slice(0);return clone.splice(at,0,item),new NaiveArrayList(clone)}remove(at){const clone=this.array.slice(0);return clone.splice(at,1),new NaiveArrayList(clone)}get(at){return this.array[at]}size(){return this.array.length}reduce(fn,aggregator){return this.array.reduce(fn,aggregator)}mempty(){return new NaiveArrayList}}exports.NaiveArrayList=NaiveArrayList},{}],9:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class NaiveImmutableMap{constructor(data){this.data=data,this.data=data||{}}set(key,value){const clone=Object.keys(this.data).reduce((clone,k)=>(clone[k]=this.data[k],clone),{});return clone[key]=value,new NaiveImmutableMap(clone)}get(key){return this.data[key]}}exports.NaiveImmutableMap=NaiveImmutableMap},{}],10:[function(require,module,exports){"use strict";function equal(a,b){return a.equal(b)}function union(a,b){return a.union(b)}function intersect(a,b){return a.intersect(b)}function difference(a,b){return a.difference(b)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.axioms=function(assert,a,b,c){assert(equal(union(union(a,b),c),union(a,union(b,c))),"associative union"),assert(equal(intersect(intersect(a,b),c),intersect(a,intersect(b,c))),"associative intersect"),assert(equal(union(a,intersect(b,c)),union(intersect(a,b),intersect(a,c))),"union distributes over intersection"),assert(equal(intersect(a,union(b,c)),intersect(union(a,b),union(a,c))),"intersection distributes over union"),assert(equal(difference(a,union(b,c)),intersect(difference(a,b),difference(a,c))),"De Morgan's law for union"),assert(equal(difference(a,intersect(b,c)),union(difference(a,b),difference(a,c))),"De Morgan's law for intersect")}},{}],11:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class Indexed{constructor(value,index){this.value=value,this.index=index}compare(b){return this.value.compare(b.value)}}exports.Indexed=Indexed;class SetMap{constructor(keys,values){this.keys=keys,this.values=values}set(key,value){const result=this.keys.add(new Indexed(key,this.keys.size()));return new SetMap(result.result,this.values.set(result.value.index,value))}get(key){const result=this.keys.add(new Indexed(key,this.keys.size()));return result.result!==this.keys?null:this.values.get(result.value.index)}merge(b){return b.reduce((aggregator,item,key)=>aggregator.set(key,item),this)}reduce(fn,aggregator){return this.keys.reduce((aggregator,key)=>fn(aggregator,this.values.get(key.index),key.value),aggregator)}}exports.SetMap=SetMap},{}],12:[function(require,module,exports){"use strict";function divide(lower,upper,elements,item,onNew,onExists){const step=upper-lower;if(step<1)return onNew(item,elements,lower);const half=step/2|0,idx=lower+half,elm=elements.get(idx),cmp=elm.compare(item);return cmp<0?divide(half?lower+half:upper,upper,elements,item,onNew,onExists):cmp>0?divide(lower,half?upper-half:lower,elements,item,onNew,onExists):onExists(elm,elements,idx)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.divide=divide;class Tuple{constructor(result,value){this.result=result,this.value=value}}exports.Tuple=Tuple;class SortedSetArray{constructor(elements){this.elements=elements}mempty(){return new SortedSetArray(this.elements.mempty())}size(){return this.elements.size()}add(value){return divide(0,this.elements.size(),this.elements,value,(item,elements,lower)=>new Tuple(new SortedSetArray(elements.insert(lower,item)),item),(item,elements)=>new Tuple(this,item))}remove(value){return divide(0,this.elements.size(),this.elements,value,(item,elements,lower)=>new Tuple(this,value),(item,elements,index)=>new Tuple(new SortedSetArray(elements.remove(index)),item))}has(value){return divide(0,this.elements.size(),this.elements,value,()=>!1,()=>!0)}union(b){return b.reduce((result,item)=>result.add(item).result,this)}intersect(b){return this.reduce((result,item)=>b.has(item)?result.add(item).result:result,this.mempty())}difference(b){return this.reduce((result,item)=>b.has(item)?result:result.add(item).result,this.mempty())}equal(b){return this.size()==b.size()&&b.reduce((equal,item)=>equal?this.has(item):equal,!0)}reduce(fn,accumulator){return this.elements.reduce(fn,accumulator)}}exports.SortedSetArray=SortedSetArray},{}],13:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class Delete{constructor(at,length){this.at=at,this.length=length,this.at=at,this.length=length}}exports.Delete=Delete},{}],14:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const text_1=require("./text"),set_map_1=require("../structures/set-map"),naive_immutable_map_1=require("../structures/naive-immutable-map"),sorted_set_array_1=require("../structures/sorted-set-array"),naive_array_list_1=require("../structures/naive-array-list");exports.createFromOrderer=function(order){return new text_1.Text(order,new set_map_1.SetMap(new sorted_set_array_1.SortedSetArray(new naive_array_list_1.NaiveArrayList([])),new naive_immutable_map_1.NaiveImmutableMap))}},{"../structures/naive-array-list":8,"../structures/naive-immutable-map":9,"../structures/set-map":11,"../structures/sorted-set-array":12,"./text":17}],15:[function(require,module,exports){"use strict";function __export(m){for(var p in m)exports.hasOwnProperty(p)||(exports[p]=m[p])}Object.defineProperty(exports,"__esModule",{value:!0}),__export(require("./insert")),__export(require("./delete")),__export(require("./text")),__export(require("./utils")),__export(require("./factory"))},{"./delete":13,"./factory":14,"./insert":16,"./text":17,"./utils":18}],16:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});class Insert{constructor(at,value){this.at=at,this.value=value,this.at=at<0?0:at,this.value=String(value)}}exports.Insert=Insert},{}],17:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const functions_1=require("../functions");class Text{constructor(order,setMap){this.order=order,this.setMap=setMap}next(){return new Text(this.order.next(),this.setMap)}apply(operation){let value=this.setMap.get(this.order);value||(value=[]),value.push(operation),this.setMap=this.setMap.set(this.order,value)}merge(b){return new Text(functions_1.merge(this.order,b.order),this.setMap.merge(b.setMap))}equal(b){return functions_1.equal(this.order,b.order)}reduce(fn,accumulator){return this.setMap.reduce((accumulator,operations,order)=>fn(accumulator,operations,order),accumulator)}}exports.Text=Text},{"../functions":1}],18:[function(require,module,exports){"use strict";function toArray(text){return text.reduce((accumulator,operations)=>operations.reduce(operationToArray,accumulator),[])}function operationToArray(data,op){if(op instanceof insert_1.Insert){let copy=data.slice(0);return(copy=utils_1.ensureArrayLength(copy,op.at)).splice(op.at,0,...op.value.split("")),copy}{if(op.at<0)return data;let copy=data.slice(0);return(copy=utils_1.ensureArrayLength(copy,op.at)).splice(op.at,op.length),copy}}function toString(value){return value.join("")}Object.defineProperty(exports,"__esModule",{value:!0});const insert_1=require("./insert");exports.snapshot=function(text){return text.next()},exports.toArray=toArray;const utils_1=require("../utils");exports.operationToArray=operationToArray,exports.toString=toString,exports.renderString=function(text){return toString(toArray(text))}},{"../utils":19,"./insert":16}],19:[function(require,module,exports){"use strict";function keyToMap(r,i){return r[i]=!0,r}Object.defineProperty(exports,"__esModule",{value:!0}),exports.between=function(value,min,max){return!(value<=min||value>=max)},exports.ensureArrayLength=function(array,len){return array.length(b.hasOwnProperty(k)&&r.push(k),r),[]).sort()},exports.diff=function(a,b){return Object.keys(a).reduce((r,k)=>(b.hasOwnProperty(k)||r.push(k),r),[])}},{}]},{},[3])(3)}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 22dfd1e..057950b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "js-crdt", - "version": "1.1.1", + "version": "1.3.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9151647..cc4de3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-crdt", - "version": "1.3.4", + "version": "1.3.5", "description": "CRDT Conflict-free Replication Data Type", "main": "build/index.js", "types": "build/index.d.ts", diff --git a/src/index.ts b/src/index.ts index c3096a9..294a0c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import * as order from './order'; import * as text from './text'; import * as structures from './structures'; -export { +export default { text, order, utils,