Comparing version 0.7.1 to 0.7.2
@@ -11,3 +11,18 @@ # Changelog | ||
## [0.7.2] — 2018-04-17 | ||
### Changed | ||
- [#83]: Changed `_objectId` property on Automerge map objects to be non-enumerable ([@EthanRBrown], [@ept]) | ||
- [#84]: Changed `_conflicts`, `_state`, and `_actorId` to be non-enumerable properties ([@ept]) | ||
### Fixed | ||
- [#77]: Fixed exception when a list element is inserted and updated in the same change callback | ||
([@mmcgrana], [@ept]) | ||
- [#78]: Better error message when trying to use an unsupported datatype ([@ept]) | ||
## [0.7.1] — 2018-02-26 | ||
### Fixed | ||
- [#69]: `Automerge.load` generates random actorId if none specified ([@saranrapjs]) | ||
@@ -95,3 +110,5 @@ - [#64]: `Automerge.applyChanges()` allows changes to be applied out-of-order ([@jimpick], [@ept]) | ||
[Unreleased]: https://github.com/automerge/automerge/compare/v0.7.0...HEAD | ||
[Unreleased]: https://github.com/automerge/automerge/compare/v0.7.2...HEAD | ||
[0.7.2]: https://github.com/automerge/automerge/compare/v0.7.1...v0.7.2 | ||
[0.7.1]: https://github.com/automerge/automerge/compare/v0.7.0...v0.7.1 | ||
[0.7.0]: https://github.com/automerge/automerge/compare/v0.6.0...v0.7.0 | ||
@@ -106,2 +123,6 @@ [0.6.0]: https://github.com/automerge/automerge/compare/v0.5.0...v0.6.0 | ||
[#84]: https://github.com/automerge/automerge/pull/84 | ||
[#83]: https://github.com/automerge/automerge/pull/83 | ||
[#78]: https://github.com/automerge/automerge/issues/78 | ||
[#77]: https://github.com/automerge/automerge/pull/77 | ||
[#69]: https://github.com/automerge/automerge/pull/69 | ||
@@ -125,7 +146,9 @@ [#64]: https://github.com/automerge/automerge/pull/64 | ||
[@aslakhellesoy]: https://github.com/aslakhellesoy | ||
[@EthanRBrown]: https://github.com/EthanRBrown | ||
[@jeffpeterson]: https://github.com/jeffpeterson | ||
[@jimpick]: https://github.com/jimpick | ||
[@ept]: https://github.com/ept | ||
[@mmcgrana]: https://github.com/mmcgrana | ||
[@mmmm1998]: https://github.com/mmmm1998 | ||
[@pvh]: https://github.com/pvh | ||
[@saranrapjs]: https://github.com/saranrapjs |
@@ -0,0 +0,0 @@ Automerge internal data structures |
@@ -0,0 +0,0 @@ module.exports = function(config) { |
{ | ||
"name": "automerge", | ||
"version": "0.7.1", | ||
"version": "0.7.2", | ||
"description": "Data structures for building collaborative applications", | ||
@@ -14,20 +14,21 @@ "main": "src/automerge.js", | ||
"dependencies": { | ||
"immutable": "^3.8.1", | ||
"immutable": "^3.8.2", | ||
"transit-immutable-js": "^0.7.0", | ||
"transit-js": "^0.8.846", | ||
"uuid": "^3.1.0" | ||
"transit-js": "^0.8.861", | ||
"uuid": "^3.2.1" | ||
}, | ||
"devDependencies": { | ||
"browserify": "^14.4.0", | ||
"browserify": "^14.5.0", | ||
"jsverify": "^0.8.3", | ||
"karma": "^1.7.0", | ||
"karma-browserify": "^5.1.1", | ||
"karma": "^1.7.1", | ||
"karma-browserify": "^5.2.0", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-firefox-launcher": "^1.0.1", | ||
"karma-firefox-launcher": "^1.1.0", | ||
"karma-mocha": "^1.3.0", | ||
"mocha": "^3.4.2", | ||
"sinon": "^3.2.1", | ||
"watchify": "^3.9.0", | ||
"webpack": "^3.0.0" | ||
"karma-sauce-launcher": "^1.2.0", | ||
"mocha": "^3.5.3", | ||
"sinon": "^3.3.0", | ||
"watchify": "^3.11.0", | ||
"webpack": "^3.11.0" | ||
} | ||
} |
@@ -5,2 +5,5 @@ # Automerge | ||
[![Build Status](https://travis-ci.org/automerge/automerge.svg?branch=master)](https://travis-ci.org/automerge/automerge) | ||
[![Browser Test Status](https://saucelabs.com/buildstatus/automerge)](https://saucelabs.com/open_sauce/user/automerge) | ||
Automerge is a library of data structures for building collaborative applications in JavaScript. | ||
@@ -37,3 +40,3 @@ | ||
* If the state was concurrently changed on different devices, Automerge automatically merges the | ||
* If the state was changed concurrently on different devices, Automerge automatically merges the | ||
changes together cleanly, so that everybody ends up in the same state, and no changes are lost. | ||
@@ -46,7 +49,9 @@ | ||
* **Network-agnostic**. Automerge is a pure data structure library that does not care what kind of | ||
* **Network-agnostic**. Automerge is a pure data structure library that does not care about what kind of | ||
network you use: client/server, peer-to-peer, Bluetooth, carrier pigeon, whatever, anything goes. | ||
Bindings to particular networking technologies are handled by separate libraries. For example, see | ||
[MPL](https://github.com/automerge/mpl) for an implementation that uses Automerge in a | ||
peer-to-peer model using [WebRTC](https://webrtc.org/). | ||
peer-to-peer model using [WebRTC](https://webrtc.org/), and | ||
[Hypermerge](https://github.com/automerge/hypermerge) is a peer-to-peer networking layer that uses | ||
[Hypercore](https://github.com/mafintosh/hypercore), part of the [Dat project](https://datproject.org/). | ||
* **Immutable state**. An Automerge object is an immutable snapshot of the application state at one | ||
@@ -68,3 +73,3 @@ point in time. Whenever you make a change, or merge in a change that came from the network, you | ||
* **Fairly portable**. We're not yet making an effort to support old platforms, but we have tested | ||
Automerge in Node.js, Chrome, Firefox, and [Electron](https://electron.atom.io/). | ||
Automerge in Node.js, Chrome, Firefox, Safari, MS Edge, and [Electron](https://electron.atom.io/). | ||
@@ -92,5 +97,9 @@ | ||
The following code samples give a quick overview of how to use Automerge. | ||
For an example of a real-life application built upon Automerge, check out | ||
[Trellis](https://github.com/automerge/trellis), a project management tool. | ||
For examples of real-life applications built upon Automerge, check out: | ||
* [Trellis](https://github.com/automerge/trellis), a project management tool | ||
in the style of [Trello](https://trello.com/). | ||
* [Pixelpusher](https://github.com/automerge/pixelpusher), a pixel art editor | ||
([blog post](https://medium.com/@pvh/pixelpusher-real-time-peer-to-peer-collaboration-with-react-7c7bc8ecbf74)). | ||
```js | ||
@@ -352,7 +361,7 @@ // This is how you load Automerge in Node. In a browser, simply including the | ||
}) | ||
val changes = Automerge.getChanges(currentDoc, newDoc) | ||
let changes = Automerge.getChanges(currentDoc, newDoc) | ||
network.broadcast(JSON.stringify(changes)) | ||
// On another node | ||
val changes = JSON.parse(network.receive()) | ||
let changes = JSON.parse(network.receive()) | ||
newDoc = Automerge.applyChanges(currentDoc, changes) | ||
@@ -359,0 +368,0 @@ ``` |
@@ -0,0 +0,0 @@ const { List, fromJS } = require('immutable') |
@@ -60,4 +60,4 @@ const { Map, List, fromJS } = require('immutable') | ||
if (typeof value === 'undefined') { | ||
return deleteField(state, objectId, key) | ||
if (!['object', 'boolean', 'number', 'string'].includes(typeof value)) { | ||
throw new TypeError('Unsupported type of value: ' + (typeof value)) | ||
} else if (isObject(value)) { | ||
@@ -64,0 +64,0 @@ const [newState, newId] = createNestedObjects(state, value) |
@@ -0,0 +0,0 @@ const { Map, fromJS } = require('immutable') |
@@ -0,0 +0,0 @@ const { Map, Set } = require('immutable') |
@@ -11,3 +11,7 @@ const { Map, List, Set } = require('immutable') | ||
if (edit.action === 'create') { | ||
return opSet.setIn(['cache', edit.obj], Object.freeze({_objectId: edit.obj})) | ||
const object = {} | ||
Object.defineProperty(object, '_objectId', {value: edit.obj}) | ||
Object.defineProperty(object, '_conflicts', {value: Object.freeze({})}) | ||
Object.freeze(object) | ||
return opSet.setIn(['cache', edit.obj], object) | ||
} | ||
@@ -17,3 +21,5 @@ | ||
const conflicts = Object.assign({}, oldObject._conflicts) | ||
const object = Object.assign(Object.create({_conflicts: conflicts}), oldObject) | ||
const object = Object.assign({}, oldObject) | ||
Object.defineProperty(object, '_objectId', {value: oldObject._objectId}) | ||
Object.defineProperty(object, '_conflicts', {value: conflicts}) | ||
@@ -38,3 +44,4 @@ if (edit.action === 'set') { | ||
Object.freeze(conflicts) | ||
return opSet.setIn(['cache', edit.obj], Object.freeze(object)) | ||
if (edit.obj !== OpSet.ROOT_ID) Object.freeze(object) | ||
return opSet.setIn(['cache', edit.obj], object) | ||
} | ||
@@ -45,3 +52,5 @@ | ||
const conflicts = Object.assign({}, oldObject._conflicts) | ||
const object = Object.assign(Object.create({_conflicts: conflicts}), oldObject) | ||
const object = Object.assign({}, oldObject) | ||
Object.defineProperty(object, '_objectId', {value: oldObject._objectId}) | ||
Object.defineProperty(object, '_conflicts', {value: conflicts}) | ||
const value = opSet.getIn(['cache', ref.get('value')]) | ||
@@ -68,3 +77,4 @@ let changed = false | ||
Object.freeze(conflicts) | ||
opSet = opSet.setIn(['cache', ref.get('obj')], Object.freeze(object)) | ||
if (ref.get('obj') !== OpSet.ROOT_ID) Object.freeze(object) | ||
opSet = opSet.setIn(['cache', ref.get('obj')], object) | ||
} | ||
@@ -196,5 +206,6 @@ return opSet | ||
if (isRoot || objType === 'makeMap') { | ||
const conflicts = OpSet.getObjectConflicts(opSet, objectId, this) | ||
obj = Object.create({_conflicts: Object.freeze(conflicts.toJS())}) | ||
obj = {} | ||
const conflicts = OpSet.getObjectConflicts(opSet, objectId, this).toJS() | ||
Object.defineProperty(obj, '_objectId', {value: objectId}) | ||
Object.defineProperty(obj, '_conflicts', {value: Object.freeze(conflicts)}) | ||
for (let field of OpSet.getObjectFields(opSet, objectId)) { | ||
@@ -214,3 +225,3 @@ obj[field] = OpSet.getObjectField(opSet, objectId, field, this) | ||
Object.freeze(obj) | ||
if (!isRoot) Object.freeze(obj) | ||
if (this.cache) this.cache[objectId] = obj | ||
@@ -228,4 +239,5 @@ return obj | ||
function rootObject(state, rootObj) { | ||
Object.assign(Object.getPrototypeOf(rootObj), {_state: state, _actorId: state.get('actorId')}) | ||
Object.freeze(Object.getPrototypeOf(rootObj)) | ||
Object.defineProperty(rootObj, '_state', {value: state}) | ||
Object.defineProperty(rootObj, '_actorId', {value: state.get('actorId')}) | ||
Object.freeze(rootObj) | ||
return rootObj | ||
@@ -241,6 +253,7 @@ } | ||
function applyChanges(root, changes, incremental) { | ||
let opSet = root._state.get('opSet'), diffs = [], diff | ||
let opSet = root._state.get('opSet'), diffs = [] | ||
for (let change of changes) { | ||
[opSet, diff] = OpSet.addChange(opSet, change) | ||
let [newOpSet, diff] = OpSet.addChange(opSet, change) | ||
diffs.push(...diff) | ||
opSet = newOpSet | ||
} | ||
@@ -253,7 +266,9 @@ | ||
if (newRoot === root) { | ||
newRoot = Object.assign(Object.create({_conflicts: root._conflicts}), root) | ||
newRoot = Object.assign({}, root) | ||
Object.defineProperty(newRoot, '_objectId', {value: root._objectId}) | ||
Object.defineProperty(newRoot, '_conflicts', {value: root._conflicts}) | ||
opSet = opSet.setIn(['cache', OpSet.ROOT_ID], newRoot) | ||
} | ||
} else { | ||
[opSet, newRoot] = materialize(opSet) | ||
;[opSet, newRoot] = materialize(opSet) | ||
} | ||
@@ -260,0 +275,0 @@ return rootObject(root._state.set('opSet', opSet), newRoot) |
@@ -20,3 +20,3 @@ const { Map, List, Set } = require('immutable') | ||
const conflicts = OpSet.getObjectConflicts(opSet, objectId, this) | ||
obj = Map().set('_conflicts', conflicts) | ||
obj = Map().set('_conflicts', conflicts).set('_objectId', objectId) | ||
@@ -23,0 +23,0 @@ for (let field of OpSet.getObjectFields(opSet, objectId)) { |
@@ -266,3 +266,5 @@ const { Map, List, Set } = require('immutable') | ||
if (action === 'set' || action === 'del' || action === 'link') { | ||
ops = ops.filter(prev => prev.get('obj') != objectId || prev.get('key') != key) | ||
ops = ops.filter(prev => | ||
prev.get('obj') != objectId || prev.get('key') != key || | ||
(prev.get('action') !== 'set' && prev.get('action') !== 'del' && prev.get('action') !== 'link')) | ||
} | ||
@@ -399,7 +401,5 @@ ops = ops.push(op) | ||
.toSet() | ||
.add('_objectId') | ||
} | ||
function getObjectField(opSet, objectId, key, context) { | ||
if (key === '_objectId') return objectId | ||
if (!validFieldName(key)) return undefined | ||
@@ -406,0 +406,0 @@ const ops = getFieldOps(opSet, objectId, key) |
@@ -131,3 +131,3 @@ const { List, fromJS } = require('immutable') | ||
has (target, key) { | ||
return (key === '_type') || (key === '_state') || (key === '_actorId') || (key === '_conflicts') || | ||
return ['_type', '_state', '_actorId', '_objectId', '_conflicts'].includes(key) || | ||
OpSet.getObjectFields(target.context.state.get('opSet'), target.objectId).has(key) | ||
@@ -134,0 +134,0 @@ }, |
@@ -0,0 +0,0 @@ const { Map } = require('immutable') |
@@ -0,0 +0,0 @@ const { SkipList } = require('./skip_list') |
@@ -0,0 +0,0 @@ const { Map, Set, fromJS } = require('immutable') |
@@ -214,5 +214,5 @@ const assert = require('assert') | ||
assert.deepEqual(nodes[1].getDoc('doc1'), | ||
{_objectId: doc1._objectId, doc1: 'doc1', one: 'one', two: 'two'}) | ||
{doc1: 'doc1', one: 'one', two: 'two'}) | ||
assert.deepEqual(nodes[1].getDoc('doc1'), | ||
{_objectId: doc1._objectId, doc1: 'doc1', one: 'one', two: 'two'}) | ||
{doc1: 'doc1', one: 'one', two: 'two'}) | ||
}) | ||
@@ -306,6 +306,6 @@ | ||
assert.deepEqual(nodes[1].getDoc('doc1'), {_objectId: doc1._objectId, list: ['hello']}) | ||
assert.deepEqual(nodes[2].getDoc('doc1'), {_objectId: doc1._objectId, list: ['hello']}) | ||
assert.deepEqual(nodes[3].getDoc('doc1'), {_objectId: doc1._objectId, list: ['hello']}) | ||
assert.deepEqual(nodes[1].getDoc('doc1'), {list: ['hello']}) | ||
assert.deepEqual(nodes[2].getDoc('doc1'), {list: ['hello']}) | ||
assert.deepEqual(nodes[3].getDoc('doc1'), {list: ['hello']}) | ||
}) | ||
}) |
@@ -0,0 +0,0 @@ const assert = require('assert') |
@@ -0,0 +0,0 @@ const assert = require('assert') |
const assert = require('assert') | ||
const sinon = require('sinon') | ||
const Immutable = require('immutable') | ||
const Automerge = require('../src/Automerge') | ||
const Automerge = require('../src/automerge') | ||
@@ -6,0 +6,0 @@ describe('Automerge.initImmutable()', () => { |
@@ -12,2 +12,3 @@ const assert = require('assert') | ||
assert.strictEqual(doc._objectId, ROOT_ID) | ||
assert.strictEqual('_objectId' in doc, true) | ||
}) | ||
@@ -21,2 +22,3 @@ }) | ||
assert.strictEqual(Automerge.init('customActorId')._actorId, 'customActorId') | ||
assert.strictEqual('_actorId' in doc, true) | ||
}) | ||
@@ -50,10 +52,7 @@ }) | ||
Automerge.change(Automerge.init(), doc => { | ||
assert.deepEqual(Object.keys(doc), ['_objectId']) | ||
assert.deepEqual(Object.keys(doc), []) | ||
doc.key1 = 'value1' | ||
equalsOneOf(Object.keys(doc), ['_objectId', 'key1'], ['key1', '_objectId']) | ||
assert.deepEqual(Object.keys(doc), ['key1']) | ||
doc.key2 = 'value2' | ||
equalsOneOf(Object.keys(doc), | ||
['_objectId', 'key1', 'key2'], ['_objectId', 'key2', 'key1'], | ||
['key1', '_objectId', 'key2'], ['key2', '_objectId', 'key1'], | ||
['key1', 'key2', '_objectId'], ['key2', 'key1', '_objectId']) | ||
equalsOneOf(Object.keys(doc), ['key1', 'key2'], ['key2', 'key1']) | ||
}) | ||
@@ -64,23 +63,25 @@ }) | ||
Automerge.change(Automerge.init(), doc => { | ||
assert.deepEqual(Object.getOwnPropertyNames(doc), ['_objectId']) | ||
assert.deepEqual(Object.getOwnPropertyNames(doc), []) | ||
doc.key1 = 'value1' | ||
equalsOneOf(Object.getOwnPropertyNames(doc), ['_objectId', 'key1'], ['key1', '_objectId']) | ||
assert.deepEqual(Object.getOwnPropertyNames(doc), ['key1']) | ||
doc.key2 = 'value2' | ||
equalsOneOf(Object.getOwnPropertyNames(doc), | ||
['_objectId', 'key1', 'key2'], ['_objectId', 'key2', 'key1'], | ||
['key1', '_objectId', 'key2'], ['key2', '_objectId', 'key1'], | ||
['key1', 'key2', '_objectId'], ['key2', 'key1', '_objectId']) | ||
equalsOneOf(Object.getOwnPropertyNames(doc), ['key1', 'key2'], ['key2', 'key1']) | ||
}) | ||
}) | ||
it('should support bulk assignment with Object.assign()', () => { | ||
Automerge.change(Automerge.init(), doc => { | ||
Object.assign(doc, {key1: 'value1', key2: 'value2'}) | ||
assert.deepEqual(doc, {key1: 'value1', key2: 'value2'}) | ||
}) | ||
}) | ||
it('should support JSON.stringify()', () => { | ||
Automerge.change(Automerge.init(), doc => { | ||
assert.deepEqual(JSON.stringify(doc), '{"_objectId":"00000000-0000-0000-0000-000000000000"}') | ||
assert.deepEqual(JSON.stringify(doc), '{}') | ||
doc.key1 = 'value1' | ||
equalsOneOf(JSON.stringify(doc), | ||
'{"_objectId":"00000000-0000-0000-0000-000000000000","key1":"value1"}', | ||
'{"key1":"value1","_objectId":"00000000-0000-0000-0000-000000000000"}') | ||
assert.deepEqual(JSON.stringify(doc), '{"key1":"value1"}') | ||
doc.key2 = 'value2' | ||
assert.deepEqual(JSON.parse(JSON.stringify(doc)), { | ||
_objectId: ROOT_ID, key1: 'value1', key2: 'value2' | ||
key1: 'value1', key2: 'value2' | ||
}) | ||
@@ -92,13 +93,13 @@ }) | ||
Automerge.change(Automerge.init(), doc => { | ||
assert.deepEqual(doc._inspect, {_objectId: ROOT_ID}) | ||
assert.deepEqual(Automerge.inspect(doc), {_objectId: ROOT_ID}) | ||
assert.deepEqual(doc._inspect, {}) | ||
assert.deepEqual(Automerge.inspect(doc), {}) | ||
doc.key1 = 'value1' | ||
assert.deepEqual(doc._inspect, {_objectId: ROOT_ID, key1: 'value1'}) | ||
assert.deepEqual(Automerge.inspect(doc), {_objectId: ROOT_ID, key1: 'value1'}) | ||
assert.deepEqual(doc._inspect, {key1: 'value1'}) | ||
assert.deepEqual(Automerge.inspect(doc), {key1: 'value1'}) | ||
doc.key2 = 'value2' | ||
assert.deepEqual(doc._inspect, { | ||
_objectId: ROOT_ID, key1: 'value1', key2: 'value2' | ||
key1: 'value1', key2: 'value2' | ||
}) | ||
assert.deepEqual(Automerge.inspect(doc), { | ||
_objectId: ROOT_ID, key1: 'value1', key2: 'value2' | ||
key1: 'value1', key2: 'value2' | ||
}) | ||
@@ -172,3 +173,3 @@ }) | ||
assert.deepEqual(JSON.parse(JSON.stringify(doc)), { | ||
_objectId: ROOT_ID, list: [1, 2, 3], empty: [] | ||
list: [1, 2, 3], empty: [] | ||
}) | ||
@@ -182,6 +183,6 @@ assert.deepEqual(JSON.stringify(doc.list), '[1,2,3]') | ||
assert.deepEqual(doc._inspect, { | ||
_objectId: ROOT_ID, list: [1, 2, 3], empty: [] | ||
list: [1, 2, 3], empty: [] | ||
}) | ||
assert.deepEqual(Automerge.inspect(doc), { | ||
_objectId: ROOT_ID, list: [1, 2, 3], empty: [] | ||
list: [1, 2, 3], empty: [] | ||
}) | ||
@@ -188,0 +189,0 @@ }) |
@@ -0,0 +0,0 @@ const assert = require('assert') |
186
test/test.js
@@ -14,3 +14,3 @@ const assert = require('assert') | ||
it('should initially be an empty map', () => { | ||
assert.deepEqual(s1, { _objectId: ROOT_ID }) | ||
assert.deepEqual(s1, {}) | ||
}) | ||
@@ -39,9 +39,7 @@ | ||
assert.deepEqual(doc, { | ||
_objectId: ROOT_ID, first: 'one', second: 'two' | ||
first: 'one', second: 'two' | ||
}) | ||
}) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID}) | ||
assert.deepEqual(s2, { | ||
_objectId: ROOT_ID, first: 'one', second: 'two' | ||
}) | ||
assert.deepEqual(s1, {}) | ||
assert.deepEqual(s2, {first: 'one', second: 'two'}) | ||
}) | ||
@@ -78,4 +76,4 @@ | ||
}) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID}) | ||
assert.deepEqual(s2, {_objectId: ROOT_ID, counter: 3}) | ||
assert.deepEqual(s1, {}) | ||
assert.deepEqual(s2, {counter: 3}) | ||
}) | ||
@@ -114,5 +112,15 @@ | ||
}) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, one: 1}) | ||
assert.deepEqual(s2, {_objectId: ROOT_ID, two: 2}) | ||
assert.deepEqual(s1, {one: 1}) | ||
assert.deepEqual(s2, {two: 2}) | ||
}) | ||
it('should work with Object.assign merges', () => { | ||
s1 = Automerge.change(s1, doc1 => { | ||
doc1.stuff = {foo: 'bar', baz: 'blur'} | ||
}) | ||
s1 = Automerge.change(s1, doc1 => { | ||
doc1.stuff = Object.assign({}, doc1.stuff, {baz: 'updated!'}) | ||
}) | ||
assert.deepEqual(s1, {stuff: {foo: 'bar', baz: 'updated!'}}) | ||
}) | ||
}) | ||
@@ -126,3 +134,3 @@ | ||
assert.strictEqual(s1.zip, 'zap') | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, foo: 'bar', zip: 'zap'}) | ||
assert.deepEqual(s1, {foo: 'bar', zip: 'zap'}) | ||
}) | ||
@@ -136,3 +144,3 @@ | ||
assert.strictEqual(s1.answer, 42) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, foo: 'bar', answer: 42}) | ||
assert.deepEqual(s1, {foo: 'bar', answer: 42}) | ||
}) | ||
@@ -145,3 +153,3 @@ | ||
assert.strictEqual(s1.something, null) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, something: null}) | ||
assert.deepEqual(s1, {something: null}) | ||
}) | ||
@@ -168,2 +176,11 @@ | ||
}) | ||
it('should not allow assignment of unsupported datatypes', () => { | ||
Automerge.change(s1, doc => { | ||
assert.throws(() => { doc.foo = undefined }, /Unsupported type of value: undefined/) | ||
assert.throws(() => { doc.foo = {prop: undefined} }, /Unsupported type of value: undefined/) | ||
assert.throws(() => { doc.foo = () => {} }, /Unsupported type of value: function/) | ||
assert.throws(() => { doc.foo = Symbol('foo') }, /Unsupported type of value: symbol/) | ||
}) | ||
}) | ||
}) | ||
@@ -176,7 +193,2 @@ | ||
assert.notEqual(s1.nested._objectId, ROOT_ID) | ||
assert.deepEqual(s1, { | ||
_objectId: ROOT_ID, | ||
nested: {_objectId: s1.nested._objectId} | ||
}) | ||
assert.deepEqual(s1.nested, {_objectId: s1.nested._objectId}) | ||
}) | ||
@@ -192,7 +204,4 @@ | ||
}) | ||
assert.deepEqual(s1, { | ||
_objectId: ROOT_ID, | ||
nested: {_objectId: s1.nested._objectId, foo: 'bar', one: 1} | ||
}) | ||
assert.deepEqual(s1.nested, {_objectId: s1.nested._objectId, foo: 'bar', one: 1}) | ||
assert.deepEqual(s1, {nested: {foo: 'bar', one: 1}}) | ||
assert.deepEqual(s1.nested, {foo: 'bar', one: 1}) | ||
assert.strictEqual(s1.nested.foo, 'bar') | ||
@@ -208,7 +217,4 @@ assert.strictEqual(s1.nested['foo'], 'bar') | ||
}) | ||
assert.deepEqual(s1, { | ||
_objectId: ROOT_ID, | ||
textStyle: {_objectId: s1.textStyle._objectId, bold: false, fontSize: 12} | ||
}) | ||
assert.deepEqual(s1.textStyle, {_objectId: s1.textStyle._objectId, bold: false, fontSize: 12}) | ||
assert.deepEqual(s1, {textStyle: {bold: false, fontSize: 12}}) | ||
assert.deepEqual(s1.textStyle, {bold: false, fontSize: 12}) | ||
assert.strictEqual(s1.textStyle.bold, false) | ||
@@ -226,5 +232,3 @@ assert.strictEqual(s1.textStyle.fontSize, 12) | ||
assert.strictEqual(s1.textStyle.fontSize, 14) | ||
assert.deepEqual(s1.textStyle, { | ||
_objectId: s1.textStyle._objectId, typeface: 'Optima', bold: false, fontSize: 14 | ||
}) | ||
assert.deepEqual(s1.textStyle, {typeface: 'Optima', bold: false, fontSize: 14}) | ||
}) | ||
@@ -239,11 +243,3 @@ | ||
}) | ||
assert.deepEqual(s1, { | ||
_objectId: ROOT_ID, a: { | ||
_objectId: s1.a._objectId, b: { | ||
_objectId: s1.a.b._objectId, c: { | ||
_objectId: s1.a.b.c._objectId, d: { | ||
_objectId: s1.a.b.c.d._objectId, e: { | ||
_objectId: s1.a.b.c.d.e._objectId, f: { | ||
_objectId: s1.a.b.c.d.e.f._objectId, | ||
g: 'h', i: 'j'}}}}}}}) | ||
assert.deepEqual(s1, {a: { b: { c: { d: { e: { f: { g: 'h', i: 'j'}}}}}}}) | ||
assert.strictEqual(s1.a.b.c.d.e.f.g, 'h') | ||
@@ -261,8 +257,8 @@ assert.strictEqual(s1.a.b.c.d.e.f.i, 'j') | ||
assert.deepEqual(s1.myPet, { | ||
_objectId: s1.myPet._objectId, species: 'dog', legs: 4, breed: 'dachshund' | ||
species: 'dog', legs: 4, breed: 'dachshund' | ||
}) | ||
assert.strictEqual(s1.myPet.breed, 'dachshund') | ||
assert.deepEqual(s2.myPet, { | ||
_objectId: s2.myPet._objectId, species: 'koi', variety: '紅白', | ||
colors: {_objectId: s2.myPet.colors._objectId, red: true, white: true, black: false} | ||
species: 'koi', variety: '紅白', | ||
colors: {red: true, white: true, black: false} | ||
}) | ||
@@ -277,3 +273,3 @@ assert.strictEqual(s2.myPet.breed, undefined) | ||
s1 = Automerge.change(s1, doc => doc.color = {red: 255, green: 127, blue: 0}) | ||
assert.deepEqual(s1.color, {_objectId: s1.color._objectId, red: 255, green: 127, blue: 0}) | ||
assert.deepEqual(s1.color, {red: 255, green: 127, blue: 0}) | ||
s1 = Automerge.change(s1, doc => doc.color = '#ff7f00') | ||
@@ -300,3 +296,3 @@ assert.strictEqual(s1.color, '#ff7f00') | ||
assert.strictEqual(s1.textStyle.bold, undefined) | ||
assert.deepEqual(s1.textStyle, {_objectId: s1.textStyle._objectId, typeface: 'Optima', fontSize: 12}) | ||
assert.deepEqual(s1.textStyle, {typeface: 'Optima', fontSize: 12}) | ||
}) | ||
@@ -310,3 +306,3 @@ | ||
assert.strictEqual(s1.textStyle, undefined) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, title: 'Hello'}) | ||
assert.deepEqual(s1, {title: 'Hello'}) | ||
}) | ||
@@ -328,3 +324,3 @@ | ||
s1 = Automerge.change(s1, doc => doc.noodles.insertAt(1, 'ramen')) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, noodles: ['udon', 'ramen', 'soba']}) | ||
assert.deepEqual(s1, {noodles: ['udon', 'ramen', 'soba']}) | ||
assert.deepEqual(s1.noodles, ['udon', 'ramen', 'soba']) | ||
@@ -339,3 +335,3 @@ assert.strictEqual(s1.noodles[0], 'udon') | ||
s1 = Automerge.change(s1, doc => doc.noodles = ['udon', 'ramen', 'soba']) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, noodles: ['udon', 'ramen', 'soba']}) | ||
assert.deepEqual(s1, {noodles: ['udon', 'ramen', 'soba']}) | ||
assert.deepEqual(s1.noodles, ['udon', 'ramen', 'soba']) | ||
@@ -412,11 +408,11 @@ assert.strictEqual(s1.noodles[0], 'udon') | ||
s1 = Automerge.change(s1, doc => doc.noodles[0].dishes.push('miso')) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID, noodles: [ | ||
{_objectId: s1.noodles[0]._objectId, type: 'ramen', dishes: ['tonkotsu', 'shoyu', 'miso']}, | ||
{_objectId: s1.noodles[1]._objectId, type: 'udon', dishes: ['tempura udon']} | ||
assert.deepEqual(s1, {noodles: [ | ||
{type: 'ramen', dishes: ['tonkotsu', 'shoyu', 'miso']}, | ||
{type: 'udon', dishes: ['tempura udon']} | ||
]}) | ||
assert.deepEqual(s1.noodles[0], { | ||
_objectId: s1.noodles[0]._objectId, type: 'ramen', dishes: ['tonkotsu', 'shoyu', 'miso'] | ||
type: 'ramen', dishes: ['tonkotsu', 'shoyu', 'miso'] | ||
}) | ||
assert.deepEqual(s1.noodles[1], { | ||
_objectId: s1.noodles[1]._objectId, type: 'udon', dishes: ['tempura udon'] | ||
type: 'udon', dishes: ['tempura udon'] | ||
}) | ||
@@ -439,3 +435,2 @@ }) | ||
assert.deepEqual(s1, { | ||
_objectId: ROOT_ID, | ||
noodles: ['wonton', 'pho'], | ||
@@ -455,3 +450,3 @@ japaneseNoodles: ['udon', 'soba', 'ramen'] | ||
s1 = Automerge.change(s1, doc => doc.noodles[1] = {type: 'soba', options: ['hot', 'cold']}) | ||
assert.deepEqual(s1.noodles, ['udon', {_objectId: s1.noodles[1]._objectId, type: 'soba', options: ['hot', 'cold']}, 'ramen']) | ||
assert.deepEqual(s1.noodles, ['udon', {type: 'soba', options: ['hot', 'cold']}, 'ramen']) | ||
s1 = Automerge.change(s1, doc => doc.noodles[1] = ['hot soba', 'cold soba']) | ||
@@ -463,2 +458,10 @@ assert.deepEqual(s1.noodles, ['udon', ['hot soba', 'cold soba'], 'ramen']) | ||
it('should allow list creation and assignment in the same change callback', () => { | ||
s1 = Automerge.change(Automerge.init(), doc => { | ||
doc.letters = ['a', 'b', 'c'] | ||
doc.letters[1] = 'd' | ||
}) | ||
assert.strictEqual(s1.letters[1], 'd') | ||
}) | ||
it('should handle arbitrary-depth nesting', () => { | ||
@@ -476,3 +479,2 @@ s1 = Automerge.change(s1, doc => doc.maze = [[[[[[[['noodles', ['here']]]]]]]]]) | ||
assert.deepEqual(s1, { | ||
_objectId: ROOT_ID, | ||
japaneseNoodles: ['udon', 'soba', 'ramen'], | ||
@@ -503,3 +505,3 @@ theBestNoodles: ['udon', 'soba', 'ramen'] | ||
assert.strictEqual(s3.hello, 'world') | ||
assert.deepEqual(s3, {_objectId: ROOT_ID, foo: 'bar', hello: 'world' }) | ||
assert.deepEqual(s3, {foo: 'bar', hello: 'world' }) | ||
assert.deepEqual(s3._conflicts, {}) | ||
@@ -513,6 +515,6 @@ }) | ||
if (s1._actorId > s2._actorId) { | ||
assert.deepEqual(s3, {_objectId: ROOT_ID, field: 'one'}) | ||
assert.deepEqual(s3, {field: 'one'}) | ||
assert.deepEqual(s3._conflicts, {field: {[s2._actorId]: 'two'}}) | ||
} else { | ||
assert.deepEqual(s3, {_objectId: ROOT_ID, field: 'two'}) | ||
assert.deepEqual(s3, {field: 'two'}) | ||
assert.deepEqual(s3._conflicts, {field: {[s1._actorId]: 'one'}}) | ||
@@ -542,8 +544,8 @@ } | ||
s1 = Automerge.merge(Automerge.merge(s1, s2), s3) | ||
equalsOneOf(s1.field, 'string', ['list'], {_objectId: s3.field._objectId, thing: 'map'}) | ||
equalsOneOf(s1.field, 'string', ['list'], {thing: 'map'}) | ||
if (s1.field === 'string') { | ||
assert.deepEqual(s1._conflicts, {field: {[s2._actorId]: ['list'], [s3._actorId]: {_objectId: s3.field._objectId, thing: 'map'}}}) | ||
assert.deepEqual(s1._conflicts, {field: {[s2._actorId]: ['list'], [s3._actorId]: {thing: 'map'}}}) | ||
} else if (Automerge.equals(s1.field, ['list'])) { | ||
assert.deepEqual(s1._conflicts, {field: {[s1._actorId]: 'string', [s3._actorId]: {_objectId: s3.field._objectId, thing: 'map'}}}) | ||
} else if (Automerge.equals(s1.field, {_objectId: s3.field._objectId, thing: 'map'})) { | ||
assert.deepEqual(s1._conflicts, {field: {[s1._actorId]: 'string', [s3._actorId]: {thing: 'map'}}}) | ||
} else if (Automerge.equals(s1.field, {thing: 'map'})) { | ||
assert.deepEqual(s1._conflicts, {field: {[s1._actorId]: 'string', [s2._actorId]: ['list']}}) | ||
@@ -560,5 +562,5 @@ } else { | ||
s3 = Automerge.merge(s1, s2) | ||
equalsOneOf(s3.field, 'string', {_objectId: s2.field._objectId, innerKey: 42}) | ||
equalsOneOf(s3.field, 'string', {innerKey: 42}) | ||
if (s3.field === 'string') { | ||
assert.deepEqual(s3._conflicts, {field: {[s2._actorId]: {_objectId: s2.field._objectId, innerKey: 42}}}) | ||
assert.deepEqual(s3._conflicts, {field: {[s2._actorId]: {innerKey: 42}}}) | ||
} else { | ||
@@ -578,7 +580,7 @@ assert.deepEqual(s3._conflicts, {field: {[s1._actorId]: 'string'}}) | ||
if (s1._actorId > s2._actorId) { | ||
assert.deepEqual(s3.list, [{_objectId: s1.list[0]._objectId, map1: true, key: 1}]) | ||
assert.deepEqual(s3.list._conflicts, [{[s2._actorId]: {_objectId: s2.list[0]._objectId, map2: true, key: 2}}]) | ||
assert.deepEqual(s3.list, [{map1: true, key: 1}]) | ||
assert.deepEqual(s3.list._conflicts, [{[s2._actorId]: {map2: true, key: 2}}]) | ||
} else { | ||
assert.deepEqual(s3.list, [{_objectId: s2.list[0]._objectId, map2: true, key: 2}]) | ||
assert.deepEqual(s3.list._conflicts, [{[s1._actorId]: {_objectId: s1.list[0]._objectId, map1: true, key: 1}}]) | ||
assert.deepEqual(s3.list, [{map2: true, key: 2}]) | ||
assert.deepEqual(s3.list._conflicts, [{[s1._actorId]: {map1: true, key: 1}}]) | ||
} | ||
@@ -592,9 +594,9 @@ }) | ||
equalsOneOf(s3.config, | ||
{_objectId: s1.config._objectId, background: 'blue'}, | ||
{_objectId: s2.config._objectId, logo_url: 'logo.png'} | ||
{background: 'blue'}, | ||
{logo_url: 'logo.png'} | ||
) | ||
if (s3.config.background === 'blue') { | ||
assert.deepEqual(s3._conflicts.config, {[s2._actorId]: {_objectId: s2.config._objectId, logo_url: 'logo.png'}}) | ||
assert.deepEqual(s3._conflicts.config, {[s2._actorId]: {logo_url: 'logo.png'}}) | ||
} else { | ||
assert.deepEqual(s3._conflicts.config, {[s1._actorId]: {_objectId: s1.config._objectId, background: 'blue'}}) | ||
assert.deepEqual(s3._conflicts.config, {[s1._actorId]: {background: 'blue'}}) | ||
} | ||
@@ -608,6 +610,6 @@ }) | ||
s3 = Automerge.change(s3, doc => doc.field = 'three') | ||
assert.deepEqual(s3, {_objectId: ROOT_ID, field: 'three'}) | ||
assert.deepEqual(s3, {field: 'three'}) | ||
assert.deepEqual(s3._conflicts, {}) | ||
s2 = Automerge.merge(s2, s3) | ||
assert.deepEqual(s2, {_objectId: ROOT_ID, field: 'three'}) | ||
assert.deepEqual(s2, {field: 'three'}) | ||
assert.deepEqual(s2._conflicts, {}) | ||
@@ -622,6 +624,3 @@ }) | ||
s3 = Automerge.merge(s1, s2) | ||
assert.deepEqual(s3, { | ||
_objectId: ROOT_ID, | ||
list: ['one', 'two', 'three', 'four'] | ||
}) | ||
assert.deepEqual(s3, {list: ['one', 'two', 'three', 'four']}) | ||
assert.deepEqual(s3._conflicts, {}) | ||
@@ -648,5 +647,5 @@ }) | ||
s3 = Automerge.merge(s1, s2) | ||
assert.deepEqual(s1, {_objectId: ROOT_ID}) | ||
assert.deepEqual(s2, {_objectId: ROOT_ID, bestBird: 'magpie'}) | ||
assert.deepEqual(s3, {_objectId: ROOT_ID, bestBird: 'magpie'}) | ||
assert.deepEqual(s1, {}) | ||
assert.deepEqual(s2, {bestBird: 'magpie'}) | ||
assert.deepEqual(s3, {bestBird: 'magpie'}) | ||
assert.deepEqual(s3._conflicts, {}) | ||
@@ -676,5 +675,3 @@ }) | ||
assert.deepEqual(s1.animals, { | ||
_objectId: s1.animals._objectId, | ||
birds: { | ||
_objectId: s1.animals.birds._objectId, | ||
pink: 'flamingo', brown: 'sparrow', black: 'starling' | ||
@@ -684,4 +681,4 @@ }, | ||
}) | ||
assert.deepEqual(s2.animals, {_objectId: s1.animals._objectId, mammals: ['badger']}) | ||
assert.deepEqual(s3.animals, {_objectId: s1.animals._objectId, mammals: ['badger']}) | ||
assert.deepEqual(s2.animals, {mammals: ['badger']}) | ||
assert.deepEqual(s3.animals, {mammals: ['badger']}) | ||
}) | ||
@@ -743,3 +740,3 @@ | ||
let s = Automerge.load(Automerge.save(Automerge.init())) | ||
assert.deepEqual(s, {_objectId: ROOT_ID}) | ||
assert.deepEqual(s, {}) | ||
}) | ||
@@ -763,5 +760,3 @@ | ||
let s2 = Automerge.load(Automerge.save(s1)) | ||
assert.deepEqual(s2, {_objectId: ROOT_ID, todos: [ | ||
{title: 'water plants', done: false, _objectId: s1.todos[0]._objectId} | ||
]}) | ||
assert.deepEqual(s2, {todos: [{title: 'water plants', done: false}]}) | ||
}) | ||
@@ -800,9 +795,6 @@ | ||
assert.deepEqual(Automerge.getHistory(s).map(state => state.snapshot), [ | ||
{_objectId: ROOT_ID, | ||
config: {_objectId: s.config._objectId, background: 'blue'}}, | ||
{_objectId: ROOT_ID, | ||
config: {_objectId: s.config._objectId, background: 'blue'}, | ||
{config: {background: 'blue'}}, | ||
{config: {background: 'blue'}, | ||
birds: ['mallard']}, | ||
{_objectId: ROOT_ID, | ||
config: {_objectId: s.config._objectId, background: 'blue'}, | ||
{config: {background: 'blue'}, | ||
birds: ['oystercatcher', 'mallard']}]) | ||
@@ -929,3 +921,3 @@ }) | ||
let s3 = Automerge.applyChanges(Automerge.init(), [changes[1]]) | ||
assert.deepEqual(s3, {_objectId: ROOT_ID}) | ||
assert.deepEqual(s3, {}) | ||
assert.deepEqual(Automerge.getMissingDeps(s3), {[s1._actorId]: 1}) | ||
@@ -932,0 +924,0 @@ s3 = Automerge.applyChanges(s3, [changes[0]]) |
@@ -0,0 +0,0 @@ const assert = require('assert') |
@@ -0,0 +0,0 @@ const assert = require('assert') |
@@ -0,0 +0,0 @@ var path = require('path'); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
30
512
212922
12
3803
Updatedimmutable@^3.8.2
Updatedtransit-js@^0.8.861
Updateduuid@^3.2.1