|
1 | 1 | 'use strict'
|
2 | 2 |
|
3 | 3 | const Block = require('ipfs-block')
|
4 |
| -const pull = require('pull-stream') |
5 | 4 | const CID = require('cids')
|
6 |
| -const pullDeferSource = require('pull-defer').source |
7 |
| -const pullTraverse = require('pull-traverse') |
8 |
| -const map = require('async/map') |
9 | 5 | const waterfall = require('async/waterfall')
|
10 | 6 | const mergeOptions = require('merge-options')
|
11 | 7 | const ipldDagCbor = require('ipld-dag-cbor')
|
@@ -283,122 +279,6 @@ class IPLDResolver {
|
283 | 279 | return fancyIterator(next)
|
284 | 280 | }
|
285 | 281 |
|
286 |
| - treeStream (cid, path, options) { |
287 |
| - if (typeof path === 'object') { |
288 |
| - options = path |
289 |
| - path = undefined |
290 |
| - } |
291 |
| - |
292 |
| - options = options || {} |
293 |
| - |
294 |
| - let p |
295 |
| - |
296 |
| - if (!options.recursive) { |
297 |
| - p = pullDeferSource() |
298 |
| - |
299 |
| - waterfall([ |
300 |
| - async () => { |
301 |
| - return this._getFormat(cid.codec) |
302 |
| - }, |
303 |
| - (format, cb) => this.bs.get(cid, (err, block) => { |
304 |
| - if (err) return cb(err) |
305 |
| - cb(null, format, block) |
306 |
| - }), |
307 |
| - (format, block, cb) => format.resolver.tree(block.data, cb) |
308 |
| - ], (err, paths) => { |
309 |
| - if (err) { |
310 |
| - p.abort(err) |
311 |
| - return p |
312 |
| - } |
313 |
| - p.resolve(pull.values(paths)) |
314 |
| - }) |
315 |
| - } |
316 |
| - |
317 |
| - // recursive |
318 |
| - if (options.recursive) { |
319 |
| - p = pull( |
320 |
| - pullTraverse.widthFirst({ |
321 |
| - basePath: null, |
322 |
| - cid: cid |
323 |
| - }, (el) => { |
324 |
| - // pass the paths through the pushable pull stream |
325 |
| - // continue traversing the graph by returning |
326 |
| - // the next cids with deferred |
327 |
| - |
328 |
| - if (typeof el === 'string') { |
329 |
| - return pull.empty() |
330 |
| - } |
331 |
| - |
332 |
| - const deferred = pullDeferSource() |
333 |
| - const cid = el.cid |
334 |
| - |
335 |
| - waterfall([ |
336 |
| - async () => { |
337 |
| - return this._getFormat(cid.codec) |
338 |
| - }, |
339 |
| - (format, cb) => this.bs.get(cid, (err, block) => { |
340 |
| - if (err) return cb(err) |
341 |
| - cb(null, format, block) |
342 |
| - }), |
343 |
| - (format, block, cb) => format.resolver.tree(block.data, (err, paths) => { |
344 |
| - if (err) { |
345 |
| - return cb(err) |
346 |
| - } |
347 |
| - map(paths, (p, cb) => { |
348 |
| - format.resolver.isLink(block.data, p, (err, link) => { |
349 |
| - if (err) { |
350 |
| - return cb(err) |
351 |
| - } |
352 |
| - cb(null, { path: p, link: link }) |
353 |
| - }) |
354 |
| - }, cb) |
355 |
| - }) |
356 |
| - ], (err, paths) => { |
357 |
| - if (err) { |
358 |
| - deferred.abort(err) |
359 |
| - return deferred |
360 |
| - } |
361 |
| - |
362 |
| - deferred.resolve(pull.values(paths.map((p) => { |
363 |
| - const base = el.basePath ? el.basePath + '/' + p.path : p.path |
364 |
| - if (p.link) { |
365 |
| - return { |
366 |
| - basePath: base, |
367 |
| - cid: IPLDResolver._maybeCID(p.link) |
368 |
| - } |
369 |
| - } |
370 |
| - return base |
371 |
| - }))) |
372 |
| - }) |
373 |
| - return deferred |
374 |
| - }), |
375 |
| - pull.map((e) => { |
376 |
| - if (typeof e === 'string') { |
377 |
| - return e |
378 |
| - } |
379 |
| - return e.basePath |
380 |
| - }), |
381 |
| - pull.filter(Boolean) |
382 |
| - ) |
383 |
| - } |
384 |
| - |
385 |
| - // filter out by path |
386 |
| - if (path) { |
387 |
| - return pull( |
388 |
| - p, |
389 |
| - pull.map((el) => { |
390 |
| - if (el.indexOf(path) === 0) { |
391 |
| - el = el.slice(path.length + 1) |
392 |
| - return el |
393 |
| - } |
394 |
| - }), |
395 |
| - pull.filter(Boolean) |
396 |
| - ) |
397 |
| - } |
398 |
| - |
399 |
| - return p |
400 |
| - } |
401 |
| - |
402 | 282 | /**
|
403 | 283 | * Remove IPLD Nodes by the given CIDs.
|
404 | 284 | *
|
@@ -434,6 +314,130 @@ class IPLDResolver {
|
434 | 314 | return fancyIterator(next)
|
435 | 315 | }
|
436 | 316 |
|
| 317 | + /** |
| 318 | + * Returns all the paths that can be resolved into. |
| 319 | + * |
| 320 | + * @param {Object} cid - The ID to get the paths from |
| 321 | + * @param {string} [offsetPath=''] - the path to start to retrieve the other paths from. |
| 322 | + * @param {Object} [userOptions] |
| 323 | + * @param {number} [userOptions.recursive=false] - whether to get the paths recursively or not. `false` resolves only the paths of the given CID. |
| 324 | + * @returns {Iterable.<Promise.<String>>} - Returns an async iterator with paths that can be resolved into |
| 325 | + */ |
| 326 | + tree (cid, offsetPath, userOptions) { |
| 327 | + if (typeof offsetPath === 'object') { |
| 328 | + userOptions = offsetPath |
| 329 | + offsetPath = undefined |
| 330 | + } |
| 331 | + offsetPath = offsetPath || '' |
| 332 | + |
| 333 | + const defaultOptions = { |
| 334 | + recursive: false |
| 335 | + } |
| 336 | + const options = mergeOptions(defaultOptions, userOptions) |
| 337 | + |
| 338 | + // Get available paths from a block |
| 339 | + const getPaths = (cid) => { |
| 340 | + return new Promise(async (resolve, reject) => { |
| 341 | + let format |
| 342 | + try { |
| 343 | + format = await this._getFormat(cid.codec) |
| 344 | + } catch (error) { |
| 345 | + return reject(error) |
| 346 | + } |
| 347 | + this.bs.get(cid, (err, block) => { |
| 348 | + if (err) { |
| 349 | + return reject(err) |
| 350 | + } |
| 351 | + format.resolver.tree(block.data, (err, paths) => { |
| 352 | + if (err) { |
| 353 | + return reject(err) |
| 354 | + } |
| 355 | + return resolve({ paths, block }) |
| 356 | + }) |
| 357 | + }) |
| 358 | + }) |
| 359 | + } |
| 360 | + |
| 361 | + // If a path is a link then follow it and return its CID |
| 362 | + const maybeRecurse = (block, treePath) => { |
| 363 | + return new Promise(async (resolve, reject) => { |
| 364 | + // A treepath we might want to follow recursively |
| 365 | + const format = await this._getFormat(block.cid.codec) |
| 366 | + format.resolver.isLink(block.data, treePath, (err, link) => { |
| 367 | + if (err) { |
| 368 | + return reject(err) |
| 369 | + } |
| 370 | + // Something to follow recusively, hence push it into the queue |
| 371 | + if (link) { |
| 372 | + const cid = IPLDResolver._maybeCID(link) |
| 373 | + resolve(cid) |
| 374 | + } else { |
| 375 | + resolve(null) |
| 376 | + } |
| 377 | + }) |
| 378 | + }) |
| 379 | + } |
| 380 | + |
| 381 | + // The list of paths that will get returned |
| 382 | + let treePaths = [] |
| 383 | + // The current block, needed to call `isLink()` on every interation |
| 384 | + let block |
| 385 | + // The list of items we want to follow recursively. The items are |
| 386 | + // an object consisting of the CID and the currently already resolved |
| 387 | + // path |
| 388 | + const queue = [{ cid, basePath: '' }] |
| 389 | + // The path that was already traversed |
| 390 | + let basePath |
| 391 | + |
| 392 | + const next = () => { |
| 393 | + // End of iteration if there aren't any paths left to return or |
| 394 | + // if we don't want to traverse recursively and have already |
| 395 | + // returne the first level |
| 396 | + if (treePaths.length === 0 && queue.length === 0) { |
| 397 | + return { done: true } |
| 398 | + } |
| 399 | + |
| 400 | + return new Promise(async (resolve, reject) => { |
| 401 | + // There aren't any paths left, get them from the given CID |
| 402 | + if (treePaths.length === 0 && queue.length > 0) { |
| 403 | + ({ cid, basePath } = queue.shift()) |
| 404 | + |
| 405 | + let paths |
| 406 | + try { |
| 407 | + ({ block, paths } = await getPaths(cid)) |
| 408 | + } catch (error) { |
| 409 | + return reject(error) |
| 410 | + } |
| 411 | + treePaths.push(...paths) |
| 412 | + } |
| 413 | + const treePath = treePaths.shift() |
| 414 | + let fullPath = basePath + treePath |
| 415 | + |
| 416 | + // Only follow links if recursion is intended |
| 417 | + if (options.recursive) { |
| 418 | + cid = await maybeRecurse(block, treePath) |
| 419 | + if (cid !== null) { |
| 420 | + queue.push({ cid, basePath: fullPath + '/' }) |
| 421 | + } |
| 422 | + } |
| 423 | + |
| 424 | + // Return it if it matches the given offset path, but is not the |
| 425 | + // offset path itself |
| 426 | + if (fullPath.startsWith(offsetPath) && |
| 427 | + fullPath.length > offsetPath.length) { |
| 428 | + if (offsetPath.length > 0) { |
| 429 | + fullPath = fullPath.slice(offsetPath.length + 1) |
| 430 | + } |
| 431 | + return resolve({ done: false, value: fullPath }) |
| 432 | + } else { // Else move on to the next iteration before returning |
| 433 | + return resolve(next()) |
| 434 | + } |
| 435 | + }) |
| 436 | + } |
| 437 | + |
| 438 | + return fancyIterator(next) |
| 439 | + } |
| 440 | + |
437 | 441 | /* */
|
438 | 442 | /* internals */
|
439 | 443 | /* */
|
|
0 commit comments