├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── messages.js ├── schema.proto └── trie.js ├── package-lock.json ├── package.json └── test ├── basic.js ├── diff.js ├── fork.js ├── helpers ├── create.js ├── replicate.js └── verify.js ├── indexing.js ├── iterator.js ├── linking.js ├── list.js ├── replicate.js ├── trie.js └── watch.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:7.10 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: npm install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: npm test 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Andrew Osheroff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # union-hyperdb 2 | [![CircleCI](https://circleci.com/gh/andrewosh/union-hyperdb.svg?style=svg)](https://circleci.com/gh/andrewosh/union-hyperdb) 3 | [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) 4 | 5 | union-hyperdb is a wrapper around multiple [hyperdbs](https://github.com/mafintosh/hyperdb) that supports nesting and/or layering of hyperdbs within a parent database: 6 | 7 | 1. Lightweight __forking__ and __layering__ operations are supported through a `parents` relationship. If an entry can't be found in the main tree, the search will traverse into all parents. This creates a union filesystem-like view over the underlying databases. 8 | 2. Cross-db __symlinking__ (of both live and versioned dbs) is supported through special `Link` entries. 9 | 10 | No external indexing is required, and sub-dbs are instantiated dynamically when first needed. 11 | 12 | ### Installation 13 | `npm i union-hyperdb` 14 | 15 | ### Usage 16 | 17 | ```js 18 | var hypercore = require('hypercore') 19 | var uniondb = require('union-hyperdb') 20 | var ram = require('random-access-memory') 21 | 22 | // A hypercore factory function is required, because union-hyperdb constructs hyperdbs dynamically. 23 | var factory = function (key, version, cb) { 24 | return hypercore(ram, key, { version }) 25 | } 26 | 27 | var baseTree = factory() 28 | var db = uniondb(baseTree, factory) 29 | ``` 30 | 31 | ### API 32 | union-hyperdb implements the hyperdb API, with the addition of the methods described below. 33 | 34 | #### `var uniondb = uniondb(factory, opts)` 35 | 36 | Since union-hyperdbs need to dynamically create sub-hyperdbs, it needs to be provided with a hypercore factory function. 37 | 38 | `factory` is a function that takes a key and an optional version, and returns a hypercore: 39 | 40 | `opts` are hyperdb options, but with the important addition of: 41 | ```js 42 | { 43 | parents: [] // A list of { key: key, version: version} objects specifying parent append-trees. 44 | } 45 | ``` 46 | 47 | #### `mount(name, target, opts cb)` 48 | 49 | Create a link record that references another hyperdb (specified by `target`) 50 | `name` is the symlink's absolute path 51 | `target` can be either: 52 | 1. An object of the form `{ key: , path: }` 53 | 2. A string of the form `dat:///` or `/` (assuming the latter form is a dat key + a path). 54 | `opts` is an optional object of the form: 55 | ```js 56 | { 57 | version: // The desired db version, if the link should be versioned (static). 58 | } 59 | ``` 60 | 61 | #### `lexIterator(opts)` 62 | 63 | Create a lexicographic iterator over the database. 64 | `opts` are LevelDB-style options (`gt`, `gte`, `lt`, `lte`, and `reverse`). 65 | 66 | #### `index(cb)` 67 | 68 | Compute a layer index over the entire database, such that lookups will require constant time (in the number of layers). 69 | 70 | __Note__: If one does *not* call `index` before calling read-intensive methods, then those methods might need to sequentially search each layer for content. 71 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var p = require('path') 2 | var EventEmitter = require('events').EventEmitter 3 | 4 | var map = require('async-each') 5 | var each = map 6 | var inherits = require('inherits') 7 | var bulk = require('bulk-write-stream') 8 | var codecs = require('codecs') 9 | var nanoiterator = require('nanoiterator') 10 | var duplexify = require('duplexify') 11 | var pumpify = require('pumpify') 12 | var through = require('through2') 13 | var merge = require('merge-stream') 14 | var maybe = require('call-me-maybe') 15 | 16 | var Trie = require('./lib/trie') 17 | var messages = require('./lib/messages') 18 | 19 | const META_PATH = 'META/' 20 | const INDEX_PATH = 'INDEX/' 21 | const DATA_PATH = 'DATA/' 22 | const LINKS_PATH = 'LINKS/' 23 | 24 | module.exports = UnionDB 25 | 26 | // This behavior is extracted to the top so that it's super visible. 27 | function resolveEntryConflicts (entries) { 28 | var winningEntry = { 29 | deleted: false, 30 | // Hopefully there are never more than 1e9 layers... 31 | layerIndex: 1e9 32 | } 33 | entries.forEach(function (entry) { 34 | if (entry.deleted) { 35 | // If any entry declares that this value has been deleted in this layer, then assume the 36 | // value has been deleted. 37 | winningEntry.deleted = true 38 | } else if (entry.layerIndex === 0) { 39 | // If any entry declares that the latest value is in the current layer, then use that value. 40 | winningEntry.layerIndex = 0 41 | } else { 42 | // Otherwise, the entry with the most recent layer index should win. 43 | winningEntry.layerIndex = Math.min(winningEntry.layerIndex, entry.layerIndex) 44 | } 45 | if (entry.link) winningEntry.link = entry.link 46 | }) 47 | return winningEntry 48 | } 49 | 50 | function UnionDB (factory, key, opts) { 51 | if (!(this instanceof UnionDB)) return new UnionDB(factory, key, opts) 52 | if (key && !(key instanceof Buffer) && !(typeof key === 'string')) { 53 | return new UnionDB(factory, null, opts) 54 | } 55 | 56 | this.opts = Object.assign({}, opts || {}) 57 | this.parent = this.opts.parent 58 | this.key = key 59 | if (this.opts.map) { 60 | this.map = this.opts.map 61 | delete this.opts.map 62 | } 63 | if (this.opts.reduce) { 64 | this.reduce = this.opts.reduce 65 | delete this.opts.reduce 66 | } 67 | 68 | if (this.opts.version) { 69 | this.versions = messages.Version.decode(this.opts.version) 70 | this.localVersion = this.versions.localVersion 71 | this.linkVersions = this.versions.linkVersions 72 | } 73 | 74 | this.localKey = null 75 | 76 | this._codec = (this.opts.valueEncoding) ? codecs(this.opts.valueEncoding) : null 77 | this._links = {} 78 | this._linkTrie = new Trie() 79 | this._changeHandlers = [] 80 | this._factory = factory 81 | 82 | // Indexing the layers will record which layer last modified each entry (for faster lookups). 83 | // If a UnionDB is not indexed, each `get` will have to traverse the list of layers. 84 | this.indexed = false 85 | 86 | // Set in open. 87 | this._db = null 88 | this._feeds = null 89 | 90 | var self = this 91 | 92 | this._ready = open() 93 | 94 | this.ready = function (cb) { 95 | if (!cb) return self._ready 96 | self._ready.then(function () { 97 | return cb(null) 98 | }).catch(function (err) { 99 | return cb(err) 100 | }) 101 | } 102 | 103 | async function open () { 104 | if (self.key && self.parent) { 105 | var error = new Error('Cannot specify both a parent key and layers.') 106 | throw error 107 | } 108 | let opts = Object.assign({}, self.opts, { valueEncoding: 'binary', version: undefined }) 109 | let db = factory(self.key, opts) 110 | if (self.localVersion) db = db.checkout(self.localVersion, opts) 111 | self._db = db 112 | return new Promise((resolve, reject) => { 113 | self._db.ready(function (err) { 114 | if (err) return reject(err) 115 | self.localKey = self._db.local.key 116 | self._feeds = self._db.feeds 117 | self._db.watch(LINKS_PATH, function () { 118 | // Refresh the links whenever any change. 119 | return self._loadLinks() 120 | }) 121 | if (self.key) { 122 | return self._load(err => { 123 | if (err) return reject(err) 124 | return resolve() 125 | }) 126 | } 127 | self.key = db.key 128 | return self._save(err => { 129 | if (err) return reject(err) 130 | return resolve() 131 | }) 132 | }) 133 | }) 134 | } 135 | } 136 | inherits(UnionDB, EventEmitter) 137 | 138 | UnionDB.prototype._getMetadata = function (cb) { 139 | var self = this 140 | 141 | this._db.ready(function (err) { 142 | if (err) return cb(err) 143 | self._db.get(META_PATH, function (err, nodes) { 144 | if (err) return cb(err) 145 | if (!nodes || !nodes.length) return cb(null, null) 146 | if (nodes.length > 1) console.error('Conflict in metadata file -- using first node\'s value.') 147 | return cb(null, messages.Metadata.decode(nodes[0].value)) 148 | }) 149 | }) 150 | } 151 | 152 | UnionDB.prototype._saveMetadata = function (cb) { 153 | var self = this 154 | 155 | this._db.ready(function (err) { 156 | if (err) return cb(err) 157 | self._db.put(META_PATH, messages.Metadata.encode({ 158 | parents: [self.parent], 159 | indexed: self.indexed 160 | }), function (err) { 161 | if (err) return cb(err) 162 | return cb(null) 163 | }) 164 | }) 165 | } 166 | 167 | UnionDB.prototype._loadDatabase = function (record, opts, cb) { 168 | if (typeof opts === 'function') return this._loadDatabase(record, null, opts) 169 | opts = opts || {} 170 | 171 | var dbMetadata = (record.db) ? record.db : record 172 | 173 | this._createUnionDB(dbMetadata.key, { 174 | version: dbMetadata.version, 175 | valueEncoding: opts.valueEncoding || this.opts.valueEncoding, 176 | sparse: opts.sparse || false 177 | }, function (err, db) { 178 | if (err) return cb(err) 179 | record.db = db 180 | return cb() 181 | }) 182 | } 183 | 184 | UnionDB.prototype._loadParent = function (cb) { 185 | if (this.parent) { 186 | return this._loadDatabase(this.parent, cb) 187 | } 188 | process.nextTick(cb, null) 189 | } 190 | 191 | UnionDB.prototype._isIndexed = function (cb) { 192 | this._getMetadata(function (err, metadata) { 193 | if (err) return cb(err) 194 | return cb(null, metadata.indexed) 195 | }) 196 | } 197 | 198 | UnionDB.prototype._loadLinks = function (cb) { 199 | var self = this 200 | 201 | this._findEntries(LINKS_PATH, function (err, entries) { 202 | if (err) return cb(err) 203 | 204 | each(Object.values(entries), function (entry, next) { 205 | var linkRecord = entry.link 206 | // TODO: might need a better link identifier. 207 | var linkId = linkRecord.localPath 208 | 209 | if (self._links[linkId]) return next() 210 | 211 | if (self.linkVersions && self.linkVersions[linkRecord.localPath]) { 212 | linkRecord.db.version = self.linkVersions[linkRecord.localPath] 213 | } 214 | 215 | self._loadDatabase(linkRecord, { 216 | valueEncoding: linkRecord.valueEncoding, 217 | sparse: true 218 | }, function (err) { 219 | if (err) return next(err) 220 | self._links[linkId] = linkRecord 221 | self._linkTrie.add(linkRecord.localPath, linkRecord, { 222 | push: !!linkRecord.flat 223 | }) 224 | return next() 225 | }) 226 | }, function (err) { 227 | if (!cb && err) throw err 228 | if (cb) return cb(err) 229 | }) 230 | }) 231 | } 232 | 233 | UnionDB.prototype._load = function (cb) { 234 | var self = this 235 | 236 | this._getMetadata(function (err, metadata) { 237 | if (err) return cb(err) 238 | if (!metadata) return cb() 239 | // TODO: handle the case of multiple parents? (i.e. a merge) 240 | self.parent = metadata.parents[0] 241 | self._loadParent(function (err) { 242 | if (err) return cb(err) 243 | return self._loadLinks(cb) 244 | }) 245 | }) 246 | } 247 | 248 | UnionDB.prototype._save = function (cb) { 249 | var self = this 250 | 251 | this._saveMetadata(function (err) { 252 | if (err) return cb(err) 253 | if (self.parent) return self._loadParent(cb) 254 | return cb() 255 | }) 256 | } 257 | 258 | UnionDB.prototype._createUnionDB = function (key, opts, cb) { 259 | let db = new UnionDB(this._factory, key, opts) 260 | db.ready(err => { 261 | if (err) return cb(err) 262 | return cb(null, db) 263 | }) 264 | } 265 | 266 | UnionDB.prototype._prereturn = function (result) { 267 | if (!result) return null 268 | result = result.map(n => { 269 | return Object.assign({}, {...n}, { 270 | key: n.key.slice(DATA_PATH.length), 271 | value: (this._codec) ? this._codec.decode(n.value) : n.value 272 | }) 273 | }) 274 | if (this.map) result = result.map(n => this.map(n)) 275 | if (this.reduce) result = result.reduce(this.reduce) 276 | return result 277 | } 278 | 279 | UnionDB.prototype._get = function (idx, key, cb) { 280 | var self = this 281 | 282 | if (!this._db) return process.nextTick(cb, new Error('Attempting to get from an uninitialized database.')) 283 | if (idx === 0) { 284 | return this._db.get(p.join(DATA_PATH, key), function (err, nodes) { 285 | if (err) return cb(err) 286 | if (!nodes) return cb(null, null) 287 | return cb(null, self._prereturn(nodes)) 288 | }) 289 | } 290 | return this.parent.db._get(idx - 1, key, cb) 291 | } 292 | 293 | UnionDB.prototype._makeIndex = function (key, idx, deleted, link) { 294 | var entry = { 295 | deleted: deleted, 296 | layerIndex: idx 297 | } 298 | if (link) entry.link = link 299 | return messages.Entry.encode(entry) 300 | } 301 | 302 | UnionDB.prototype._putIndex = async function (key, idx, deleted, cb) { 303 | var indexPath = p.join(INDEX_PATH, key) 304 | 305 | var index = this._makeIndex(key, idx, deleted) 306 | 307 | return maybe(cb, new Promise(async (resolve, reject) => { 308 | this._db.put(indexPath, index, err => { 309 | if (err) return reject(err) 310 | return resolve() 311 | }) 312 | })) 313 | } 314 | 315 | UnionDB.prototype._putIndices = function (indices, cb) { 316 | var self = this 317 | 318 | this._db.batch(Object.keys(indices).map(function (key) { 319 | var value = indices[key] 320 | return { 321 | type: 'put', 322 | key: key, 323 | value: self._makeIndex(key, value.layerIndex, value.deleted) 324 | } 325 | }), function (err) { 326 | return cb(err) 327 | }) 328 | } 329 | 330 | UnionDB.prototype._putLink = function (key, path, opts, cb) { 331 | if (typeof opts === 'function') return this._putLink(key, path, null, cb) 332 | opts = opts || {} 333 | var self = this 334 | 335 | var linkPath = p.join(LINKS_PATH, path) 336 | 337 | this._db.put(linkPath, this._makeIndex(linkPath, 0, false, { 338 | db: { 339 | key: key, 340 | version: opts.version 341 | }, 342 | localPath: path, 343 | remotePath: opts.remotePath, 344 | valueEncoding: opts.valueEncoding, 345 | flat: !!opts.flat 346 | }), function (err) { 347 | if (err) return cb(err) 348 | return self._loadLinks(cb) 349 | }) 350 | } 351 | 352 | UnionDB.prototype._delLink = function (path, cb) { 353 | var linkPath = p.join(LINKS_PATH, path) 354 | this._db.put(linkPath, this._makeIndex(linkPath, 0, true, cb)) 355 | } 356 | 357 | UnionDB.prototype._getIndex = function (cb) { 358 | var self = this 359 | 360 | this._ready.then(function () { 361 | self._isIndexed(function (err, indexed) { 362 | if (err) return cb(err) 363 | if (indexed || !self.parent) { 364 | // If already indexed, or there's no parent, just spit out the contents of the INDEX subtree. 365 | getLocalIndex(function (err, localIndex) { 366 | if (err) return cb(err) 367 | return cb(null, localIndex) 368 | }) 369 | } else { 370 | // Otherwise, recursively get the index of the parent and merge with our INDEX subtree. 371 | self.parent.db._getIndex(function (err, parentIndex) { 372 | if (err) return cb(err) 373 | getLocalIndex(function (err, localIndex) { 374 | if (err) return cb(err) 375 | Object.keys(parentIndex).forEach(function (pkey) { 376 | if (!localIndex[pkey]) { 377 | var parentEntry = parentIndex[pkey] 378 | parentEntry.layerIndex++ 379 | localIndex[pkey] = parentEntry 380 | } 381 | }) 382 | return cb(null, localIndex) 383 | }) 384 | }) 385 | } 386 | }) 387 | }).catch(function (err) { 388 | return cb(err) 389 | }) 390 | 391 | function getLocalIndex (cb) { 392 | var allEntries = {} 393 | self._db.list(INDEX_PATH, function (err, nodes) { 394 | if (err) return cb(err) 395 | if (!nodes || !nodes.length) return cb(null, allEntries) 396 | // We only care about the last put for each key in the layer. 397 | nodes.forEach(function (nodes) { 398 | allEntries[nodes[0].key] = nodes.map(function (node) { 399 | return messages.Entry.decode(node.value) 400 | }) 401 | }) 402 | Object.keys(allEntries).forEach(function (key) { 403 | var entries = allEntries[key] 404 | allEntries[key] = resolveEntryConflicts(entries) 405 | }) 406 | return cb(null, allEntries) 407 | }) 408 | } 409 | } 410 | 411 | UnionDB.prototype._getEntryValues = function (key, nodes, cb) { 412 | var entries = nodes.map(function (node) { 413 | return messages.Entry.decode(node.value) 414 | }) 415 | 416 | // If there are any conflicting entries, resolve them according to the the strategy above. 417 | if (entries.length > 1) { 418 | this.emit('conflict', key, entries) 419 | } 420 | var resolvedEntry = resolveEntryConflicts(entries) 421 | 422 | if (resolvedEntry.deleted) { 423 | return cb(null, null) 424 | } 425 | return this._get(resolvedEntry.layerIndex, key, cb) 426 | } 427 | 428 | // BEGIN Public API 429 | 430 | UnionDB.prototype.mount = async function (key, path, opts, cb) { 431 | if (typeof opts === 'function') return this.mount(key, path, null, opts) 432 | return maybe(cb, new Promise(async (resolve, reject) => { 433 | await this.ready() 434 | this._putLink(key, path, opts, err => { 435 | if (err) return reject(err) 436 | return resolve() 437 | }) 438 | })) 439 | } 440 | 441 | UnionDB.prototype.get = async function (key, opts, cb) { 442 | if (typeof opts === 'function') return this.get(key, null, opts) 443 | opts = opts || {} 444 | 445 | var self = this 446 | 447 | return maybe(cb, new Promise((resolve, reject) => { 448 | this._ready.then(function () { 449 | // If there's a link, recurse into the linked db. 450 | var link = self._linkTrie.get(key, { enclosing: true }) 451 | if (link && link.localPath === key) { 452 | // TODO: hacky way of getting link records. 453 | // This shows that conflict handling for links needs to be fixed. 454 | var linkMeta = Object.assign({}, link, { db: null }) 455 | return resolve([{ 456 | key, 457 | value: linkMeta 458 | }]) 459 | } else if (link) { 460 | var remotePath = p.resolve(key.slice(link.localPath.length)) 461 | if (link.remotePath) remotePath = p.resolve(p.join(link.remotePath, remotePath)) 462 | return link.db.get(remotePath, opts, (err, nodes) => { 463 | if (err) return reject(err) 464 | return resolve(nodes) 465 | }) 466 | } 467 | 468 | // Else, first check this database, then recurse into parents. 469 | self._db.get(p.join(INDEX_PATH, key), function (err, nodes) { 470 | if (err) return cb(err) 471 | if ((!nodes || !nodes.length) && self.parent) { 472 | return self.parent.db.get(key, opts, (err, values) => { 473 | if (err) return reject(err) 474 | return resolve(values) 475 | }) 476 | } 477 | 478 | if (!nodes || !nodes.length) return resolve(null) 479 | 480 | return self._getEntryValues(key, nodes, (err, values) => { 481 | if (err) return reject(err) 482 | return resolve(values) 483 | }) 484 | }) 485 | }).catch(function (err) { 486 | return reject(err) 487 | }) 488 | })) 489 | } 490 | 491 | UnionDB.prototype.put = async function (key, value, cb) { 492 | var self = this 493 | 494 | return maybe(cb, new Promise((resolve, reject) => { 495 | this._ready.then(function () { 496 | // If there's a link, recurse into the linked db. 497 | if (self._links[key]) return cb(new Error('Cannot overwrite a symlink. Please delete first.')) 498 | var link = self._linkTrie.get(key, { enclosing: true }) 499 | if (link) { 500 | return link.db.put(key.slice(link.localPath), value, err => { 501 | if (err) return reject(err) 502 | return resolve() 503 | }) 504 | } 505 | 506 | var encoded = (self._codec) ? self._codec.encode(value) : value 507 | 508 | var dataPath = p.join(DATA_PATH, key) 509 | 510 | // TODO: This operation should be transactional. 511 | self._db.put(dataPath, encoded, function (err) { 512 | if (err) return cb(err) 513 | self._putIndex(key, 0, false, function (err) { 514 | if (err) return reject(err) 515 | return resolve() 516 | }) 517 | }) 518 | }).catch(function (err) { 519 | return reject(err) 520 | }) 521 | })) 522 | } 523 | 524 | UnionDB.prototype.batch = async function (records, cb) { 525 | var self = this 526 | 527 | return maybe(cb, new Promise((resolve, reject) => { 528 | this._ready.then(function () { 529 | // Warning: records is mutated in this map to save an iteration. 530 | var stats = records.map(function (record) { 531 | var stat = { 532 | type: 'put', 533 | key: p.join(DATA_PATH, record.key), 534 | value: (self._codec) ? self._codec.encode(record.value) : record.value 535 | } 536 | record.key = p.join(INDEX_PATH, record.key) 537 | record.value = self._makeIndex(record.key, 0, false) 538 | return stat 539 | }) 540 | var toBatch = records.concat(stats) 541 | return self._db.batch(toBatch, (err, nodes) => { 542 | if (err) return reject(err) 543 | return resolve(nodes) 544 | }) 545 | }).catch(function (err) { 546 | return reject(err) 547 | }) 548 | })) 549 | } 550 | 551 | UnionDB.prototype.createWriteStream = function () { 552 | var self = this 553 | return bulk.obj(write) 554 | 555 | function write (batch, cb) { 556 | self.batch(batch, cb) 557 | } 558 | } 559 | 560 | UnionDB.prototype.del = async function (key, cb) { 561 | var self = this 562 | 563 | return maybe(cb, new Promise(async (resolve, reject) => { 564 | await this._ready 565 | await self._putIndex(key, 0, true) 566 | self._db.del(p.join(DATA_PATH, key), err => { 567 | if (err) return reject(err) 568 | if (self._links[key]) { 569 | self._delLink(key, err => { 570 | if (err) return reject(err) 571 | delete self._links[key] 572 | return resolve(null) 573 | }) 574 | } else { 575 | return resolve(null) 576 | } 577 | }) 578 | })) 579 | } 580 | 581 | /** 582 | * Indexing with many layers is a very expensive operation unless an index has already been constructed 583 | * (either in this layer, or in a previous one -- the more recent the better). 584 | * 585 | * Diff streams have to be created for each layer, starting with the base layer. Entries must be 586 | * created one layer at a time. 587 | * 588 | * Any future forks of this database will be able to take advantage of precomputed indices, 589 | * reducing the cost of indexing subsequent layers. 590 | */ 591 | UnionDB.prototype.index = function (opts, cb) { 592 | if (typeof opts === 'function') return this.index(null, opts) 593 | opts = opts || {} 594 | var self = this 595 | 596 | this._getIndex(function (err, index) { 597 | if (err) return cb(err) 598 | writeIndex(index) 599 | }) 600 | 601 | function writeIndex (index) { 602 | self._putIndices(index, function (err) { 603 | if (err) return cb(err) 604 | self.indexed = true 605 | self._saveMetadata(function (err) { 606 | if (err) return cb(err) 607 | return cb() 608 | }) 609 | }) 610 | } 611 | } 612 | 613 | UnionDB.prototype._findEntries = function (dir, cb) { 614 | var self = this 615 | 616 | self._db.list(dir, function (err, nodes) { 617 | if (err) return cb(err) 618 | if (!nodes || !nodes.length) return cb(null, {}) 619 | var entries = {} 620 | nodes.forEach(function (nodes) { 621 | var newEntries = nodes.map(function (node) { 622 | return messages.Entry.decode(node.value) 623 | }) 624 | entries[nodes[0].key] = resolveEntryConflicts(newEntries) 625 | }) 626 | return cb(null, entries) 627 | }) 628 | } 629 | 630 | UnionDB.prototype._list = function (prefix, dir, cb) { 631 | var self = this 632 | 633 | this._isIndexed(function (err, indexed) { 634 | if (err) return cb(err) 635 | if (!indexed && self.parent) { 636 | self.parent.db.ready().then(function () { 637 | self.parent.db._findEntries(dir, function (err, parentEntries) { 638 | if (err) return cb(err) 639 | self._findEntries(dir, function (err, entries) { 640 | if (err) return cb(err) 641 | Object.assign(parentEntries, entries) 642 | return processEntries(null, parentEntries) 643 | }) 644 | }) 645 | }).catch(function (err) { 646 | return cb(err) 647 | }) 648 | } else { 649 | self._findEntries(dir, processEntries) 650 | } 651 | }) 652 | 653 | function processEntries (err, entries) { 654 | if (err) return cb(err) 655 | var list = [] 656 | for (var name in entries) { 657 | if (!entries[name].deleted) { 658 | list.push(name.slice(prefix.length)) 659 | } 660 | } 661 | return cb(null, list) 662 | } 663 | } 664 | 665 | UnionDB.prototype.list = async function (dir, cb) { 666 | var self = this 667 | return maybe(cb, new Promise((resolve, reject) => { 668 | this._ready.then(function () { 669 | return self._list(INDEX_PATH, p.join(INDEX_PATH, dir), (err, values) => { 670 | if (err) return reject(err) 671 | return resolve(values) 672 | }) 673 | }).catch(function (err) { 674 | return reject(err) 675 | }) 676 | })) 677 | } 678 | 679 | /** 680 | * TODO: this will currently not traverse into symlinks *during* iteration. If the entire range 681 | * is contained within a symlink, then that will still work. 682 | */ 683 | UnionDB.prototype.lexIterator = function (opts) { 684 | // TODO: Support for non-indexed dbs. 685 | var self = this 686 | opts = opts || {} 687 | 688 | var ite = null 689 | var link = null 690 | 691 | return nanoiterator({ next, open }) 692 | 693 | function next (cb) { 694 | ite.next((err, nodes) => { 695 | if (err) return cb(err) 696 | if (!nodes) return cb(null, null) 697 | if (link) { 698 | nodes.forEach(function (n) { 699 | n.key = p.join(link.localPath, n.key.slice(link.remotePath.length - 1)) 700 | }) 701 | return cb(null, nodes) 702 | } 703 | self._getEntryValues(nodes[0].key.slice(INDEX_PATH.length), nodes, (err, values) => { 704 | if (err) return cb(err) 705 | // If this is a deletion, get the next value. 706 | if (values.length) { 707 | // Remove deletions 708 | values = values.filter(v => !!v.value) 709 | } else { 710 | values.key = values.key.slice(DATA_PATH.length) 711 | if (!values.value) values = null 712 | } 713 | if (!values) return next(cb) 714 | return cb(null, values) 715 | }) 716 | }) 717 | } 718 | 719 | function open (cb) { 720 | self._isIndexed((err, indexed) => { 721 | if (err) return cb(err) 722 | if (!indexed && self.parent) return cb(new Error('Can only iterate over an indexed database')) 723 | 724 | // Check if this iteration is contained within a symlink. 725 | let end = opts.lt || opts.lte 726 | let start = opts.gt || opts.gte 727 | let startLink = start && self._linkTrie.get(start, { enclosing: true }) 728 | let endLink = end && self._linkTrie.get(end, { enclosing: true }) 729 | if (startLink && endLink) { 730 | if (!startLink.db.key.equals(endLink.db.key)) { 731 | return cb(new Error('Cannot iterate across multiple different symlinks')) 732 | } 733 | link = startLink 734 | ite = startLink.db.lexIterator(fixPaths(startLink.remotePath, startLink.localPath, opts)) 735 | return cb(null) 736 | } else if (startLink || endLink) { 737 | return cb(new Error('Cannot currently iterate both locally and through symlinks')) 738 | } 739 | 740 | fixPaths(INDEX_PATH, '', opts) 741 | ite = self._db.lexIterator(opts) 742 | return cb(null) 743 | }) 744 | } 745 | 746 | function fixPaths (base, local, opts) { 747 | if (opts.lt) opts.lt = p.join(base, opts.lt.slice(local.length)) 748 | if (opts.gt) opts.gt = p.join(base, opts.gt.slice(local.length)) 749 | if (opts.gte) opts.gte = p.join(base, opts.gte.slice(local.length)) 750 | if (opts.lte) opts.lte = p.join(base, opts.lte.slice(local.length)) 751 | if (!opts.gt && !opts.gte) opts.gt = base 752 | 753 | // If no lt or lte is specified, ensure that the iterator doesn't leave the 'base' subtree. 754 | if (!opts.lt && !opts.lte) { 755 | let lastChar = base.charCodeAt[base.length - 1] 756 | let lt = base 757 | lt[lt.length - 1] = lastChar + 1 758 | opts.lt = lt 759 | } 760 | 761 | return opts 762 | } 763 | } 764 | 765 | /** 766 | * Note: The diff stream currently *only* operates on the top layer. 767 | */ 768 | UnionDB.prototype.createDiffStream = function (other, prefix) { 769 | var self = this 770 | 771 | if (other) { 772 | var version = messages.Version.decode(other) 773 | var localCheckout = version.localVersion 774 | var linkCheckouts = version.linkVersions 775 | } 776 | 777 | var proxy = duplexify.obj() 778 | proxy.pause() 779 | proxy.setWritable(null) 780 | 781 | this.ready(err => { 782 | if (err) proxy.destroy(err) 783 | var localDiff = localCheckout ? this._db.checkout(localCheckout) : null 784 | var localStream = pumpify.obj( 785 | this._db.createDiffStream(localDiff, p.join(DATA_PATH, prefix)), 786 | through.obj(({ left, right }, enc, cb) => { 787 | return cb(null, { 788 | left: self._prereturn(left), 789 | right: self._prereturn(right) 790 | }) 791 | }) 792 | ) 793 | 794 | var links = getLinks(prefix) 795 | if (links) { 796 | map(links, (link, next) => { 797 | return createLinkStream(link, next) 798 | }, (err, linkStreams) => { 799 | if (err) proxy.destroy(err) 800 | proxy.setReadable(merge.apply(merge, [localStream, ...linkStreams])) 801 | proxy.resume() 802 | }) 803 | } else { 804 | proxy.setReadable(localStream) 805 | proxy.resume() 806 | } 807 | }) 808 | 809 | return proxy 810 | 811 | function createLinkStream (link, cb) { 812 | var version = linkCheckouts[link.localPath] 813 | return cb(null, link.db.createDiffStream(version, linkPath(link, prefix))) 814 | } 815 | 816 | function getLinks (prefix) { 817 | return Object.keys(self._links) 818 | .filter(path => path.startsWith(prefix)) 819 | .map(path => self._links[path]) 820 | } 821 | } 822 | 823 | UnionDB.prototype.watch = function (key, onchange) { 824 | var self = this 825 | 826 | // Check to see if there are any links that are prefixed by `key`. If so, watch each 827 | // one and propagate any changes through `onchange` 828 | var watchers = [] 829 | 830 | // TODO: remove a watch if its corresponding link is deleted. 831 | // TODO: add a watch if a link is added to a watched directory (currently will have to re-watch). 832 | Object.keys(self._links).forEach(watchLink) 833 | 834 | watchers.push(this._db.watch(p.join(DATA_PATH, key), onchange)) 835 | 836 | self._changeHandlers.push(onchange) 837 | 838 | return unwatch 839 | 840 | function unwatch () { 841 | self._changeHandlers.splice(self._changeHandlers.indexOf(onchange), 1) 842 | watchers.forEach(function (watcher) { 843 | if (watcher.destroy) watcher.destroy() 844 | else watcher() 845 | }) 846 | } 847 | 848 | function watchLink (linkKey) { 849 | if (linkKey.startsWith(key) || key.startsWith(linkKey)) { 850 | var suffix = key.slice(linkKey.length) 851 | var linkRecord = self._links[linkKey] 852 | 853 | watchers.push(linkRecord.db.watch((suffix === '') ? '/' : suffix, onchange)) 854 | } 855 | } 856 | } 857 | 858 | UnionDB.prototype.replicate = function (opts) { 859 | var self = this 860 | return self._db.replicate(opts) 861 | } 862 | 863 | UnionDB.prototype.fork = function (opts, cb) { 864 | if (typeof opts === 'function') return this.fork(null, opts) 865 | opts = opts || {} 866 | var self = this 867 | 868 | if (opts.version) return process.nextTick(finish, opts.version) 869 | this.version(function (err, version) { 870 | if (err) return cb(err) 871 | finish(version) 872 | }) 873 | 874 | function finish (version) { 875 | self._createUnionDB(null, Object.assign({}, self.opts, { 876 | parent: { 877 | key: self.key, 878 | version: version 879 | } 880 | }), cb) 881 | } 882 | } 883 | 884 | UnionDB.prototype.sub = async function (path, opts, cb) { 885 | if (typeof opts === 'function') return this.sub(path, null, opts) 886 | opts = opts || {} 887 | 888 | return maybe(cb, new Promise((resolve, reject) => { 889 | this._createUnionDB(null, opts, (err, db) => { 890 | if (err) return reject(err) 891 | this.mount(db.key, path, opts, err => { 892 | if (err) return reject(err) 893 | return resolve(db) 894 | }) 895 | }) 896 | })) 897 | } 898 | 899 | /** 900 | * TODO: should snapshot just fork? 901 | */ 902 | UnionDB.prototype.snapshot = function (opts, cb) { 903 | return this.fork(opts, cb) 904 | } 905 | 906 | UnionDB.prototype.authorize = function (key) { 907 | return this._db.authorize(key) 908 | } 909 | 910 | UnionDB.prototype.version = async function (cb) { 911 | var self = this 912 | 913 | return maybe(cb, new Promise((resolve, reject) => { 914 | this._ready.then(function () { 915 | self._db.version(function (err, dbVersion) { 916 | if (err) return reject(err) 917 | 918 | var sortedLinks = Object.values(self._links).sort(function (l1, l2) { 919 | return l1.db.key < l2.db.key 920 | }) 921 | 922 | map(sortedLinks, function (link, next) { 923 | link.db.version(function (err, version) { 924 | if (err) return next(err) 925 | return next(null, { path: link.localPath, version: version }) 926 | }) 927 | }, function (err, linkAndVersions) { 928 | if (err) return reject(err) 929 | 930 | var linkVersions = {} 931 | linkAndVersions.forEach(function (lak) { 932 | linkVersions[lak.path] = lak.version 933 | }) 934 | 935 | return resolve(messages.Version.encode({ 936 | localVersion: dbVersion, 937 | linkVersions: linkVersions 938 | })) 939 | }) 940 | }) 941 | }).catch(function (err) { 942 | return reject(err) 943 | }) 944 | })) 945 | } 946 | 947 | function linkPath (link, prefix) { 948 | return p.join(link.remotePath, prefix.slice(link.localPath.length)) 949 | } 950 | -------------------------------------------------------------------------------- /lib/messages.js: -------------------------------------------------------------------------------- 1 | var p = require('path') 2 | var fs = require('fs') 3 | var protobuf = require('protocol-buffers') 4 | 5 | module.exports = protobuf(fs.readFileSync(p.join(__dirname, 'schema.proto'))) 6 | -------------------------------------------------------------------------------- /lib/schema.proto: -------------------------------------------------------------------------------- 1 | message DB { 2 | required bytes key = 1; 3 | optional bytes version = 2; 4 | } 5 | 6 | message StaticDB { 7 | required bytes key = 1; 8 | required bytes version = 2; 9 | } 10 | 11 | message Metadata { 12 | repeated StaticDB parents = 1; 13 | required bool indexed = 2; 14 | } 15 | 16 | message Link { 17 | required DB db = 1; 18 | required string localPath = 2; 19 | optional string remotePath = 3; 20 | optional string valueEncoding = 4; 21 | optional bool flat = 5; 22 | } 23 | 24 | message Entry { 25 | required bool deleted = 1; 26 | required uint32 layerIndex = 2; 27 | optional Link link = 3; 28 | } 29 | 30 | message Version { 31 | required bytes localVersion = 1; 32 | map linkVersions = 2; 33 | } 34 | -------------------------------------------------------------------------------- /lib/trie.js: -------------------------------------------------------------------------------- 1 | function normalize (path) { 2 | if (path.startsWith('/')) return path.slice(1) 3 | return path 4 | } 5 | 6 | function Trie () { 7 | this.trie = { nodes: {} } 8 | } 9 | 10 | Trie.prototype.add = function (path, data, opts) { 11 | opts = opts || {} 12 | path = normalize(path) 13 | var list = path.split('/') 14 | var trie = this.trie 15 | for (var i = 0; i < list.length; i++) { 16 | var next = trie.nodes[list[i]] || { nodes: {} } 17 | trie.nodes[list[i]] = next 18 | trie = next 19 | } 20 | 21 | trie.path = path 22 | 23 | if (opts.push) { 24 | if (!trie.value) trie.value = [] 25 | if (!(trie.value instanceof Array)) trie.value = [trie.value] 26 | trie.value.push(data) 27 | } else { 28 | trie.value = data 29 | } 30 | } 31 | 32 | Trie.prototype.get = function (path, opts) { 33 | opts = opts || {} 34 | path = normalize(path) 35 | 36 | var list = path.split('/') 37 | var trie = this.trie 38 | for (var i = 0; i < list.length; i++) { 39 | var next = trie.nodes[list[i]] 40 | if (next) trie = next 41 | } 42 | 43 | if (trie.path !== path && !opts.enclosing) return null 44 | return trie.value 45 | } 46 | 47 | module.exports = Trie 48 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "union-hyperdb", 3 | "version": "0.0.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "acorn": { 8 | "version": "5.4.1", 9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", 10 | "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", 11 | "dev": true 12 | }, 13 | "acorn-jsx": { 14 | "version": "3.0.1", 15 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 16 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 17 | "dev": true, 18 | "requires": { 19 | "acorn": "3.3.0" 20 | }, 21 | "dependencies": { 22 | "acorn": { 23 | "version": "3.3.0", 24 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 25 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", 26 | "dev": true 27 | } 28 | } 29 | }, 30 | "ajv": { 31 | "version": "4.11.8", 32 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", 33 | "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", 34 | "dev": true, 35 | "requires": { 36 | "co": "4.6.0", 37 | "json-stable-stringify": "1.0.1" 38 | } 39 | }, 40 | "ajv-keywords": { 41 | "version": "1.5.1", 42 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", 43 | "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", 44 | "dev": true 45 | }, 46 | "ansi-escapes": { 47 | "version": "1.4.0", 48 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", 49 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", 50 | "dev": true 51 | }, 52 | "ansi-regex": { 53 | "version": "2.1.1", 54 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 55 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 56 | "dev": true 57 | }, 58 | "ansi-styles": { 59 | "version": "2.2.1", 60 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 61 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 62 | "dev": true 63 | }, 64 | "argparse": { 65 | "version": "1.0.10", 66 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 67 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 68 | "dev": true, 69 | "requires": { 70 | "sprintf-js": "1.0.3" 71 | } 72 | }, 73 | "array-union": { 74 | "version": "1.0.2", 75 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 76 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 77 | "dev": true, 78 | "requires": { 79 | "array-uniq": "1.0.3" 80 | } 81 | }, 82 | "array-uniq": { 83 | "version": "1.0.3", 84 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 85 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 86 | "dev": true 87 | }, 88 | "array.prototype.find": { 89 | "version": "2.0.4", 90 | "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", 91 | "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", 92 | "dev": true, 93 | "requires": { 94 | "define-properties": "1.1.2", 95 | "es-abstract": "1.10.0" 96 | } 97 | }, 98 | "arrify": { 99 | "version": "1.0.1", 100 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 101 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 102 | "dev": true 103 | }, 104 | "async-each": { 105 | "version": "1.0.1", 106 | "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", 107 | "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" 108 | }, 109 | "babel-code-frame": { 110 | "version": "6.26.0", 111 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 112 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 113 | "dev": true, 114 | "requires": { 115 | "chalk": "1.1.3", 116 | "esutils": "2.0.2", 117 | "js-tokens": "3.0.2" 118 | } 119 | }, 120 | "balanced-match": { 121 | "version": "1.0.0", 122 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 123 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 124 | "dev": true 125 | }, 126 | "brace-expansion": { 127 | "version": "1.1.11", 128 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 129 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 130 | "dev": true, 131 | "requires": { 132 | "balanced-match": "1.0.0", 133 | "concat-map": "0.0.1" 134 | } 135 | }, 136 | "builtin-modules": { 137 | "version": "1.1.1", 138 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 139 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 140 | "dev": true 141 | }, 142 | "bulk-write-stream": { 143 | "version": "1.1.3", 144 | "resolved": "https://registry.npmjs.org/bulk-write-stream/-/bulk-write-stream-1.1.3.tgz", 145 | "integrity": "sha1-0pyjhfvVPzV67lvT0wKHMrYq4nU=", 146 | "requires": { 147 | "inherits": "2.0.3", 148 | "readable-stream": "2.3.4" 149 | } 150 | }, 151 | "call-me-maybe": { 152 | "version": "1.0.1", 153 | "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", 154 | "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" 155 | }, 156 | "caller-path": { 157 | "version": "0.1.0", 158 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 159 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 160 | "dev": true, 161 | "requires": { 162 | "callsites": "0.2.0" 163 | } 164 | }, 165 | "callsites": { 166 | "version": "0.2.0", 167 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 168 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 169 | "dev": true 170 | }, 171 | "chalk": { 172 | "version": "1.1.3", 173 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 174 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 175 | "dev": true, 176 | "requires": { 177 | "ansi-styles": "2.2.1", 178 | "escape-string-regexp": "1.0.5", 179 | "has-ansi": "2.0.0", 180 | "strip-ansi": "3.0.1", 181 | "supports-color": "2.0.0" 182 | } 183 | }, 184 | "circular-json": { 185 | "version": "0.3.3", 186 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", 187 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", 188 | "dev": true 189 | }, 190 | "cli-cursor": { 191 | "version": "1.0.2", 192 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", 193 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", 194 | "dev": true, 195 | "requires": { 196 | "restore-cursor": "1.0.1" 197 | } 198 | }, 199 | "cli-width": { 200 | "version": "2.2.0", 201 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 202 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 203 | "dev": true 204 | }, 205 | "cmp": { 206 | "version": "0.0.2", 207 | "resolved": "https://registry.npmjs.org/cmp/-/cmp-0.0.2.tgz", 208 | "integrity": "sha1-YPURAvqpsCBxm7hffpDPPYsdwrY=", 209 | "requires": { 210 | "hashkeys": "0.0.1" 211 | } 212 | }, 213 | "co": { 214 | "version": "4.6.0", 215 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 216 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 217 | "dev": true 218 | }, 219 | "code-point-at": { 220 | "version": "1.1.0", 221 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 222 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 223 | "dev": true 224 | }, 225 | "codecs": { 226 | "version": "1.2.0", 227 | "resolved": "https://registry.npmjs.org/codecs/-/codecs-1.2.0.tgz", 228 | "integrity": "sha1-UUhUnj0VbF+gU9fLtBlxWgz0PRY=" 229 | }, 230 | "concat-map": { 231 | "version": "0.0.1", 232 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 233 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 234 | "dev": true 235 | }, 236 | "concat-stream": { 237 | "version": "1.6.0", 238 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", 239 | "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", 240 | "dev": true, 241 | "requires": { 242 | "inherits": "2.0.3", 243 | "readable-stream": "2.3.4", 244 | "typedarray": "0.0.6" 245 | } 246 | }, 247 | "contains-path": { 248 | "version": "0.1.0", 249 | "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", 250 | "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", 251 | "dev": true 252 | }, 253 | "core-util-is": { 254 | "version": "1.0.2", 255 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 256 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 257 | }, 258 | "d": { 259 | "version": "1.0.0", 260 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", 261 | "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", 262 | "dev": true, 263 | "requires": { 264 | "es5-ext": "0.10.39" 265 | } 266 | }, 267 | "debug": { 268 | "version": "2.6.9", 269 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 270 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 271 | "dev": true, 272 | "requires": { 273 | "ms": "2.0.0" 274 | } 275 | }, 276 | "debug-log": { 277 | "version": "1.0.1", 278 | "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", 279 | "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", 280 | "dev": true 281 | }, 282 | "deep-equal": { 283 | "version": "1.0.1", 284 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 285 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", 286 | "dev": true 287 | }, 288 | "deep-is": { 289 | "version": "0.1.3", 290 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 291 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 292 | "dev": true 293 | }, 294 | "define-properties": { 295 | "version": "1.1.2", 296 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", 297 | "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", 298 | "dev": true, 299 | "requires": { 300 | "foreach": "2.0.5", 301 | "object-keys": "1.0.11" 302 | } 303 | }, 304 | "defined": { 305 | "version": "1.0.0", 306 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 307 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", 308 | "dev": true 309 | }, 310 | "deglob": { 311 | "version": "2.1.0", 312 | "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz", 313 | "integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=", 314 | "dev": true, 315 | "requires": { 316 | "find-root": "1.1.0", 317 | "glob": "7.1.2", 318 | "ignore": "3.3.7", 319 | "pkg-config": "1.1.1", 320 | "run-parallel": "1.1.7", 321 | "uniq": "1.0.1" 322 | } 323 | }, 324 | "del": { 325 | "version": "2.2.2", 326 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 327 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 328 | "dev": true, 329 | "requires": { 330 | "globby": "5.0.0", 331 | "is-path-cwd": "1.0.0", 332 | "is-path-in-cwd": "1.0.0", 333 | "object-assign": "4.1.1", 334 | "pify": "2.3.0", 335 | "pinkie-promise": "2.0.1", 336 | "rimraf": "2.6.2" 337 | } 338 | }, 339 | "doctrine": { 340 | "version": "2.1.0", 341 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 342 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 343 | "dev": true, 344 | "requires": { 345 | "esutils": "2.0.2" 346 | } 347 | }, 348 | "duplexify": { 349 | "version": "3.6.0", 350 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", 351 | "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", 352 | "requires": { 353 | "end-of-stream": "1.4.1", 354 | "inherits": "2.0.3", 355 | "readable-stream": "2.3.4", 356 | "stream-shift": "1.0.0" 357 | } 358 | }, 359 | "end-of-stream": { 360 | "version": "1.4.1", 361 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 362 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 363 | "requires": { 364 | "once": "1.4.0" 365 | } 366 | }, 367 | "error-ex": { 368 | "version": "1.3.1", 369 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", 370 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", 371 | "dev": true, 372 | "requires": { 373 | "is-arrayish": "0.2.1" 374 | } 375 | }, 376 | "es-abstract": { 377 | "version": "1.10.0", 378 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", 379 | "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", 380 | "dev": true, 381 | "requires": { 382 | "es-to-primitive": "1.1.1", 383 | "function-bind": "1.1.1", 384 | "has": "1.0.1", 385 | "is-callable": "1.1.3", 386 | "is-regex": "1.0.4" 387 | } 388 | }, 389 | "es-to-primitive": { 390 | "version": "1.1.1", 391 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", 392 | "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", 393 | "dev": true, 394 | "requires": { 395 | "is-callable": "1.1.3", 396 | "is-date-object": "1.0.1", 397 | "is-symbol": "1.0.1" 398 | } 399 | }, 400 | "es5-ext": { 401 | "version": "0.10.39", 402 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.39.tgz", 403 | "integrity": "sha512-AlaXZhPHl0po/uxMx1tyrlt1O86M6D5iVaDH8UgLfgek4kXTX6vzsRfJQWC2Ku+aG8pkw1XWzh9eTkwfVrsD5g==", 404 | "dev": true, 405 | "requires": { 406 | "es6-iterator": "2.0.3", 407 | "es6-symbol": "3.1.1" 408 | } 409 | }, 410 | "es6-iterator": { 411 | "version": "2.0.3", 412 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", 413 | "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", 414 | "dev": true, 415 | "requires": { 416 | "d": "1.0.0", 417 | "es5-ext": "0.10.39", 418 | "es6-symbol": "3.1.1" 419 | } 420 | }, 421 | "es6-map": { 422 | "version": "0.1.5", 423 | "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", 424 | "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", 425 | "dev": true, 426 | "requires": { 427 | "d": "1.0.0", 428 | "es5-ext": "0.10.39", 429 | "es6-iterator": "2.0.3", 430 | "es6-set": "0.1.5", 431 | "es6-symbol": "3.1.1", 432 | "event-emitter": "0.3.5" 433 | } 434 | }, 435 | "es6-set": { 436 | "version": "0.1.5", 437 | "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", 438 | "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", 439 | "dev": true, 440 | "requires": { 441 | "d": "1.0.0", 442 | "es5-ext": "0.10.39", 443 | "es6-iterator": "2.0.3", 444 | "es6-symbol": "3.1.1", 445 | "event-emitter": "0.3.5" 446 | } 447 | }, 448 | "es6-symbol": { 449 | "version": "3.1.1", 450 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", 451 | "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", 452 | "dev": true, 453 | "requires": { 454 | "d": "1.0.0", 455 | "es5-ext": "0.10.39" 456 | } 457 | }, 458 | "es6-weak-map": { 459 | "version": "2.0.2", 460 | "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", 461 | "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", 462 | "dev": true, 463 | "requires": { 464 | "d": "1.0.0", 465 | "es5-ext": "0.10.39", 466 | "es6-iterator": "2.0.3", 467 | "es6-symbol": "3.1.1" 468 | } 469 | }, 470 | "escape-string-regexp": { 471 | "version": "1.0.5", 472 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 473 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 474 | "dev": true 475 | }, 476 | "escope": { 477 | "version": "3.6.0", 478 | "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", 479 | "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", 480 | "dev": true, 481 | "requires": { 482 | "es6-map": "0.1.5", 483 | "es6-weak-map": "2.0.2", 484 | "esrecurse": "4.2.0", 485 | "estraverse": "4.2.0" 486 | } 487 | }, 488 | "eslint": { 489 | "version": "3.19.0", 490 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", 491 | "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", 492 | "dev": true, 493 | "requires": { 494 | "babel-code-frame": "6.26.0", 495 | "chalk": "1.1.3", 496 | "concat-stream": "1.6.0", 497 | "debug": "2.6.9", 498 | "doctrine": "2.1.0", 499 | "escope": "3.6.0", 500 | "espree": "3.5.3", 501 | "esquery": "1.0.0", 502 | "estraverse": "4.2.0", 503 | "esutils": "2.0.2", 504 | "file-entry-cache": "2.0.0", 505 | "glob": "7.1.2", 506 | "globals": "9.18.0", 507 | "ignore": "3.3.7", 508 | "imurmurhash": "0.1.4", 509 | "inquirer": "0.12.0", 510 | "is-my-json-valid": "2.17.2", 511 | "is-resolvable": "1.1.0", 512 | "js-yaml": "3.10.0", 513 | "json-stable-stringify": "1.0.1", 514 | "levn": "0.3.0", 515 | "lodash": "4.17.5", 516 | "mkdirp": "0.5.1", 517 | "natural-compare": "1.4.0", 518 | "optionator": "0.8.2", 519 | "path-is-inside": "1.0.2", 520 | "pluralize": "1.2.1", 521 | "progress": "1.1.8", 522 | "require-uncached": "1.0.3", 523 | "shelljs": "0.7.8", 524 | "strip-bom": "3.0.0", 525 | "strip-json-comments": "2.0.1", 526 | "table": "3.8.3", 527 | "text-table": "0.2.0", 528 | "user-home": "2.0.0" 529 | } 530 | }, 531 | "eslint-config-standard": { 532 | "version": "10.2.1", 533 | "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", 534 | "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=", 535 | "dev": true 536 | }, 537 | "eslint-config-standard-jsx": { 538 | "version": "4.0.2", 539 | "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-4.0.2.tgz", 540 | "integrity": "sha512-F8fRh2WFnTek7dZH9ZaE0PCBwdVGkwVWZmizla/DDNOmg7Tx6B/IlK5+oYpiX29jpu73LszeJj5i1axEZv6VMw==", 541 | "dev": true 542 | }, 543 | "eslint-import-resolver-node": { 544 | "version": "0.2.3", 545 | "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", 546 | "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", 547 | "dev": true, 548 | "requires": { 549 | "debug": "2.6.9", 550 | "object-assign": "4.1.1", 551 | "resolve": "1.5.0" 552 | } 553 | }, 554 | "eslint-module-utils": { 555 | "version": "2.1.1", 556 | "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", 557 | "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", 558 | "dev": true, 559 | "requires": { 560 | "debug": "2.6.9", 561 | "pkg-dir": "1.0.0" 562 | } 563 | }, 564 | "eslint-plugin-import": { 565 | "version": "2.2.0", 566 | "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", 567 | "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", 568 | "dev": true, 569 | "requires": { 570 | "builtin-modules": "1.1.1", 571 | "contains-path": "0.1.0", 572 | "debug": "2.6.9", 573 | "doctrine": "1.5.0", 574 | "eslint-import-resolver-node": "0.2.3", 575 | "eslint-module-utils": "2.1.1", 576 | "has": "1.0.1", 577 | "lodash.cond": "4.5.2", 578 | "minimatch": "3.0.4", 579 | "pkg-up": "1.0.0" 580 | }, 581 | "dependencies": { 582 | "doctrine": { 583 | "version": "1.5.0", 584 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", 585 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", 586 | "dev": true, 587 | "requires": { 588 | "esutils": "2.0.2", 589 | "isarray": "1.0.0" 590 | } 591 | } 592 | } 593 | }, 594 | "eslint-plugin-node": { 595 | "version": "4.2.3", 596 | "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-4.2.3.tgz", 597 | "integrity": "sha512-vIUQPuwbVYdz/CYnlTLsJrRy7iXHQjdEe5wz0XhhdTym3IInM/zZLlPf9nZ2mThsH0QcsieCOWs2vOeCy/22LQ==", 598 | "dev": true, 599 | "requires": { 600 | "ignore": "3.3.7", 601 | "minimatch": "3.0.4", 602 | "object-assign": "4.1.1", 603 | "resolve": "1.5.0", 604 | "semver": "5.3.0" 605 | } 606 | }, 607 | "eslint-plugin-promise": { 608 | "version": "3.5.0", 609 | "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz", 610 | "integrity": "sha1-ePu2/+BHIBYnVp6FpsU3OvKmj8o=", 611 | "dev": true 612 | }, 613 | "eslint-plugin-react": { 614 | "version": "6.10.3", 615 | "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz", 616 | "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=", 617 | "dev": true, 618 | "requires": { 619 | "array.prototype.find": "2.0.4", 620 | "doctrine": "1.5.0", 621 | "has": "1.0.1", 622 | "jsx-ast-utils": "1.4.1", 623 | "object.assign": "4.1.0" 624 | }, 625 | "dependencies": { 626 | "doctrine": { 627 | "version": "1.5.0", 628 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", 629 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", 630 | "dev": true, 631 | "requires": { 632 | "esutils": "2.0.2", 633 | "isarray": "1.0.0" 634 | } 635 | } 636 | } 637 | }, 638 | "eslint-plugin-standard": { 639 | "version": "3.0.1", 640 | "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz", 641 | "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=", 642 | "dev": true 643 | }, 644 | "espree": { 645 | "version": "3.5.3", 646 | "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz", 647 | "integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==", 648 | "dev": true, 649 | "requires": { 650 | "acorn": "5.4.1", 651 | "acorn-jsx": "3.0.1" 652 | } 653 | }, 654 | "esprima": { 655 | "version": "4.0.0", 656 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", 657 | "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", 658 | "dev": true 659 | }, 660 | "esquery": { 661 | "version": "1.0.0", 662 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", 663 | "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", 664 | "dev": true, 665 | "requires": { 666 | "estraverse": "4.2.0" 667 | } 668 | }, 669 | "esrecurse": { 670 | "version": "4.2.0", 671 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", 672 | "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", 673 | "dev": true, 674 | "requires": { 675 | "estraverse": "4.2.0", 676 | "object-assign": "4.1.1" 677 | } 678 | }, 679 | "estraverse": { 680 | "version": "4.2.0", 681 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 682 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 683 | "dev": true 684 | }, 685 | "esutils": { 686 | "version": "2.0.2", 687 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 688 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 689 | "dev": true 690 | }, 691 | "event-emitter": { 692 | "version": "0.3.5", 693 | "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", 694 | "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", 695 | "dev": true, 696 | "requires": { 697 | "d": "1.0.0", 698 | "es5-ext": "0.10.39" 699 | } 700 | }, 701 | "exit-hook": { 702 | "version": "1.1.1", 703 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", 704 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", 705 | "dev": true 706 | }, 707 | "fast-levenshtein": { 708 | "version": "2.0.6", 709 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 710 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 711 | "dev": true 712 | }, 713 | "figures": { 714 | "version": "1.7.0", 715 | "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", 716 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", 717 | "dev": true, 718 | "requires": { 719 | "escape-string-regexp": "1.0.5", 720 | "object-assign": "4.1.1" 721 | } 722 | }, 723 | "file-entry-cache": { 724 | "version": "2.0.0", 725 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 726 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 727 | "dev": true, 728 | "requires": { 729 | "flat-cache": "1.3.0", 730 | "object-assign": "4.1.1" 731 | } 732 | }, 733 | "find-root": { 734 | "version": "1.1.0", 735 | "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", 736 | "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", 737 | "dev": true 738 | }, 739 | "find-up": { 740 | "version": "1.1.2", 741 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 742 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 743 | "dev": true, 744 | "requires": { 745 | "path-exists": "2.1.0", 746 | "pinkie-promise": "2.0.1" 747 | } 748 | }, 749 | "flat-cache": { 750 | "version": "1.3.0", 751 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", 752 | "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", 753 | "dev": true, 754 | "requires": { 755 | "circular-json": "0.3.3", 756 | "del": "2.2.2", 757 | "graceful-fs": "4.1.11", 758 | "write": "0.2.1" 759 | } 760 | }, 761 | "for-each": { 762 | "version": "0.3.2", 763 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", 764 | "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", 765 | "dev": true, 766 | "requires": { 767 | "is-function": "1.0.1" 768 | } 769 | }, 770 | "foreach": { 771 | "version": "2.0.5", 772 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 773 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", 774 | "dev": true 775 | }, 776 | "fs.realpath": { 777 | "version": "1.0.0", 778 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 779 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 780 | "dev": true 781 | }, 782 | "function-bind": { 783 | "version": "1.1.1", 784 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 785 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 786 | "dev": true 787 | }, 788 | "generate-function": { 789 | "version": "2.0.0", 790 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", 791 | "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" 792 | }, 793 | "generate-object-property": { 794 | "version": "1.2.0", 795 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", 796 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", 797 | "requires": { 798 | "is-property": "1.0.2" 799 | } 800 | }, 801 | "get-stdin": { 802 | "version": "5.0.1", 803 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", 804 | "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", 805 | "dev": true 806 | }, 807 | "glob": { 808 | "version": "7.1.2", 809 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 810 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 811 | "dev": true, 812 | "requires": { 813 | "fs.realpath": "1.0.0", 814 | "inflight": "1.0.6", 815 | "inherits": "2.0.3", 816 | "minimatch": "3.0.4", 817 | "once": "1.4.0", 818 | "path-is-absolute": "1.0.1" 819 | } 820 | }, 821 | "globals": { 822 | "version": "9.18.0", 823 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", 824 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", 825 | "dev": true 826 | }, 827 | "globby": { 828 | "version": "5.0.0", 829 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 830 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 831 | "dev": true, 832 | "requires": { 833 | "array-union": "1.0.2", 834 | "arrify": "1.0.1", 835 | "glob": "7.1.2", 836 | "object-assign": "4.1.1", 837 | "pify": "2.3.0", 838 | "pinkie-promise": "2.0.1" 839 | } 840 | }, 841 | "graceful-fs": { 842 | "version": "4.1.11", 843 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 844 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 845 | "dev": true 846 | }, 847 | "has": { 848 | "version": "1.0.1", 849 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", 850 | "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", 851 | "dev": true, 852 | "requires": { 853 | "function-bind": "1.1.1" 854 | } 855 | }, 856 | "has-ansi": { 857 | "version": "2.0.0", 858 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 859 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 860 | "dev": true, 861 | "requires": { 862 | "ansi-regex": "2.1.1" 863 | } 864 | }, 865 | "has-symbols": { 866 | "version": "1.0.0", 867 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 868 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", 869 | "dev": true 870 | }, 871 | "hashkeys": { 872 | "version": "0.0.1", 873 | "resolved": "https://registry.npmjs.org/hashkeys/-/hashkeys-0.0.1.tgz", 874 | "integrity": "sha1-4faXFxWOnRwscE3IFHP6314on/s=" 875 | }, 876 | "ignore": { 877 | "version": "3.3.7", 878 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", 879 | "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", 880 | "dev": true 881 | }, 882 | "imurmurhash": { 883 | "version": "0.1.4", 884 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 885 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 886 | "dev": true 887 | }, 888 | "inflight": { 889 | "version": "1.0.6", 890 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 891 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 892 | "dev": true, 893 | "requires": { 894 | "once": "1.4.0", 895 | "wrappy": "1.0.2" 896 | } 897 | }, 898 | "inherits": { 899 | "version": "2.0.3", 900 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 901 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 902 | }, 903 | "inquirer": { 904 | "version": "0.12.0", 905 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", 906 | "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", 907 | "dev": true, 908 | "requires": { 909 | "ansi-escapes": "1.4.0", 910 | "ansi-regex": "2.1.1", 911 | "chalk": "1.1.3", 912 | "cli-cursor": "1.0.2", 913 | "cli-width": "2.2.0", 914 | "figures": "1.7.0", 915 | "lodash": "4.17.5", 916 | "readline2": "1.0.1", 917 | "run-async": "0.1.0", 918 | "rx-lite": "3.1.2", 919 | "string-width": "1.0.2", 920 | "strip-ansi": "3.0.1", 921 | "through": "2.3.8" 922 | } 923 | }, 924 | "interpret": { 925 | "version": "1.1.0", 926 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", 927 | "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", 928 | "dev": true 929 | }, 930 | "is-arrayish": { 931 | "version": "0.2.1", 932 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 933 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 934 | "dev": true 935 | }, 936 | "is-callable": { 937 | "version": "1.1.3", 938 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", 939 | "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", 940 | "dev": true 941 | }, 942 | "is-date-object": { 943 | "version": "1.0.1", 944 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 945 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", 946 | "dev": true 947 | }, 948 | "is-fullwidth-code-point": { 949 | "version": "1.0.0", 950 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 951 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 952 | "dev": true, 953 | "requires": { 954 | "number-is-nan": "1.0.1" 955 | } 956 | }, 957 | "is-function": { 958 | "version": "1.0.1", 959 | "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", 960 | "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", 961 | "dev": true 962 | }, 963 | "is-my-ip-valid": { 964 | "version": "1.0.0", 965 | "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", 966 | "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", 967 | "dev": true 968 | }, 969 | "is-my-json-valid": { 970 | "version": "2.17.2", 971 | "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", 972 | "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", 973 | "dev": true, 974 | "requires": { 975 | "generate-function": "2.0.0", 976 | "generate-object-property": "1.2.0", 977 | "is-my-ip-valid": "1.0.0", 978 | "jsonpointer": "4.0.1", 979 | "xtend": "4.0.1" 980 | } 981 | }, 982 | "is-path-cwd": { 983 | "version": "1.0.0", 984 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 985 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", 986 | "dev": true 987 | }, 988 | "is-path-in-cwd": { 989 | "version": "1.0.0", 990 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", 991 | "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", 992 | "dev": true, 993 | "requires": { 994 | "is-path-inside": "1.0.1" 995 | } 996 | }, 997 | "is-path-inside": { 998 | "version": "1.0.1", 999 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 1000 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 1001 | "dev": true, 1002 | "requires": { 1003 | "path-is-inside": "1.0.2" 1004 | } 1005 | }, 1006 | "is-property": { 1007 | "version": "1.0.2", 1008 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 1009 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" 1010 | }, 1011 | "is-regex": { 1012 | "version": "1.0.4", 1013 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 1014 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 1015 | "dev": true, 1016 | "requires": { 1017 | "has": "1.0.1" 1018 | } 1019 | }, 1020 | "is-resolvable": { 1021 | "version": "1.1.0", 1022 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", 1023 | "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", 1024 | "dev": true 1025 | }, 1026 | "is-symbol": { 1027 | "version": "1.0.1", 1028 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", 1029 | "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", 1030 | "dev": true 1031 | }, 1032 | "isarray": { 1033 | "version": "1.0.0", 1034 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1035 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1036 | }, 1037 | "js-tokens": { 1038 | "version": "3.0.2", 1039 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 1040 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 1041 | "dev": true 1042 | }, 1043 | "js-yaml": { 1044 | "version": "3.10.0", 1045 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", 1046 | "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", 1047 | "dev": true, 1048 | "requires": { 1049 | "argparse": "1.0.10", 1050 | "esprima": "4.0.0" 1051 | } 1052 | }, 1053 | "json-parse-better-errors": { 1054 | "version": "1.0.1", 1055 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz", 1056 | "integrity": "sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw==", 1057 | "dev": true 1058 | }, 1059 | "json-stable-stringify": { 1060 | "version": "1.0.1", 1061 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 1062 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 1063 | "dev": true, 1064 | "requires": { 1065 | "jsonify": "0.0.0" 1066 | } 1067 | }, 1068 | "jsonify": { 1069 | "version": "0.0.0", 1070 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 1071 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", 1072 | "dev": true 1073 | }, 1074 | "jsonpointer": { 1075 | "version": "4.0.1", 1076 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", 1077 | "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", 1078 | "dev": true 1079 | }, 1080 | "jsx-ast-utils": { 1081 | "version": "1.4.1", 1082 | "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", 1083 | "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", 1084 | "dev": true 1085 | }, 1086 | "levn": { 1087 | "version": "0.3.0", 1088 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1089 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 1090 | "dev": true, 1091 | "requires": { 1092 | "prelude-ls": "1.1.2", 1093 | "type-check": "0.3.2" 1094 | } 1095 | }, 1096 | "load-json-file": { 1097 | "version": "4.0.0", 1098 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 1099 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 1100 | "dev": true, 1101 | "requires": { 1102 | "graceful-fs": "4.1.11", 1103 | "parse-json": "4.0.0", 1104 | "pify": "3.0.0", 1105 | "strip-bom": "3.0.0" 1106 | }, 1107 | "dependencies": { 1108 | "pify": { 1109 | "version": "3.0.0", 1110 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1111 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 1112 | "dev": true 1113 | } 1114 | } 1115 | }, 1116 | "locate-path": { 1117 | "version": "2.0.0", 1118 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 1119 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 1120 | "dev": true, 1121 | "requires": { 1122 | "p-locate": "2.0.0", 1123 | "path-exists": "3.0.0" 1124 | }, 1125 | "dependencies": { 1126 | "path-exists": { 1127 | "version": "3.0.0", 1128 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1129 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 1130 | "dev": true 1131 | } 1132 | } 1133 | }, 1134 | "lodash": { 1135 | "version": "4.17.5", 1136 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", 1137 | "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", 1138 | "dev": true 1139 | }, 1140 | "lodash.cond": { 1141 | "version": "4.5.2", 1142 | "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", 1143 | "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", 1144 | "dev": true 1145 | }, 1146 | "merge-stream": { 1147 | "version": "1.0.1", 1148 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", 1149 | "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", 1150 | "requires": { 1151 | "readable-stream": "2.3.4" 1152 | } 1153 | }, 1154 | "minimatch": { 1155 | "version": "3.0.4", 1156 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1157 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1158 | "dev": true, 1159 | "requires": { 1160 | "brace-expansion": "1.1.11" 1161 | } 1162 | }, 1163 | "minimist": { 1164 | "version": "0.0.8", 1165 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1166 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 1167 | "dev": true 1168 | }, 1169 | "mkdirp": { 1170 | "version": "0.5.1", 1171 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1172 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1173 | "dev": true, 1174 | "requires": { 1175 | "minimist": "0.0.8" 1176 | } 1177 | }, 1178 | "ms": { 1179 | "version": "2.0.0", 1180 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1181 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1182 | "dev": true 1183 | }, 1184 | "mute-stream": { 1185 | "version": "0.0.5", 1186 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", 1187 | "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", 1188 | "dev": true 1189 | }, 1190 | "nanoiterator": { 1191 | "version": "1.2.0", 1192 | "resolved": "https://registry.npmjs.org/nanoiterator/-/nanoiterator-1.2.0.tgz", 1193 | "integrity": "sha512-Ybp8yZQDvjgqjrER+jlvZ2m/qzhK7gakNTKFDs84OLPrzsHcSbsFczJlNkDmyBlYldxFEO3JT3gwBWCN9nj8aQ==", 1194 | "requires": { 1195 | "inherits": "2.0.3", 1196 | "readable-stream": "2.3.4" 1197 | } 1198 | }, 1199 | "natural-compare": { 1200 | "version": "1.4.0", 1201 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1202 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1203 | "dev": true 1204 | }, 1205 | "number-is-nan": { 1206 | "version": "1.0.1", 1207 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1208 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 1209 | "dev": true 1210 | }, 1211 | "object-assign": { 1212 | "version": "4.1.1", 1213 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1214 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1215 | "dev": true 1216 | }, 1217 | "object-inspect": { 1218 | "version": "1.5.0", 1219 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.5.0.tgz", 1220 | "integrity": "sha512-UmOFbHbwvv+XHj7BerrhVq+knjceBdkvU5AriwLMvhv2qi+e7DJzxfBeFpILEjVzCp+xA+W/pIf06RGPWlZNfw==", 1221 | "dev": true 1222 | }, 1223 | "object-keys": { 1224 | "version": "1.0.11", 1225 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", 1226 | "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", 1227 | "dev": true 1228 | }, 1229 | "object.assign": { 1230 | "version": "4.1.0", 1231 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 1232 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 1233 | "dev": true, 1234 | "requires": { 1235 | "define-properties": "1.1.2", 1236 | "function-bind": "1.1.1", 1237 | "has-symbols": "1.0.0", 1238 | "object-keys": "1.0.11" 1239 | } 1240 | }, 1241 | "once": { 1242 | "version": "1.4.0", 1243 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1244 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1245 | "requires": { 1246 | "wrappy": "1.0.2" 1247 | } 1248 | }, 1249 | "onetime": { 1250 | "version": "1.1.0", 1251 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", 1252 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", 1253 | "dev": true 1254 | }, 1255 | "optionator": { 1256 | "version": "0.8.2", 1257 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1258 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1259 | "dev": true, 1260 | "requires": { 1261 | "deep-is": "0.1.3", 1262 | "fast-levenshtein": "2.0.6", 1263 | "levn": "0.3.0", 1264 | "prelude-ls": "1.1.2", 1265 | "type-check": "0.3.2", 1266 | "wordwrap": "1.0.0" 1267 | } 1268 | }, 1269 | "os-homedir": { 1270 | "version": "1.0.2", 1271 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1272 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 1273 | "dev": true 1274 | }, 1275 | "p-limit": { 1276 | "version": "1.2.0", 1277 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", 1278 | "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", 1279 | "dev": true, 1280 | "requires": { 1281 | "p-try": "1.0.0" 1282 | } 1283 | }, 1284 | "p-locate": { 1285 | "version": "2.0.0", 1286 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 1287 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 1288 | "dev": true, 1289 | "requires": { 1290 | "p-limit": "1.2.0" 1291 | } 1292 | }, 1293 | "p-try": { 1294 | "version": "1.0.0", 1295 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 1296 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", 1297 | "dev": true 1298 | }, 1299 | "parse-json": { 1300 | "version": "4.0.0", 1301 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 1302 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 1303 | "dev": true, 1304 | "requires": { 1305 | "error-ex": "1.3.1", 1306 | "json-parse-better-errors": "1.0.1" 1307 | } 1308 | }, 1309 | "path-exists": { 1310 | "version": "2.1.0", 1311 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 1312 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 1313 | "dev": true, 1314 | "requires": { 1315 | "pinkie-promise": "2.0.1" 1316 | } 1317 | }, 1318 | "path-is-absolute": { 1319 | "version": "1.0.1", 1320 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1321 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1322 | "dev": true 1323 | }, 1324 | "path-is-inside": { 1325 | "version": "1.0.2", 1326 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 1327 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 1328 | "dev": true 1329 | }, 1330 | "path-parse": { 1331 | "version": "1.0.5", 1332 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 1333 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", 1334 | "dev": true 1335 | }, 1336 | "pify": { 1337 | "version": "2.3.0", 1338 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1339 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 1340 | "dev": true 1341 | }, 1342 | "pinkie": { 1343 | "version": "2.0.4", 1344 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1345 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 1346 | "dev": true 1347 | }, 1348 | "pinkie-promise": { 1349 | "version": "2.0.1", 1350 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1351 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1352 | "dev": true, 1353 | "requires": { 1354 | "pinkie": "2.0.4" 1355 | } 1356 | }, 1357 | "pkg-conf": { 1358 | "version": "2.1.0", 1359 | "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", 1360 | "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", 1361 | "dev": true, 1362 | "requires": { 1363 | "find-up": "2.1.0", 1364 | "load-json-file": "4.0.0" 1365 | }, 1366 | "dependencies": { 1367 | "find-up": { 1368 | "version": "2.1.0", 1369 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 1370 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 1371 | "dev": true, 1372 | "requires": { 1373 | "locate-path": "2.0.0" 1374 | } 1375 | } 1376 | } 1377 | }, 1378 | "pkg-config": { 1379 | "version": "1.1.1", 1380 | "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", 1381 | "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", 1382 | "dev": true, 1383 | "requires": { 1384 | "debug-log": "1.0.1", 1385 | "find-root": "1.1.0", 1386 | "xtend": "4.0.1" 1387 | } 1388 | }, 1389 | "pkg-dir": { 1390 | "version": "1.0.0", 1391 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", 1392 | "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", 1393 | "dev": true, 1394 | "requires": { 1395 | "find-up": "1.1.2" 1396 | } 1397 | }, 1398 | "pkg-up": { 1399 | "version": "1.0.0", 1400 | "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", 1401 | "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", 1402 | "dev": true, 1403 | "requires": { 1404 | "find-up": "1.1.2" 1405 | } 1406 | }, 1407 | "pluralize": { 1408 | "version": "1.2.1", 1409 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", 1410 | "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", 1411 | "dev": true 1412 | }, 1413 | "prelude-ls": { 1414 | "version": "1.1.2", 1415 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1416 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1417 | "dev": true 1418 | }, 1419 | "process-nextick-args": { 1420 | "version": "2.0.0", 1421 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1422 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1423 | }, 1424 | "progress": { 1425 | "version": "1.1.8", 1426 | "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", 1427 | "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", 1428 | "dev": true 1429 | }, 1430 | "protocol-buffers": { 1431 | "version": "4.0.4", 1432 | "resolved": "https://registry.npmjs.org/protocol-buffers/-/protocol-buffers-4.0.4.tgz", 1433 | "integrity": "sha512-NkNk4jdcTR/0+bTMfacy89aveinsTeerW2uaNhsekg4FqlXOYRqcY5s1x7sX4N16f/88JiI6tPVQko0xJMpZew==", 1434 | "requires": { 1435 | "generate-function": "2.0.0", 1436 | "generate-object-property": "1.2.0", 1437 | "protocol-buffers-encodings": "1.1.0", 1438 | "protocol-buffers-schema": "3.3.2", 1439 | "signed-varint": "2.0.1", 1440 | "varint": "5.0.0" 1441 | } 1442 | }, 1443 | "protocol-buffers-encodings": { 1444 | "version": "1.1.0", 1445 | "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.1.0.tgz", 1446 | "integrity": "sha512-SmjEuAf3hc3h3rWZ6V1YaaQw2MNJWK848gLJgzx/sefOJdNLujKinJVXIS0q2cBQpQn2Q32TinawZyDZPzm4kQ==", 1447 | "requires": { 1448 | "signed-varint": "2.0.1", 1449 | "varint": "5.0.0" 1450 | } 1451 | }, 1452 | "protocol-buffers-schema": { 1453 | "version": "3.3.2", 1454 | "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz", 1455 | "integrity": "sha512-Xdayp8sB/mU+sUV4G7ws8xtYMGdQnxbeIfLjyO9TZZRJdztBGhlmbI5x1qcY4TG5hBkIKGnc28i7nXxaugu88w==" 1456 | }, 1457 | "pump": { 1458 | "version": "2.0.1", 1459 | "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", 1460 | "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", 1461 | "requires": { 1462 | "end-of-stream": "1.4.1", 1463 | "once": "1.4.0" 1464 | } 1465 | }, 1466 | "pumpify": { 1467 | "version": "1.5.1", 1468 | "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", 1469 | "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", 1470 | "requires": { 1471 | "duplexify": "3.6.0", 1472 | "inherits": "2.0.3", 1473 | "pump": "2.0.1" 1474 | } 1475 | }, 1476 | "random-access-memory": { 1477 | "version": "2.4.0", 1478 | "resolved": "https://registry.npmjs.org/random-access-memory/-/random-access-memory-2.4.0.tgz", 1479 | "integrity": "sha1-cvPYZbS1WiWYeUc+L7LeNWnGnuI=", 1480 | "dev": true, 1481 | "requires": { 1482 | "process-nextick-args": "1.0.7" 1483 | }, 1484 | "dependencies": { 1485 | "process-nextick-args": { 1486 | "version": "1.0.7", 1487 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 1488 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", 1489 | "dev": true 1490 | } 1491 | } 1492 | }, 1493 | "readable-stream": { 1494 | "version": "2.3.4", 1495 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", 1496 | "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", 1497 | "requires": { 1498 | "core-util-is": "1.0.2", 1499 | "inherits": "2.0.3", 1500 | "isarray": "1.0.0", 1501 | "process-nextick-args": "2.0.0", 1502 | "safe-buffer": "5.1.1", 1503 | "string_decoder": "1.0.3", 1504 | "util-deprecate": "1.0.2" 1505 | } 1506 | }, 1507 | "readline2": { 1508 | "version": "1.0.1", 1509 | "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", 1510 | "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", 1511 | "dev": true, 1512 | "requires": { 1513 | "code-point-at": "1.1.0", 1514 | "is-fullwidth-code-point": "1.0.0", 1515 | "mute-stream": "0.0.5" 1516 | } 1517 | }, 1518 | "rechoir": { 1519 | "version": "0.6.2", 1520 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 1521 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 1522 | "dev": true, 1523 | "requires": { 1524 | "resolve": "1.5.0" 1525 | } 1526 | }, 1527 | "require-uncached": { 1528 | "version": "1.0.3", 1529 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 1530 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 1531 | "dev": true, 1532 | "requires": { 1533 | "caller-path": "0.1.0", 1534 | "resolve-from": "1.0.1" 1535 | } 1536 | }, 1537 | "resolve": { 1538 | "version": "1.5.0", 1539 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", 1540 | "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", 1541 | "dev": true, 1542 | "requires": { 1543 | "path-parse": "1.0.5" 1544 | } 1545 | }, 1546 | "resolve-from": { 1547 | "version": "1.0.1", 1548 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 1549 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 1550 | "dev": true 1551 | }, 1552 | "restore-cursor": { 1553 | "version": "1.0.1", 1554 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", 1555 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", 1556 | "dev": true, 1557 | "requires": { 1558 | "exit-hook": "1.1.1", 1559 | "onetime": "1.1.0" 1560 | } 1561 | }, 1562 | "resumer": { 1563 | "version": "0.0.0", 1564 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 1565 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 1566 | "dev": true, 1567 | "requires": { 1568 | "through": "2.3.8" 1569 | } 1570 | }, 1571 | "rimraf": { 1572 | "version": "2.6.2", 1573 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1574 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1575 | "dev": true, 1576 | "requires": { 1577 | "glob": "7.1.2" 1578 | } 1579 | }, 1580 | "run-async": { 1581 | "version": "0.1.0", 1582 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", 1583 | "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", 1584 | "dev": true, 1585 | "requires": { 1586 | "once": "1.4.0" 1587 | } 1588 | }, 1589 | "run-parallel": { 1590 | "version": "1.1.7", 1591 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.7.tgz", 1592 | "integrity": "sha512-nB641a6enJOh0fdsFHR9SiVCiOlAyjMplImDdjV3kWCzJZw9rwzvGwmpGuPmfX//Yxblh0pkzPcFcxA81iwmxA==", 1593 | "dev": true 1594 | }, 1595 | "rx-lite": { 1596 | "version": "3.1.2", 1597 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", 1598 | "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", 1599 | "dev": true 1600 | }, 1601 | "safe-buffer": { 1602 | "version": "5.1.1", 1603 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 1604 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 1605 | }, 1606 | "semver": { 1607 | "version": "5.3.0", 1608 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", 1609 | "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", 1610 | "dev": true 1611 | }, 1612 | "shallow-equals": { 1613 | "version": "1.0.0", 1614 | "resolved": "https://registry.npmjs.org/shallow-equals/-/shallow-equals-1.0.0.tgz", 1615 | "integrity": "sha1-JLdL8cY0wR7Uxxgqbfb7MA3OQ5A=", 1616 | "dev": true 1617 | }, 1618 | "shelljs": { 1619 | "version": "0.7.8", 1620 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", 1621 | "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", 1622 | "dev": true, 1623 | "requires": { 1624 | "glob": "7.1.2", 1625 | "interpret": "1.1.0", 1626 | "rechoir": "0.6.2" 1627 | } 1628 | }, 1629 | "signed-varint": { 1630 | "version": "2.0.1", 1631 | "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz", 1632 | "integrity": "sha1-UKmYnafJjCxh2tEZvJdHDvhSgSk=", 1633 | "requires": { 1634 | "varint": "5.0.0" 1635 | } 1636 | }, 1637 | "slice-ansi": { 1638 | "version": "0.0.4", 1639 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", 1640 | "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", 1641 | "dev": true 1642 | }, 1643 | "sprintf-js": { 1644 | "version": "1.0.3", 1645 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1646 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1647 | "dev": true 1648 | }, 1649 | "standard": { 1650 | "version": "10.0.3", 1651 | "resolved": "https://registry.npmjs.org/standard/-/standard-10.0.3.tgz", 1652 | "integrity": "sha512-JURZ+85ExKLQULckDFijdX5WHzN6RC7fgiZNSV4jFQVo+3tPoQGHyBrGekye/yf0aOfb4210EM5qPNlc2cRh4w==", 1653 | "dev": true, 1654 | "requires": { 1655 | "eslint": "3.19.0", 1656 | "eslint-config-standard": "10.2.1", 1657 | "eslint-config-standard-jsx": "4.0.2", 1658 | "eslint-plugin-import": "2.2.0", 1659 | "eslint-plugin-node": "4.2.3", 1660 | "eslint-plugin-promise": "3.5.0", 1661 | "eslint-plugin-react": "6.10.3", 1662 | "eslint-plugin-standard": "3.0.1", 1663 | "standard-engine": "7.0.0" 1664 | } 1665 | }, 1666 | "standard-engine": { 1667 | "version": "7.0.0", 1668 | "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-7.0.0.tgz", 1669 | "integrity": "sha1-67d7nI/CyBZf+jU72Rug3/Qa9pA=", 1670 | "dev": true, 1671 | "requires": { 1672 | "deglob": "2.1.0", 1673 | "get-stdin": "5.0.1", 1674 | "minimist": "1.2.0", 1675 | "pkg-conf": "2.1.0" 1676 | }, 1677 | "dependencies": { 1678 | "minimist": { 1679 | "version": "1.2.0", 1680 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1681 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 1682 | "dev": true 1683 | } 1684 | } 1685 | }, 1686 | "stream-shift": { 1687 | "version": "1.0.0", 1688 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", 1689 | "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" 1690 | }, 1691 | "string-width": { 1692 | "version": "1.0.2", 1693 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1694 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1695 | "dev": true, 1696 | "requires": { 1697 | "code-point-at": "1.1.0", 1698 | "is-fullwidth-code-point": "1.0.0", 1699 | "strip-ansi": "3.0.1" 1700 | } 1701 | }, 1702 | "string.prototype.trim": { 1703 | "version": "1.1.2", 1704 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", 1705 | "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", 1706 | "dev": true, 1707 | "requires": { 1708 | "define-properties": "1.1.2", 1709 | "es-abstract": "1.10.0", 1710 | "function-bind": "1.1.1" 1711 | } 1712 | }, 1713 | "string_decoder": { 1714 | "version": "1.0.3", 1715 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 1716 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 1717 | "requires": { 1718 | "safe-buffer": "5.1.1" 1719 | } 1720 | }, 1721 | "strip-ansi": { 1722 | "version": "3.0.1", 1723 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1724 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1725 | "dev": true, 1726 | "requires": { 1727 | "ansi-regex": "2.1.1" 1728 | } 1729 | }, 1730 | "strip-bom": { 1731 | "version": "3.0.0", 1732 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1733 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 1734 | "dev": true 1735 | }, 1736 | "strip-json-comments": { 1737 | "version": "2.0.1", 1738 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1739 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1740 | "dev": true 1741 | }, 1742 | "supports-color": { 1743 | "version": "2.0.0", 1744 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1745 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1746 | "dev": true 1747 | }, 1748 | "table": { 1749 | "version": "3.8.3", 1750 | "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", 1751 | "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", 1752 | "dev": true, 1753 | "requires": { 1754 | "ajv": "4.11.8", 1755 | "ajv-keywords": "1.5.1", 1756 | "chalk": "1.1.3", 1757 | "lodash": "4.17.5", 1758 | "slice-ansi": "0.0.4", 1759 | "string-width": "2.1.1" 1760 | }, 1761 | "dependencies": { 1762 | "ansi-regex": { 1763 | "version": "3.0.0", 1764 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1765 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 1766 | "dev": true 1767 | }, 1768 | "is-fullwidth-code-point": { 1769 | "version": "2.0.0", 1770 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1771 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1772 | "dev": true 1773 | }, 1774 | "string-width": { 1775 | "version": "2.1.1", 1776 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1777 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1778 | "dev": true, 1779 | "requires": { 1780 | "is-fullwidth-code-point": "2.0.0", 1781 | "strip-ansi": "4.0.0" 1782 | } 1783 | }, 1784 | "strip-ansi": { 1785 | "version": "4.0.0", 1786 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1787 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1788 | "dev": true, 1789 | "requires": { 1790 | "ansi-regex": "3.0.0" 1791 | } 1792 | } 1793 | } 1794 | }, 1795 | "tape": { 1796 | "version": "4.9.0", 1797 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.0.tgz", 1798 | "integrity": "sha512-j0jO9BiScfqtPBb9QmPLL0qvxXMz98xjkMb7x8lKipFlJZwNJkqkWPou+NU4V6T9RnVh1kuSthLE8gLrN8bBfw==", 1799 | "dev": true, 1800 | "requires": { 1801 | "deep-equal": "1.0.1", 1802 | "defined": "1.0.0", 1803 | "for-each": "0.3.2", 1804 | "function-bind": "1.1.1", 1805 | "glob": "7.1.2", 1806 | "has": "1.0.1", 1807 | "inherits": "2.0.3", 1808 | "minimist": "1.2.0", 1809 | "object-inspect": "1.5.0", 1810 | "resolve": "1.5.0", 1811 | "resumer": "0.0.0", 1812 | "string.prototype.trim": "1.1.2", 1813 | "through": "2.3.8" 1814 | }, 1815 | "dependencies": { 1816 | "minimist": { 1817 | "version": "1.2.0", 1818 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1819 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 1820 | "dev": true 1821 | } 1822 | } 1823 | }, 1824 | "text-table": { 1825 | "version": "0.2.0", 1826 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1827 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1828 | "dev": true 1829 | }, 1830 | "through": { 1831 | "version": "2.3.8", 1832 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1833 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1834 | "dev": true 1835 | }, 1836 | "through2": { 1837 | "version": "2.0.3", 1838 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", 1839 | "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", 1840 | "requires": { 1841 | "readable-stream": "2.3.4", 1842 | "xtend": "4.0.1" 1843 | } 1844 | }, 1845 | "thunky": { 1846 | "version": "1.0.2", 1847 | "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", 1848 | "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=" 1849 | }, 1850 | "type-check": { 1851 | "version": "0.3.2", 1852 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1853 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1854 | "dev": true, 1855 | "requires": { 1856 | "prelude-ls": "1.1.2" 1857 | } 1858 | }, 1859 | "typedarray": { 1860 | "version": "0.0.6", 1861 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1862 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1863 | "dev": true 1864 | }, 1865 | "uniq": { 1866 | "version": "1.0.1", 1867 | "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", 1868 | "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", 1869 | "dev": true 1870 | }, 1871 | "user-home": { 1872 | "version": "2.0.0", 1873 | "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", 1874 | "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", 1875 | "dev": true, 1876 | "requires": { 1877 | "os-homedir": "1.0.2" 1878 | } 1879 | }, 1880 | "util-deprecate": { 1881 | "version": "1.0.2", 1882 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1883 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1884 | }, 1885 | "varint": { 1886 | "version": "5.0.0", 1887 | "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", 1888 | "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8=" 1889 | }, 1890 | "wordwrap": { 1891 | "version": "1.0.0", 1892 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1893 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1894 | "dev": true 1895 | }, 1896 | "wrappy": { 1897 | "version": "1.0.2", 1898 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1899 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1900 | }, 1901 | "write": { 1902 | "version": "0.2.1", 1903 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1904 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1905 | "dev": true, 1906 | "requires": { 1907 | "mkdirp": "0.5.1" 1908 | } 1909 | }, 1910 | "xtend": { 1911 | "version": "4.0.1", 1912 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1913 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 1914 | } 1915 | } 1916 | } 1917 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "union-hyperdb", 3 | "version": "0.0.2", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "async-each": "^1.0.1", 8 | "bulk-write-stream": "^1.1.3", 9 | "call-me-maybe": "^1.0.1", 10 | "cmp": "0.0.2", 11 | "codecs": "^1.2.0", 12 | "duplexify": "^3.6.0", 13 | "merge-stream": "^1.0.1", 14 | "nanoiterator": "^1.2.0", 15 | "protocol-buffers": "^4.0.2", 16 | "pumpify": "^1.5.1", 17 | "through2": "^2.0.3", 18 | "thunky": "^1.0.2" 19 | }, 20 | "devDependencies": { 21 | "corestore": "andrewosh/corestore", 22 | "hyperdb": "andrewosh/hyperdb#ready-for-typed-hyperdb", 23 | "random-access-memory": "^2.4.0", 24 | "rimraf": "^2.6.2", 25 | "shallow-equals": "^1.0.0", 26 | "standard": "^10.0.3", 27 | "tape": "^4.8.0" 28 | }, 29 | "scripts": { 30 | "test": "standard && tape test/*.js && rimraf test/test-storage" 31 | }, 32 | "keywords": [ 33 | "hyperdb", 34 | "union", 35 | "layer", 36 | "dat" 37 | ], 38 | "author": "Andrew Osheroff ", 39 | "license": "MIT" 40 | } 41 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var create = require('./helpers/create') 3 | 4 | test('put/get with a single layer', function (t) { 5 | t.plan(7) 6 | 7 | create.fromLayers([ 8 | [ 9 | { type: 'put', key: 'a', value: 'hello' }, 10 | { type: 'put', key: 'b', value: 'goodbye' } 11 | ] 12 | ], function (err, db) { 13 | t.error(err) 14 | db.get('a', function (err, nodes) { 15 | t.error(err) 16 | t.same(nodes.length, 1) 17 | t.same(nodes[0].value, 'hello') 18 | db.get('b', function (err, nodes) { 19 | t.error(err) 20 | t.same(nodes.length, 1) 21 | t.same(nodes[0].value, 'goodbye') 22 | }) 23 | }) 24 | }) 25 | }) 26 | 27 | test('put/get with two layers', function (t) { 28 | t.plan(10) 29 | 30 | create.fromLayers([ 31 | [ 32 | { type: 'put', key: 'a', value: 'hello' }, 33 | { type: 'put', key: 'b', value: 'goodbye' } 34 | ], 35 | [ 36 | { type: 'put', key: 'a', value: 'dog' }, 37 | { type: 'put', key: 'c', value: 'human' } 38 | ] 39 | ], function (err, db) { 40 | t.error(err) 41 | db.get('a', function (err, nodes) { 42 | t.error(err) 43 | t.same(nodes.length, 1) 44 | t.same(nodes[0].value, 'dog') 45 | db.get('b', function (err, nodes) { 46 | t.error(err) 47 | t.same(nodes.length, 1) 48 | t.same(nodes[0].value, 'goodbye') 49 | db.get('c', function (err, nodes) { 50 | t.error(err) 51 | t.same(nodes.length, 1) 52 | t.same(nodes[0].value, 'human') 53 | }) 54 | }) 55 | }) 56 | }) 57 | }) 58 | 59 | test('put/get with two layers and a deletion', function (t) { 60 | t.plan(4) 61 | 62 | create.fromLayers([ 63 | [ 64 | { type: 'put', key: 'a', value: 'hello' }, 65 | { type: 'put', key: 'b', value: 'goodbye' } 66 | ], 67 | [ 68 | { type: 'put', key: 'a', value: 'dog' }, 69 | { type: 'put', key: 'c', value: 'human' } 70 | ] 71 | ], function (err, db) { 72 | t.error(err) 73 | db.del('a', function (err) { 74 | t.error(err) 75 | db.get('a', function (err, nodes) { 76 | t.error(err) 77 | t.same(nodes, null) 78 | }) 79 | }) 80 | }) 81 | }) 82 | -------------------------------------------------------------------------------- /test/diff.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var equals = require('shallow-equals') 3 | 4 | var create = require('./helpers/create') 5 | 6 | test('simple diff, no links', t => { 7 | create.fromLayers([ 8 | [ 9 | { type: 'put', key: 'a', value: 'hello' }, 10 | { type: 'put', key: 'b', value: 'goodbye' } 11 | ] 12 | ], function (err, db) { 13 | t.error(err) 14 | validate(db) 15 | }) 16 | 17 | var expected = [ 18 | { left: 'a', right: null }, 19 | { left: 'b', right: null } 20 | ] 21 | var seen = 0 22 | 23 | function validate (db) { 24 | var diff = db.createDiffStream(null, '/') 25 | diff.on('data', (diff) => { 26 | for (var i = 0; i < expected.length; i++) { 27 | if (equals(toKeys(diff), expected[i])) { 28 | seen++ 29 | return 30 | } 31 | } 32 | t.fail('unexpected diff:', diff) 33 | }) 34 | diff.on('end', () => { 35 | t.equals(expected.length, seen) 36 | t.end() 37 | }) 38 | diff.on('error', (err) => { 39 | t.fail(err.message) 40 | t.end() 41 | }) 42 | } 43 | }) 44 | 45 | test('diff between versions, no links', t => { 46 | var version 47 | create.fromLayers([ 48 | [ 49 | { type: 'put', key: 'a', value: 'hello' }, 50 | { type: 'put', key: 'b', value: 'goodbye' } 51 | ] 52 | ], function (err, db) { 53 | t.error(err) 54 | db.version((err, v) => { 55 | t.error(err) 56 | version = v 57 | db.put('a', 'something', err => { 58 | t.error(err) 59 | validate(db) 60 | }) 61 | }) 62 | }) 63 | 64 | var expected = [ 65 | { left: 'a', right: 'a' } 66 | ] 67 | var seen = 0 68 | 69 | function validate (db) { 70 | var diff = db.createDiffStream(version, '/') 71 | diff.on('data', (diff) => { 72 | for (var i = 0; i < expected.length; i++) { 73 | if (equals(toKeys(diff), expected[i])) { 74 | seen++ 75 | return 76 | } 77 | } 78 | t.fail('unexpected diff:', diff) 79 | }) 80 | diff.on('end', () => { 81 | t.equals(expected.length, seen) 82 | t.end() 83 | }) 84 | diff.on('error', (err) => { 85 | t.fail(err.message) 86 | t.end() 87 | }) 88 | } 89 | }) 90 | 91 | test('diff with links', t => { 92 | var version 93 | create.fromLayers([ 94 | [ 95 | { type: 'put', key: 'a', value: 'hello' } 96 | ], 97 | [ 98 | { type: 'mount', key: 'b', remotePath: '/' }, 99 | { type: 'put', key: 'c', value: 'goodbye' } 100 | ] 101 | ], function (err, db) { 102 | t.error(err) 103 | db.version((err, v) => { 104 | t.error(err) 105 | version = v 106 | db.put('a', 'something', err => { 107 | t.error(err) 108 | db.put('b/c', 'other', err => { 109 | t.error(err) 110 | db.put('c', 'blah', err => { 111 | t.error(err) 112 | validate(db) 113 | }) 114 | }) 115 | }) 116 | }) 117 | }) 118 | 119 | var expected = [ 120 | { left: 'a', right: null }, 121 | { left: 'b/c', right: null }, 122 | { left: 'c', right: 'c' } 123 | ] 124 | var seen = 0 125 | 126 | function validate (db) { 127 | var diff = db.createDiffStream(version, '') 128 | diff.on('data', (diff) => { 129 | for (var i = 0; i < expected.length; i++) { 130 | if (equals(toKeys(diff), expected[i])) { 131 | seen++ 132 | return 133 | } 134 | } 135 | t.fail('unexpected diff:', diff) 136 | }) 137 | diff.on('end', () => { 138 | t.equals(expected.length, seen) 139 | t.end() 140 | }) 141 | diff.on('error', (err) => { 142 | t.fail(err.message) 143 | t.end() 144 | }) 145 | } 146 | }) 147 | 148 | function toKeys (diff) { 149 | return { 150 | left: diff.left ? diff.left[0].key : null, 151 | right: diff.right ? diff.right[0].key : null 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /test/fork.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var create = require('./helpers/create') 3 | 4 | test('can create a static fork', function (t) { 5 | create.fromLayers([ 6 | [ 7 | { type: 'put', key: 'a', value: 'hello' }, 8 | { type: 'put', key: 'b', value: 'goodbye' } 9 | ] 10 | ], function (err, db) { 11 | t.error(err) 12 | db.fork(function (err, fork) { 13 | t.error(err) 14 | fork.get('a', function (err, nodes) { 15 | t.error(err) 16 | t.same(nodes[0].value, 'hello') 17 | t.end() 18 | }) 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/helpers/create.js: -------------------------------------------------------------------------------- 1 | const p = require('path') 2 | 3 | const hyperdb = require('hyperdb') 4 | const each = require('async-each') 5 | const corestore = require('corestore') 6 | 7 | const uniondb = require('../..') 8 | 9 | const STORAGE_DIR = p.join(__dirname, '..', 'test-storage') 10 | var idx = 0 11 | 12 | async function makeFactory (cb) { 13 | var store = corestore(p.join(STORAGE_DIR, '' + idx++), { 14 | network: { 15 | disable: true 16 | } 17 | }) 18 | store.ready(err => { 19 | if (err) return cb(err) 20 | 21 | function coreFactory (key, opts) { 22 | return store.get(key, opts) 23 | } 24 | 25 | function dbFactory (key, opts) { 26 | opts.lex = true 27 | return hyperdb(coreFactory, key, opts) 28 | } 29 | 30 | return cb(null, dbFactory) 31 | }) 32 | } 33 | 34 | async function fromLayers (layerBatches, cb) { 35 | var dbs = [] 36 | var currentDb = null 37 | var currentIdx = 0 38 | 39 | var factory 40 | makeFactory((err, f) => { 41 | if (err) return cb(err) 42 | factory = f 43 | makeNextLayer() 44 | }) 45 | 46 | function makeNextLayer (err) { 47 | if (err) return cb(err) 48 | if (currentIdx === layerBatches.length) return cb(null, currentDb, dbs) 49 | if (currentDb) { 50 | currentDb.version(function (err, version) { 51 | if (err) return cb(err) 52 | return makeUnionDB({ 53 | parent: { 54 | key: currentDb.key, 55 | version: version 56 | }, 57 | valueEncoding: 'utf-8' 58 | }) 59 | }) 60 | } else { 61 | return makeUnionDB({ 62 | valueEncoding: 'utf-8' 63 | }) 64 | } 65 | } 66 | 67 | function makeUnionDB (opts) { 68 | var batch = layerBatches[currentIdx++] 69 | var db = uniondb(factory, null, opts) 70 | db.ready(err => { 71 | if (err) throw err 72 | each(batch, function (cmd, next) { 73 | switch (cmd.type) { 74 | case 'put': 75 | db.put(cmd.key, cmd.value, next) 76 | break 77 | case 'mount': 78 | if (cmd.versioned) { 79 | currentDb.version(function (err, version) { 80 | if (err) throw err 81 | db.mount(currentDb.key, cmd.key, { 82 | version: version, 83 | remotePath: cmd.remotePath 84 | }, next) 85 | }) 86 | } else { 87 | db.mount(currentDb.key, cmd.key, { 88 | remotePath: cmd.remotePath 89 | }, next) 90 | } 91 | break 92 | case 'delete': 93 | db.delete(cmd.key, next) 94 | break 95 | } 96 | }, function (err) { 97 | if (err) throw err 98 | currentDb = db 99 | dbs.push(db) 100 | process.nextTick(makeNextLayer) 101 | }) 102 | }) 103 | } 104 | } 105 | 106 | function twoFromLayers (layerFiles, cb) { 107 | makeFactory((err, f1) => { 108 | if (err) return cb(err) 109 | finish(f1) 110 | }) 111 | 112 | function finish (f1) { 113 | fromLayers(layerFiles, function (err, db1) { 114 | if (err) return cb(err) 115 | db1.ready(err => { 116 | if (err) return cb(err) 117 | var db2 = uniondb(f1, db1.key, { valueEncoding: 'utf8' }) 118 | return cb(null, db1, db2) 119 | }) 120 | }) 121 | } 122 | } 123 | 124 | function one (opts, cb) { 125 | makeFactory((err, f1) => { 126 | if (err) return cb(err) 127 | let db1 = uniondb(f1, Object.assign(opts, { valueEncoding: 'utf-8' })) 128 | db1.ready(err => { 129 | if (err) return cb(err) 130 | return cb(null, db1) 131 | }) 132 | }) 133 | } 134 | 135 | function two (cb) { 136 | makeFactory((err, f1) => { 137 | if (err) return cb(err) 138 | makeFactory((err, f2) => { 139 | if (err) return cb(err) 140 | finish(f1, f2) 141 | }) 142 | }) 143 | 144 | function finish (f1, f2) { 145 | var db1 = uniondb(f1, { valueEncoding: 'utf8' }) 146 | db1.ready(err => { 147 | if (err) return cb(err) 148 | var db2 = uniondb(f2, db1.key, { valueEncoding: 'utf8' }) 149 | // db2 will not be ready until the first remote-update. 150 | return cb(null, db1, db2) 151 | }) 152 | } 153 | } 154 | 155 | module.exports = { 156 | fromLayers, 157 | twoFromLayers, 158 | two, 159 | one 160 | } 161 | -------------------------------------------------------------------------------- /test/helpers/replicate.js: -------------------------------------------------------------------------------- 1 | module.exports = replicate 2 | 3 | function replicate (a, b, cb) { 4 | var stream = a.replicate() 5 | stream.pipe(b.replicate()).pipe(stream).on('end', cb) 6 | } 7 | -------------------------------------------------------------------------------- /test/helpers/verify.js: -------------------------------------------------------------------------------- 1 | var p = require('path') 2 | 3 | var messages = require('../../lib/messages') 4 | 5 | module.exports.indices = verifyIndices 6 | module.exports.values = verifyValues 7 | 8 | function verifyIndices (t, db, indicesByKey) { 9 | Object.keys(indicesByKey).forEach(function (key) { 10 | db._db.get(p.join('/INDEX', key), function (err, nodes) { 11 | t.error(err) 12 | t.same(nodes.length, 1) 13 | var decoded = messages.Entry.decode(nodes[0].value) 14 | t.same(decoded.layerIndex, indicesByKey[key]) 15 | }) 16 | }) 17 | } 18 | 19 | function verifyValues (t, db, valuesByKey) { 20 | Object.keys(valuesByKey).forEach(function (key) { 21 | db.get(key, function (err, nodes) { 22 | t.error(err) 23 | t.same(nodes.length, 1) 24 | t.same(nodes[0].value, valuesByKey[key]) 25 | }) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /test/indexing.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | var create = require('./helpers/create') 4 | var verify = require('./helpers/verify') 5 | 6 | test('put/get with an index and one layer', function (t) { 7 | t.plan(2 + 3 * 2) 8 | 9 | create.fromLayers([ 10 | [ 11 | { type: 'put', key: 'a', value: 'hello' }, 12 | { type: 'put', key: 'b', value: 'goodbye' } 13 | ] 14 | ], function (err, db) { 15 | t.error(err) 16 | db.index(function (err) { 17 | t.error(err) 18 | verify.values(t, db, { 'a': 'hello', 'b': 'goodbye' }) 19 | }) 20 | }) 21 | }) 22 | 23 | test('put/get with an index and two layers', function (t) { 24 | t.plan(2 + 3 * 3) 25 | 26 | create.fromLayers([ 27 | [ 28 | { type: 'put', key: 'a', value: 'hello' }, 29 | { type: 'put', key: 'b', value: 'goodbye' } 30 | ], 31 | [ 32 | { type: 'put', key: 'a', value: 'dog' }, 33 | { type: 'put', key: 'c', value: 'human' } 34 | ] 35 | ], function (err, db) { 36 | t.error(err) 37 | db.index(function (err) { 38 | t.error(err) 39 | verify.values(t, db, { 'a': 'dog', 'b': 'goodbye', 'c': 'human' }) 40 | }) 41 | }) 42 | }) 43 | 44 | test('index entries are correctly added', function (t) { 45 | t.plan(2 + 3 * 4 + 3 * 4) 46 | create.fromLayers([ 47 | [ 48 | { type: 'put', key: 'a', value: 'hello' }, 49 | { type: 'put', key: 'b', value: 'goodbye' } 50 | ], 51 | [ 52 | { type: 'put', key: 'a', value: 'dog' }, 53 | { type: 'put', key: 'c', value: 'human' } 54 | ], 55 | [ 56 | { type: 'put', key: 'c', value: 'somewhere' }, 57 | { type: 'put', key: 'd', value: 'rainbow' } 58 | ] 59 | ], function (err, db) { 60 | t.error(err) 61 | db.index(function (err) { 62 | t.error(err) 63 | verify.indices(t, db, { 'a': 1, 'b': 2, 'c': 0, 'd': 0 }) 64 | verify.values(t, db, { 'a': 'dog', 'b': 'goodbye', 'c': 'somewhere', 'd': 'rainbow' }) 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /test/iterator.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var create = require('./helpers/create') 3 | 4 | test('can iterate over local values without layers', t => { 5 | create.fromLayers([ 6 | [ 7 | { type: 'put', key: 'a', value: 'hello' }, 8 | { type: 'put', key: 'z', value: 'world' }, 9 | { type: 'put', key: 'b', value: 'goodbye' }, 10 | { type: 'put', key: 'd', value: 'dog' }, 11 | { type: 'put', key: 'f', value: 'yes' } 12 | ] 13 | ], (err, db) => { 14 | t.error(err) 15 | db.index(err => { 16 | t.error(err) 17 | let keys = ['a', 'b', 'd', 'f', 'z'] 18 | testIteratorOrder(t, false, db.lexIterator(), keys, err => { 19 | t.error(err) 20 | t.end() 21 | }) 22 | }) 23 | }) 24 | }) 25 | 26 | test('multiple layer iteration fails without indexing', t => { 27 | create.fromLayers([ 28 | [ 29 | { type: 'put', key: 'a', value: 'hello' }, 30 | { type: 'put', key: 'z', value: 'world' }, 31 | { type: 'put', key: 'b', value: 'goodbye' } 32 | ], 33 | [ 34 | { type: 'put', key: 'd', value: 'dog' }, 35 | { type: 'put', key: 'f', value: 'yes' } 36 | ] 37 | ], (err, db) => { 38 | t.error(err) 39 | let ite = db.lexIterator() 40 | ite.next((err, value) => { 41 | t.true(err) 42 | t.end() 43 | }) 44 | }) 45 | }) 46 | 47 | test('can iterate over local values in multiple layers', t => { 48 | create.fromLayers([ 49 | [ 50 | { type: 'put', key: 'a', value: 'hello' }, 51 | { type: 'put', key: 'z', value: 'world' } 52 | ], 53 | [ 54 | { type: 'put', key: 'b', value: 'goodbye' }, 55 | { type: 'put', key: 'd', value: 'dog' }, 56 | { type: 'put', key: 'f', value: 'yes' } 57 | ] 58 | ], (err, db) => { 59 | t.error(err) 60 | db.index(err => { 61 | t.error(err) 62 | let keys = ['a', 'b', 'd', 'f', 'z'] 63 | testIteratorOrder(t, false, db.lexIterator(), keys, err => { 64 | t.error(err) 65 | t.end() 66 | }) 67 | }) 68 | }) 69 | }) 70 | 71 | test('can iterate within a single symlink', t => { 72 | create.fromLayers([ 73 | [ 74 | { type: 'put', key: 'z', value: 'hello' }, 75 | { type: 'put', key: 'a', value: 'there' }, 76 | { type: 'put', key: 'f', value: 'goodbye' } 77 | ], 78 | [ 79 | { type: 'mount', key: 'b', remotePath: '/' }, 80 | { type: 'put', key: 'c', value: 'goodbye' } 81 | ] 82 | ], (err, db) => { 83 | t.error(err) 84 | db.index(err => { 85 | t.error(err) 86 | let keys = ['b/a', 'b/f', 'b/z'] 87 | testIteratorOrder(t, false, db.lexIterator({ gt: 'b', lt: 'b/zz' }), keys, err => { 88 | t.error(err) 89 | t.end() 90 | }) 91 | }) 92 | }) 93 | }) 94 | 95 | // Copied from hyperdb 96 | 97 | function testIteratorOrder (t, reverse, iterator, expected, done) { 98 | var sorted = expected.slice().sort() 99 | if (reverse) sorted.reverse() 100 | each(iterator, onEach, onDone) 101 | function onEach (err, node) { 102 | t.error(err, 'no error') 103 | var key = node.key || node[0].key 104 | t.same(key, sorted.shift()) 105 | } 106 | function onDone () { 107 | t.same(sorted.length, 0) 108 | if (done === undefined) t.end() 109 | else done() 110 | } 111 | } 112 | 113 | function each (ite, cb, done) { 114 | ite.next(function loop (err, node) { 115 | if (err) return cb(err) 116 | if (!node) return done() 117 | cb(null, node) 118 | ite.next(loop) 119 | }) 120 | } 121 | -------------------------------------------------------------------------------- /test/linking.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | var create = require('./helpers/create') 4 | var verify = require('./helpers/verify') 5 | 6 | test('a link can be created and read', function (t) { 7 | create.fromLayers([ 8 | [ 9 | { type: 'put', key: 'a', value: 'hello' }, 10 | { type: 'put', key: 'b', value: 'goodbye' } 11 | ] 12 | ], function (err, db) { 13 | t.error(err) 14 | db.sub('link', function (err, sub) { 15 | t.error(err) 16 | db.get('link', function (err, nodes) { 17 | t.error(err) 18 | t.same(nodes.length, 1) 19 | t.same(nodes[0].value.localPath, 'link') 20 | t.end() 21 | }) 22 | }) 23 | }) 24 | }) 25 | 26 | test('simple read from a mount works', function (t) { 27 | t.plan(1 + 3 * 3) 28 | 29 | create.fromLayers([ 30 | [ 31 | { type: 'put', key: 'a', value: 'hello' } 32 | ], 33 | [ 34 | { type: 'mount', key: 'b', remotePath: '/' }, 35 | { type: 'put', key: 'c', value: 'goodbye' } 36 | ] 37 | ], function (err, db) { 38 | t.error(err) 39 | verify.values(t, db, { 40 | 'b/a': 'hello', 41 | 'a': 'hello', 42 | 'c': 'goodbye' 43 | }) 44 | }) 45 | }) 46 | 47 | test('read from a mount with a remote link works', function (t) { 48 | t.plan(1 + 3 * 2) 49 | 50 | create.fromLayers([ 51 | [ 52 | { type: 'put', key: 'a/b/c', value: 'hello' } 53 | ], 54 | [ 55 | { type: 'mount', key: 'b', remotePath: '/a/b' }, 56 | { type: 'put', key: 'd', value: 'goodbye' } 57 | ] 58 | ], function (err, db) { 59 | t.error(err) 60 | verify.values(t, db, { 61 | 'b/c': 'hello', 62 | 'd': 'goodbye' 63 | }) 64 | }) 65 | }) 66 | 67 | test('parent layers have frozen links', function (t) { 68 | t.plan(4 + 3 * 3) 69 | 70 | create.fromLayers([ 71 | [ 72 | { type: 'put', key: 'a', value: 'hello' } 73 | ], 74 | [ 75 | { type: 'mount', key: 'b', remotePath: '/' }, 76 | { type: 'put', key: 'c', value: 'goodbye' } 77 | ], 78 | [ 79 | { type: 'put', key: 'd', value: 'something' } 80 | ] 81 | ], function (err, topLayer, layers) { 82 | t.error(err) 83 | 84 | var firstLayer = layers[0] 85 | var secondLayer = layers[1] 86 | 87 | firstLayer.put('a', 'world', function (err) { 88 | t.error(err) 89 | secondLayer.get('b/a', function (err, nodes) { 90 | t.error(err) 91 | t.same(nodes[0].value, 'world') 92 | verify.values(t, topLayer, { 93 | 'b/a': 'hello', 94 | 'c': 'goodbye', 95 | 'd': 'something' 96 | }) 97 | }) 98 | }) 99 | }) 100 | }) 101 | 102 | test('symlinks replicate') 103 | test('symlinks are automatically frozen in layers') 104 | -------------------------------------------------------------------------------- /test/list.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var create = require('./helpers/create') 3 | 4 | test('put/list with a single layer', function (t) { 5 | t.plan(3) 6 | 7 | create.fromLayers([ 8 | [ 9 | { type: 'put', key: 'a', value: 'hello' }, 10 | { type: 'put', key: 'b', value: 'goodbye' } 11 | ] 12 | ], function (err, db) { 13 | t.error(err) 14 | db.list('/', function (err, l) { 15 | t.error(err) 16 | t.same(l, ['a', 'b']) 17 | }) 18 | }) 19 | }) 20 | 21 | test('put/get with two layers', function (t) { 22 | t.plan(3) 23 | 24 | create.fromLayers([ 25 | [ 26 | { type: 'put', key: 'a', value: 'hello' }, 27 | { type: 'put', key: 'b', value: 'goodbye' } 28 | ], 29 | [ 30 | { type: 'put', key: 'a', value: 'dog' }, 31 | { type: 'put', key: 'c', value: 'human' } 32 | ] 33 | ], function (err, db) { 34 | t.error(err) 35 | db.list('/', function (err, l) { 36 | t.error(err) 37 | t.same(l, ['a', 'b', 'c']) 38 | }) 39 | }) 40 | }) 41 | 42 | test('put/get with two layers and a deletion', function (t) { 43 | t.plan(4) 44 | 45 | create.fromLayers([ 46 | [ 47 | { type: 'put', key: 'a', value: 'hello' }, 48 | { type: 'put', key: 'b', value: 'goodbye' } 49 | ], 50 | [ 51 | { type: 'put', key: 'a', value: 'dog' }, 52 | { type: 'put', key: 'c', value: 'human' } 53 | ] 54 | ], function (err, db) { 55 | t.error(err) 56 | db.del('a', function (err) { 57 | t.error(err) 58 | db.list('/', function (err, l) { 59 | t.error(err) 60 | t.same(l, ['b', 'c']) 61 | }) 62 | }) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /test/replicate.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | var replicate = require('./helpers/replicate') 4 | var create = require('./helpers/create') 5 | var verify = require('./helpers/verify') 6 | 7 | test('should replicate without any layers', function (t) { 8 | t.plan(3 + 3 * 1) 9 | 10 | create.two(function (err, db1, db2) { 11 | t.error(err) 12 | db1.put('cat', 'dog', function (err) { 13 | t.error(err) 14 | replicate(db1, db2, function (err) { 15 | t.error(err) 16 | verify.values(t, db2, { 17 | 'cat': 'dog' 18 | }) 19 | }) 20 | }) 21 | }) 22 | }) 23 | 24 | test('should replicate between two databases with no local changes', function (t) { 25 | t.plan(2 + 3 * 2) 26 | 27 | create.twoFromLayers([ 28 | [ 29 | { type: 'put', key: 'hello', value: 'goodbye' }, 30 | { type: 'put', key: 'cat', value: 'dog' } 31 | ] 32 | ], function (err, db1, db2) { 33 | t.error(err) 34 | replicate(db1, db2, function (err) { 35 | t.error(err) 36 | verify.values(t, db2, { 37 | 'cat': 'dog', 38 | 'hello': 'goodbye' 39 | }) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/trie.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | var Trie = require('../lib/trie') 4 | 5 | test('simple gets', function (t) { 6 | var trie = new Trie() 7 | trie.add('a', 'hello') 8 | trie.add('b', 'goodbye') 9 | var a = trie.get('a') 10 | t.equal(a, 'hello') 11 | t.end() 12 | }) 13 | 14 | test('exact subpaths', function (t) { 15 | var trie = new Trie() 16 | trie.add('/a', 'hello') 17 | trie.add('/a/b', 'goodbye') 18 | var a = trie.get('/a') 19 | var b = trie.get('/a/b') 20 | t.equal(a, 'hello') 21 | t.equal(b, 'goodbye') 22 | t.end() 23 | }) 24 | 25 | test('enclosing path', function (t) { 26 | var trie = new Trie() 27 | trie.add('/a', 'hello') 28 | trie.add('/a/b', 'goodbye') 29 | var b = trie.get('/a/b') 30 | var c = trie.get('/a/c', { enclosing: true }) 31 | var d = trie.get('/a/d') 32 | t.equal(b, 'goodbye') 33 | t.equal(c, 'hello') 34 | t.equal(d, null) 35 | t.end() 36 | }) 37 | 38 | test('multiple values at path', function (t) { 39 | var trie = new Trie() 40 | trie.add('/a', 'hello') 41 | trie.add('/a', 'goodbye', { push: true }) 42 | var results = trie.get('/a') 43 | t.deepEqual(results, ['hello', 'goodbye']) 44 | t.end() 45 | }) 46 | -------------------------------------------------------------------------------- /test/watch.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var create = require('./helpers/create') 3 | 4 | test('should emit changes to watch functions', t => { 5 | create.fromLayers([ 6 | [ 7 | { type: 'put', key: '/a', value: 'hello' }, 8 | { type: 'put', key: '/b', value: 'goodbye' } 9 | ] 10 | ], function (err, db) { 11 | t.error(err) 12 | db.watch('/', () => { 13 | t.end() 14 | }) 15 | db.put('a', 'hello', function (err) { 16 | t.error(err) 17 | }) 18 | }) 19 | }) 20 | 21 | test('should emit deletions', t => { 22 | create.fromLayers([ 23 | [ 24 | { type: 'put', key: '/a', value: 'hello' }, 25 | { type: 'put', key: '/b', value: 'goodbye' } 26 | ] 27 | ], function (err, db) { 28 | t.error(err) 29 | db.watch('/', () => { 30 | t.end() 31 | }) 32 | db.del('a', function (err) { 33 | t.error(err) 34 | }) 35 | }) 36 | }) 37 | 38 | test('should emit changes in symlinks to watch functions', t => { 39 | create.fromLayers([ 40 | [ 41 | { type: 'put', key: 'a', value: 'hello' } 42 | ], 43 | [ 44 | { type: 'mount', key: 'b', remotePath: '/' }, 45 | { type: 'put', key: 'c', value: 'goodbye' } 46 | ] 47 | ], function (err, db) { 48 | t.error(err) 49 | db.watch('b', () => { 50 | t.end() 51 | }) 52 | db.put('b/d', 'hello', err => { 53 | t.error(err) 54 | }) 55 | }) 56 | }) 57 | 58 | test('should stop watching', t => { 59 | create.fromLayers([ 60 | [ 61 | { type: 'put', key: 'a', value: 'hello' } 62 | ], 63 | [ 64 | { type: 'mount', key: 'b', remotePath: '/' }, 65 | { type: 'put', key: 'c', value: 'goodbye' } 66 | ] 67 | ], function (err, db) { 68 | t.error(err) 69 | var calls = 0 70 | var unwatch = db.watch('b', () => { 71 | calls++ 72 | }) 73 | db.put('b/d', 'hello', err => { 74 | t.error(err) 75 | unwatch() 76 | db.put('b/e', 'goodbye', err => { 77 | t.error(err) 78 | t.same(calls, 1) 79 | t.end() 80 | }) 81 | }) 82 | }) 83 | }) 84 | --------------------------------------------------------------------------------