MCPcopy
hub / github.com/Yomguithereal/baobab

github.com/Yomguithereal/baobab @2.6.1 sqlite

repository ↗ · DeepWiki ↗ · release 2.6.1 ↗
136 symbols 324 edges 22 files 61 documented · 45%
README

Build Status

Baobab

Baobab is a JavaScript & TypeScript persistent and immutable (at least by default) data tree supporting cursors and enabling developers to easily navigate and monitor nested data through events.

It is mainly inspired by functional zippers (such as Clojure's ones) and by Om's cursors.

It aims at providing a centralized model holding an application's state and can be paired with React easily through mixins, higher order components, wrapper components or decorators (available there).

Fun fact: A Baobab, or Adansonia digitata, is a very big and magnificent African tree.

Summary

Example

var Baobab = require('baobab');

var tree = new Baobab({
  palette: {
    colors: ['yellow', 'purple'],
    name: 'Glorious colors'
  }
});

var colorsCursor = tree.select('palette', 'colors');

colorsCursor.on('update', function() {
  console.log('Selected colors have updated!');
});

colorsCursor.push('orange');

Installation

If you want to use Baobab with node.js or browserify/webpack etc., you can use npm.

npm install baobab

# Or if you need the latest dev version
npm install git+https://github.com/Yomguithereal/baobab.git

If you want to use it in the browser, just include the minified script located here.

Note that the library comes along with its own declaration files so you can use it comfortably with TypeScript also.

<script src="https://github.com/Yomguithereal/baobab/raw/2.6.1/baobab.min.js"></script>

Or install with bower:

bower install baobab

The library (as a standalone) currently weighs ~8kb gzipped.

Usage

Basics

Instantiation

Creating a tree is as simple as instantiating Baobab with an initial data set.

var Baobab = require('baobab');

var tree = new Baobab({hello: 'world'});

// Retrieving data from your tree
tree.get();
>>> {hello: 'world'}

Cursors

Then you can create cursors to easily access nested data in your tree and listen to changes concerning the part of the tree you selected.

// Considering the following tree
var tree = new Baobab({
  palette: {
    name: 'fancy',
    colors: ['blue', 'yellow', 'green']
  }
});

// Creating a cursor on the palette
var paletteCursor = tree.select('palette');
paletteCursor.get();
>>> {name: 'fancy', colors: ['blue', 'yellow', 'green']}

// Creating a cursor on the palette's colors
var colorsCursor = tree.select('palette', 'colors');
colorsCursor.get();
>>> ['blue', 'yellow', 'green']

// Creating a cursor on the palette's third color
var thirdColorCursor = tree.select('palette', 'colors', 2);
thirdColorCursor.get();
>>> 'green'

// Note that you can also perform subselections if needed
var colorCursor = paletteCursor.select('colors');

Updates

A baobab tree can obviously be updated. However, one has to understand that, even if you can write the tree synchronously, update events won't be, at least by default, fired until next frame.

If you really need to fire an update synchronously (typically if you store a form's state within your app's state, for instance), your remain free to use the tree.commit() method or tweak the tree's options to fit your needs.

Important: Note that the tree, being a persistent data structure, will shift the references of the objects it stores in order to enable immutable comparisons between one version of the state and another (this is especially useful when using strategies as such as React's pure rendering).

Example

var tree = new Baobab({hello: 'world'});

var initialState = tree.get();
tree.set('hello', 'monde');

// After asynchronous update...
assert(initialState !== tree.get());


tree/cursor.set

Replaces value at the given key or the cursor's value altogether if no value is supplied.

It will also work if you want to replace a list's item.

// Replacing the cursor's value
var newValue = cursor.set(newValue);

// Setting a precise key
var newValue = cursor.set('key', newValue);

// Setting a nested key
var newValue = cursor.set(['one', 'two'], newValue);
var newValue = cursor.select('one', 'two').set(newValue);
var newValue = cursor.select('one').set('two', newValue);
tree/cursor.unset

Unsets the given key.

It will also work if you want to delete a list's item.

// Removing data at cursor
cursor.unset();

// Removing a precise key
cursor.unset('key');

// Removing a nested key
cursor.unset(['one', 'two']);
tree/cursor.push

Pushes a value into the selected list. This will of course fail if the selected node is not a list.

// Pushing a value
var newList = cursor.push(newValue);

// Pushing a value in the list at key
var newList = cursor.push('key', newValue);

// Pushing into a nested path
var newList = cursor.push(['one', 'two'], newValue);
var newList = cursor.select('one', 'two').push(newValue);
var newList = cursor.select('one').push('two', 'world');
tree/cursor.unshift

Unshifts a value into the selected list. This will of course fail if the selected node is not a list.

// Unshifting a value
var newList = cursor.unshift(newValue);

// Unshifting a value in the list at key
var newList = cursor.unshift('key', newValue);

// Unshifting into a nested path
var newList = cursor.unshift(['one', 'two'], newValue);
var newList = cursor.select('one', 'two').unshift(newValue);
var newList = cursor.select('one').unshift('two', newValue);
tree/cursor.concat

Concatenates a list into the selected list. This will of course fail if the selected node is not a list.

// Concatenating a list
var newList = cursor.concat(list);

// Concatenating a list in the list at key
var newList = cursor.concat('key', list);

// Concatenating into a nested path
var newList = cursor.concat(['one', 'two'], list);
var newList = cursor.select('one', 'two').concat(list);
var newList = cursor.select('one').concat('two', list);
tree/cursor.pop

Removes the last item of the selected list. This will of course fail if the selected node is not a list.

// Popping the list
var newList = cursor.pop();

// Popping the list at key
var newList = cursor.pop('key');

// Popping list at path
var newList = cursor.pop(['one', 'two']);
var newList = cursor.select('one', 'two').pop();
var newList = cursor.select('one').pop('two');
tree/cursor.shift

Removes the first item of the selected list. This will of course fail if the selected node is not a list.

// Shifting the list
var newList = cursor.shift();

// Shifting the list at key
var newList = cursor.shift('key');

// Shifting list at path
var newList = cursor.shift(['one', 'two']);
var newList = cursor.select('one', 'two').shift();
var newList = cursor.select('one').shift('two');
tree/cursor.splice

Splices the selected list. This will of course fail if the selected node is not a list.

The splice specifications works the same as for Array.prototype.splice. There is one exception though: Per specification, splice deletes no values if the deleteCount argument is not parseable as a number. The splice implementation of Baobab instead throws an error, if the given deleteCount argument could not be parsed.

// Splicing the list
var newList = cursor.splice([1, 1]);

// Omitting the deleteCount argument makes splice delete no elements.
var newList = cursor.splice([1]);

// Inserting an item etc.
var newList = cursor.splice([1, 0, 'newItem']);
var newList = cursor.splice([1, 0, 'newItem1', 'newItem2']);

// Splicing the list at key
var newList = cursor.splice('key', [1, 1]);

// Splicing list at path
var newList = cursor.splice(['one', 'two'], [1, 1]);
var newList = cursor.select('one', 'two').splice([1, 1]);
var newList = cursor.select('one').splice('two', [1, 1]);
tree/cursor.apply

Applies the given function to the selected value.

var inc = function(nb) {
  return nb + 1;
};

// Applying the function
var newList = cursor.apply(inc);

// Applying the function at key
var newList = cursor.apply('key', inc);

// Applying the function at path
var newList = cursor.apply(['one', 'two'], inc);
var newList = cursor.select('one', 'two').apply(inc);
var newList = cursor.select('one').apply('two', inc);
tree/cursor.merge

Shallow merges the selected object with another one. This will of course fail if the selected node is not an object.

// Merging
var newList = cursor.merge({name: 'John'});

// Merging at key
var newList = cursor.merge('key', {name: 'John'});

// Merging at path
var newList = cursor.merge(['one', 'two'], {name: 'John'});
var newList = cursor.select('one', 'two').merge({name: 'John'});
var newList = cursor.select('one').merge('two', {name: 'John'});
tree/cursor.deepMerge

Deep merges the selected object with another one. This will of course fail if the selected node is not an object.

// Merging
var newList = cursor.deepMerge({user: {name: 'John'}});

// Merging at key
var newList = cursor.deepMerge('key', {user: {name: 'John'}});

// Merging at path
var newList = cursor.deepMerge(['one', 'two'], {user: {name: 'John'}});
var newList = cursor.select('one', 'two').deepMerge({user: {name: 'John'}});
var newList = cursor.select('one').deepMerge('two', {user: {name: 'John'}});

Events

Whenever an update is committed, events are fired to notify relevant parts of the tree that data was changed so that bound elements, UI components, for instance, may update.

Note however that only relevant cursors will be notified of a change.

Events can be bound to either the tree or cursors using the on method.

Example

// Considering the following tree
var tree = new Baobab({
  users: {
    john: {
      firstname: 'John',
      lastname: 'Silver'
    },
    jack: {
      firstname: 'Jack',
      lastname: 'Gold'
    }
  }
});

// And the following cursors
var usersCursor = tree.select('users'),
    johnCursor = usersCursor.select('john'),
    jackCursor = usersCursor.select('jack');

// If we update both users
johnCursor.set('firstname', 'John the third');
jackCursor.set('firstname', 'Jack the second');
// Every cursor above will be notified of the update

// But if we update only john
johnCursor.set('firstname', 'John the third');
// Only the users and john cursors will be notified
Tree level

update

Will fire if the tree is updated (this concerns the asynchronous updates of the tree).

tree.on('update', function(e) {
  var eventData = e.data;

  console.log('Current data:', eventData.currentData);
  console.log('Previous data:', eventData.previousData);
  console.log('Transaction details:', eventData.transaction);
  console.log('Affected paths', eventData.paths);
});

write

Will fire whenever the tree is written (synchronously, unlike the update event).

tree.on('write', function(e) {
  console.log('Affected path:', e.data.path);
});

invalid

Will fire if the validate function (see options) returned an error for the current update.

tree.on('invalid', function(e) {
  console.log('Error:', e.data.error);
});

get

Will fire whenever data is accessed in the tree.

tree.on('get', function(e) {
  console.log('Path:', e.data.path);
  console.log('Solved path:', e.data.solvedPath);
  console.log('Target data:', e.data.data);
});

select

Will fire whenever a path is selected in the tree.

tree.on('select', function(e) {
  console.log('Path:', e.data.path);
  console.log('Resultant cursor:', e.data.cursor);
});
Cursor level

update

Will fire if data watched over by the cursor has updated.

cursor.on('update', function(e) {
  var eventData = e.data;
  console.log('Current data:', eventData.currentData);
  console.log('Previous data:', eventData.previousData);
});
N.B.

For more information concerning Baobab's event emitting, see the emmett library.

Advanced

Polymorphisms

If you

Extension points exported contracts — how you extend this code

PlainObject (Interface)
(no doc)
src/baobab.d.ts
BaobabOptions (Interface)
(no doc)
src/baobab.d.ts
MonkeyOptions (Interface)
(no doc)
src/baobab.d.ts

Core symbols most depended-on inside this repo

get
called by 236
src/cursor.js
select
called by 84
src/baobab.js
splice
called by 39
src/helpers.js
assertIsFrozen
called by 21
test/utils.ts
getIn
called by 20
src/helpers.js
makeError
called by 17
src/helpers.js
exists
called by 16
src/cursor.js
map
called by 14
src/cursor.js

Shape

Method 65
Function 43
Class 25
Interface 3

Languages

TypeScript100%

Modules by API surface

src/cursor.js39 symbols
src/helpers.js23 symbols
src/baobab.js16 symbols
src/baobab.d.ts14 symbols
test/suites/monkey.ts9 symbols
src/monkey.js9 symbols
test/suites/cursor.ts7 symbols
src/watcher.js7 symbols
test/suites/baobab.ts6 symbols
test/utils.ts2 symbols
src/update.js2 symbols
test/suites/watcher.ts1 symbols

Dependencies from manifests, versioned

@babel/cli7.7.4 · 1×
@babel/core7.7.4 · 1×
@babel/node7.7.4 · 1×
@babel/preset-env7.7.4 · 1×
@babel/register7.7.4 · 1×
@types/async3.0.7 · 1×
@types/expect24.3.0 · 1×
@types/lodash4.14.149 · 1×
@types/mocha7.0.1 · 1×
@types/node13.7.0 · 1×
async3.1.0 · 1×

For agents

$ claude mcp add baobab \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact