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

baobab

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

baobab - npm Package Compare versions

Comparing version 1.1.0-syncwrite to 1.1.0

2

bower.json
{
"name": "baobab",
"main": "build/baobab.min.js",
"version": "1.1.0-syncwrite",
"version": "1.1.0",
"homepage": "https://github.com/Yomguithereal/baobab",

@@ -6,0 +6,0 @@ "author": {

# Changelog
## v1.1.0
* Adding an `immutable` option to the tree.
* Adding a `syncwrite` option to the tree.
* Adding a `get` and `select` event to the tree.
* Facets getters are now applied within the tree's scope.
* `update` events are now exposing the related data for convenience.
* Fixing a `$cursor` related bug.
* Fixing `type.Primitive`.
* Fixing `facet.release` issues.
## v1.0.3

@@ -4,0 +15,0 @@

@@ -17,2 +17,5 @@ /**

// Should the tree's data be immutable?
immutable: false,
// Validation specifications

@@ -22,3 +25,6 @@ validate: null,

// Validation behaviour 'rollback' or 'notify'
validationBehavior: 'rollback'
validationBehavior: 'rollback',
// Should the user be able to write the tree synchronously?
syncwrite: false
};

@@ -14,3 +14,3 @@ /**

Object.defineProperty(Baobab, 'version', {
value: '1.1.0-syncwrite'
value: '1.1.0'
});

@@ -17,0 +17,0 @@

{
"name": "baobab",
"version": "1.1.0-syncwrite",
"version": "1.1.0",
"description": "JavaScript persistent data tree with cursors.",

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

@@ -5,3 +5,3 @@ [![Build Status](https://travis-ci.org/Yomguithereal/baobab.svg)](https://travis-ci.org/Yomguithereal/baobab)

**Baobab** is a JavaScript [persistent](http://en.wikipedia.org/wiki/Persistent_data_structure) data tree supporting cursors and enabling developers to easily navigate and monitor nested data.
**Baobab** is a JavaScript [persistent](http://en.wikipedia.org/wiki/Persistent_data_structure) and optionally [immutable](http://en.wikipedia.org/wiki/Immutable_object) data tree supporting cursors and enabling developers to easily navigate and monitor nested data.

@@ -140,3 +140,3 @@ It is mainly inspired by functional [zippers](http://clojuredocs.org/clojure.zip/zipper) such as Clojure's ones and by [Om](https://github.com/swannodette/om)'s cursors.

Rather, the tree will stack and merge every update order you give it and will only commit them later on.
Rather, the tree will stack and merge every update order you give it and will only commit them later on (note that you remain free to force a synchronous update of the tree through `tree.commit` or by tweaking the tree's [options](#options)).

@@ -352,6 +352,4 @@ This enables the tree to perform efficient mutations and to be able to notify any relevant cursors that the data they are watching over has changed.

tree.on('update', function(e) {
var affectedPaths = e.data.log,
previousState = e.data.previousState;
//...
console.log('Update log', e.data.log);
console.log('Previous data', e.data.previousData);
});

@@ -366,6 +364,17 @@ ```

tree.on('invalid', function(e) {
console.log(e.data.error);
console.log('Error:', e.data.error);
});
```
*get*
Will fire whenever data is accessed in the tree.
```js
tree.on('get', function(e) {
console.log('Path:', e.data.path);
console.log('Target data:', e.data.data);
});
```
##### Cursor level

@@ -541,2 +550,4 @@

* **facets** *object*: a collection of facets to register when the tree is istantiated. For more information, see [facets](#facets).
* **immutable** *boolean* [`false`]: should the tree's data be immutable? Note that immutability is performed through `Object.freeze`.
* **syncwrite** *boolean* [`false`]: when in syncwrite mode, all writes will apply to the tree synchronously, so you can easily read your writes, while keeping update events asynchronous.
* **validate** *function*: a function in charge of validating the tree whenever it updates. See below for an example of such function.

@@ -549,3 +560,3 @@ * **validationBehavior** *string* [`rollback`]: validation behavior of the tree. If `rollback`, the tree won't apply the current update and fire an `invalid` event while `notify` will only emit the event and let the tree enter the invalid state anyway.

function validationFunction(previousState, newState, affectedPaths) {
// Peform validation here and return an error if
// Perform validation here and return an error if
// the tree is invalid

@@ -827,2 +838,18 @@ if (!valid)

Note that, if you want the tree to be immutable, you can now enable it through the `immutable` [option](#options).
**Releasing**
In most complex use cases, you might need to release the manipulated objects, i.e. kill their event emitters and wipe their associated data.
Thus, any Baobab object can be cleared from memory by using the `release` method. This applies to trees, cursors and facets.
```js
tree.release();
cursor.release();
facet.release();
```
Note also that releasing a tree will consequently and automatically release every of its cursors and facets.
## Philosophy

@@ -829,0 +856,0 @@

@@ -16,6 +16,8 @@ /**

function complexHash(type) {
return type + '$' +
(new Date()).getTime() + ('' + Math.random()).replace('0.', '');
}
var uniqid = (function() {
var i = 0;
return function() {
return i++;
};
})();

@@ -51,6 +53,10 @@ /**

this.previousData = null;
this.data = helpers.deepClone(initialData);
this.root = this.select([]);
this.data = initialData;
this.root = this.select();
this.facets = {};
// Immutable tree?
if (this.options.immutable)
helpers.deepFreeze(this.data);
// Boostrapping root cursor's methods

@@ -89,2 +95,4 @@ function bootstrap(name) {

Baobab.prototype.select = function(path) {
path = path || [];
if (arguments.length > 1)

@@ -99,16 +107,6 @@ path = helpers.arrayOf(arguments);

// Complex path?
var complex = type.ComplexPath(path);
var solvedPath;
if (complex)
solvedPath = helpers.solvePath(this.data, path, this);
// Registering a new cursor or giving the already existing one for path
// Computing hash
var hash = path.map(function(step) {
if (type.Function(step))
return complexHash('fn');
else if (type.Object(step))
return complexHash('ob');
if (type.Function(step) || type.Object(step))
return '$' + uniqid() + '$';
else

@@ -118,10 +116,15 @@ return step;

// Registering a new cursor or giving the already existing one for path
var cursor;
if (!this._cursors[hash]) {
var cursor = new Cursor(this, path, solvedPath, hash);
cursor = new Cursor(this, path, hash);
this._cursors[hash] = cursor;
return cursor;
}
else {
return this._cursors[hash];
cursor = this._cursors[hash];
}
// Emitting an event
this.emit('select', {path: path, cursor: cursor});
return cursor;
};

@@ -131,3 +134,2 @@

// TODO: uniq'ing the log through path hashing
// TODO: fix failed tested behaviors
Baobab.prototype.stack = function(spec, skipMerge) {

@@ -143,7 +145,13 @@ var self = this;

// Applying modifications
var result = update(this.data, spec, this.options);
if (this.options.syncwrite) {
var result = update(this.data, spec, this.options);
this.data = result.data;
this.log = [].concat(this.log).concat(result.log);
}
else {
this._transaction = (skipMerge && !Object.keys(this._transaction).length) ?
spec :
merge(this._transaction, spec);
}
this.data = result.data;
this.log = [].concat(this.log).concat(result.log);
// Should we let the user commit?

@@ -169,2 +177,13 @@ if (!this.options.autoCommit)

if (!this.options.syncwrite) {
// Applying the asynchronous transaction
var result = update(this.data, this._transaction, this.options);
this.data = result.data;
this.log = result.log;
}
// Resetting transaction
this._transaction = {};
// Validate?

@@ -190,3 +209,4 @@ var validate = this.options.validate,

log: this.log,
previousState: this.previousData
previousData: this.previousData,
data: this.data
});

@@ -193,0 +213,0 @@

@@ -15,3 +15,3 @@ /**

*/
function Cursor(tree, path, solvedPath, hash) {
function Cursor(tree, path, hash) {
var self = this;

@@ -25,2 +25,6 @@

// Privates
this._identity = '[object Cursor]';
this._additionnalPaths = [];
// Properties

@@ -34,18 +38,25 @@ this.tree = tree;

// Privates
this._identity = '[object Cursor]';
// Path initialization
this.complex = type.ComplexPath(path);
this.solvedPath = path;
// Complex path?
this.complexPath = !!solvedPath;
this.solvedPath = this.complexPath ? solvedPath : this.path;
if (this.complex)
this.solvedPath = helpers.solvePath(this.tree.data, path, this.tree);
if (this.complex)
path.forEach(function(step) {
if (type.Object(step) && '$cursor' in step)
this._additionnalPaths.push(step.$cursor);
}, this);
// Relevant?
this.relevant = this.get() !== undefined;
this.relevant = this.get(false) !== undefined;
// Root listeners
function update(previousState) {
function update(previousData) {
var record = helpers.getIn(previousData, self.solvedPath, self.tree);
if (self.recording && !self.undoing) {
// Handle archive
var record = helpers.getIn(previousState, self.solvedPath, self.tree);
self.archive.add(record);

@@ -55,3 +66,6 @@ }

self.undoing = false;
return self.emit('update');
return self.emit('update', {
data: self.get(false),
previousData: record
});
}

@@ -61,3 +75,3 @@

var log = e.data.log,
previousState = e.data.previousState,
previousData = e.data.previousData,
shouldFire = false,

@@ -72,14 +86,17 @@ c, p, l, m, i, j;

if (!self.path.length)
return update(previousState);
return update(previousData);
// Checking update log to see whether the cursor should update.
if (self.solvedPath)
shouldFire = helpers.solveUpdate(log, [self.solvedPath]);
shouldFire = helpers.solveUpdate(
log,
[self.solvedPath].concat(self._additionnalPaths)
);
// Handling relevancy
var data = self.get() !== undefined;
var data = self.get(false) !== undefined;
if (self.relevant) {
if (data && shouldFire) {
update(previousState);
update(previousData);
}

@@ -94,3 +111,3 @@ else if (!data) {

self.emit('relevant');
update(previousState);
update(previousData);
self.relevant = true;

@@ -108,2 +125,3 @@ }

bound = true;
self.tree.on('update', self.updateHandler);

@@ -129,3 +147,3 @@ };

Cursor.prototype.isLeaf = function() {
return type.Primitive(this.get());
return type.Primitive(this.get(false));
};

@@ -186,3 +204,3 @@

if (last + 1 === this.up().get().length)
if (last + 1 === this.up().get(false).length)
return null;

@@ -199,3 +217,3 @@

var list = this.up().get();
var list = this.up().get(false);

@@ -208,3 +226,3 @@ return this.tree.select(this.solvedPath.slice(0, -1).concat(list.length - 1));

if (!(this.get() instanceof Array))
if (!(this.get(false) instanceof Array))
return null;

@@ -215,2 +233,18 @@

Cursor.prototype.map = function(fn, scope) {
var array = this.get(false),
l = arguments.length;
if (!type.Array(array))
throw Error('baobab.Cursor.map: cannot map a non-list type.');
return array.map(function(item, i) {
return fn.call(
l > 1 ? scope : this,
this.select(i),
i
);
}, this);
};
/**

@@ -220,2 +254,9 @@ * Access

Cursor.prototype.get = function(path) {
var skipEvent = false;
if (path === false) {
path = [];
skipEvent = true;
}
if (arguments.length > 1)

@@ -228,3 +269,10 @@ path = helpers.arrayOf(arguments);

return helpers.getIn(this.tree.data, fullPath, this.tree);
// Retrieving data
var data = helpers.getIn(this.tree.data, fullPath, this.tree);
// Emitting an event
if (!skipEvent)
this.tree.emit('get', {path: fullPath, data: data});
return data;
};

@@ -268,3 +316,3 @@

var path = [].concat(key),
solvedPath = helpers.solvePath(this.get(), path, this.tree);
solvedPath = helpers.solvePath(this.get(false), path, this.tree);

@@ -271,0 +319,0 @@ if (!solvedPath)

@@ -24,2 +24,3 @@ /**

// Properties
this.killed = false;
this.tree = tree;

@@ -124,3 +125,3 @@ this.cursors = {};

data = typeof getter === 'function' ?
getter.call(null, data) :
getter.call(self, data) :
data;

@@ -150,2 +151,4 @@

this.updateHandler = function(e) {
if (self.killed)
return;

@@ -175,2 +178,3 @@ var paths = cursorsPaths(self.cursors).concat(facetsPaths(self.facets));

this.facets = null;
this.killed = true;
this.kill();

@@ -177,0 +181,0 @@ };

@@ -17,3 +17,3 @@ /**

return function() {
decorator();
decorator.apply(null, arguments);
fn.apply(null, arguments);

@@ -59,3 +59,3 @@ };

// Cloning function
function clone(deep, item) {
function cloner(deep, item) {
if (!item ||

@@ -105,29 +105,46 @@ typeof item !== 'object' ||

// Shallow & deep cloning functions
var shallowClone = clone.bind(null, false),
deepClone = clone.bind(null, true);
var shallowClone = cloner.bind(null, false),
deepClone = cloner.bind(null, true);
// Freezing function
var freeze = Object.freeze || Function.prototype;
function deepFreeze(o) {
function freezer(deep, o) {
if (typeof o !== 'object')
return;
var p,
k;
Object.freeze(o);
freeze(o);
if (!deep)
return;
for (k in o) {
p = o[k];
if (Array.isArray(o)) {
if (!o.hasOwnProperty(k) ||
typeof p !== 'object' ||
Object.isFrozen(p))
continue;
// Iterating through the elements
var i,
l;
deepFreeze(p);
for (i = 0, l = o.length; i < l; i++)
deepFreeze(o[i]);
}
else {
var p,
k;
for (k in o) {
p = o[k];
if (!p ||
!o.hasOwnProperty(k) ||
typeof p !== 'object' ||
Object.isFrozen(p))
continue;
deepFreeze(p);
}
}
}
// Shallow & deep freezing function
var freeze = Object.freeze ? freezer.bind(null, false) : Function.prototype,
deepFreeze = Object.freeze ? freezer.bind(null, true) : Function.prototype;
// Simplistic composition

@@ -386,2 +403,3 @@ function compose(fn1, fn2) {

before: before,
freeze: freeze,
deepClone: deepClone,

@@ -388,0 +406,0 @@ deepFreeze: deepFreeze,

@@ -53,6 +53,3 @@ /**

type.Primitive = function(value) {
return !value ||
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean';
return value !== Object(value);
};

@@ -59,0 +56,0 @@

@@ -41,8 +41,2 @@ /**

// If nested object does not exist, we create it
if (type.Primitive(o[lastKey]))
o[lastKey] = {};
else
o[lastKey] = helpers.shallowClone(o[lastKey]);
// Are we at leaf level?

@@ -69,2 +63,4 @@ var leafLevel = Object.keys(spec).some(function(k) {

if (opts.immutable)
helpers.freeze(parent[olderKey]);
break;

@@ -130,5 +126,20 @@ }

}
// Deep freezing the new value?
if (opts.immutable)
helpers.deepFreeze(o);
}
}
else {
// If nested object does not exist, we create it
if (type.Primitive(o[lastKey]))
o[lastKey] = {};
else
o[lastKey] = helpers.shallowClone(o[lastKey]);
// Should we freeze the parent?
if (opts.immutable)
helpers.freeze(o);
for (k in spec) {

@@ -135,0 +146,0 @@

TODO
====
* Fix $cursor?
# v1.1.0
v1.1.0
* Fix $cursor implementation
* Fix path misattribution
* (créer un path solver basé sur la complexité du chemin , doit retourner le chemin statique et les chemins intéressés par le $cursor, récusif donc)
* .map
* cloning history option
* freezing option (immutable)
* facet mapping polymorphism
* missing hook (rather cursor hook)
* sync write async update
* splice polymorphism
* multi-arity on some non destructive setters
* memoizing?
* memoizing accessors
* accessing flat paths rather than iterating through objects?
* nesting facets (check nesting plus access in mappings)
immutability
# documentation
* decide whether to deepClone data fed to the tree?
* .map
* immutable
* synwrite
* documenter events get/select
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