|
1 | 1 | 'use strict'
|
2 | 2 |
|
3 |
| -const util = require('./util') |
4 |
| -const traverse = require('traverse') |
5 | 3 | const CID = require('cids')
|
6 | 4 |
|
7 |
| -exports = module.exports |
8 |
| - |
9 |
| -exports.multicodec = 'dag-cbor' |
10 |
| -exports.defaultHashAlg = 'sha2-256' |
| 5 | +const util = require('./util') |
11 | 6 |
|
12 |
| -/* |
13 |
| - * resolve: receives a path and a binary blob and returns the value on path, |
14 |
| - * throw if not possible. `binaryBlob` is CBOR encoded data. |
| 7 | +/** |
| 8 | + * Resolves a path within a CBOR block. |
| 9 | + * |
| 10 | + * Returns the value or a link and the partial mising path. This way the |
| 11 | + * IPLD Resolver can fetch the link and continue to resolve. |
| 12 | + * |
| 13 | + * @param {Buffer} binaryBlob - Binary representation of a CBOR block |
| 14 | + * @param {string} [path='/'] - Path that should be resolved |
| 15 | + * @returns {Object} result - Result of the path it it was resolved successfully |
| 16 | + * @returns {*} result.value - Value the path resolves to |
| 17 | + * @returns {string} result.remainderPath - If the path resolves half-way to a |
| 18 | + * link, then the `remainderPath` is the part after the link that can be used |
| 19 | + * for further resolving |
15 | 20 | */
|
16 |
| -exports.resolve = (binaryBlob, path, callback) => { |
17 |
| - if (typeof path === 'function') { |
18 |
| - callback = path |
19 |
| - path = undefined |
20 |
| - } |
21 |
| - |
22 |
| - util.deserialize(binaryBlob, (err, node) => { |
23 |
| - if (err) { |
24 |
| - return callback(err) |
| 21 | +exports.resolve = (binaryBlob, path) => { |
| 22 | + let node = util.deserialize(binaryBlob) |
| 23 | + |
| 24 | + const parts = path.split('/').filter(Boolean) |
| 25 | + while (parts.length) { |
| 26 | + const key = parts.shift() |
| 27 | + if (node[key] === undefined) { |
| 28 | + throw new Error(`Object has no property '${key}'`) |
25 | 29 | }
|
26 | 30 |
|
27 |
| - // root |
28 |
| - |
29 |
| - if (!path || path === '/') { |
30 |
| - return callback(null, { |
| 31 | + node = node[key] |
| 32 | + if (CID.isCID(node)) { |
| 33 | + return { |
31 | 34 | value: node,
|
32 |
| - remainderPath: '' |
33 |
| - }) |
34 |
| - } |
35 |
| - |
36 |
| - // within scope |
37 |
| - |
38 |
| - const parts = path.split('/') |
39 |
| - const val = traverse(node).get(parts) |
40 |
| - |
41 |
| - if (val !== undefined) { |
42 |
| - return callback(null, { |
43 |
| - value: val, |
44 |
| - remainderPath: '' |
45 |
| - }) |
46 |
| - } |
47 |
| - |
48 |
| - // out of scope |
49 |
| - let value |
50 |
| - const len = parts.length |
51 |
| - |
52 |
| - for (let i = 0; i < len; i++) { |
53 |
| - const partialPath = parts.shift() |
54 |
| - |
55 |
| - if (Array.isArray(node) && !Buffer.isBuffer(node)) { |
56 |
| - value = node[Number(partialPath)] |
57 |
| - } if (node[partialPath]) { |
58 |
| - value = node[partialPath] |
59 |
| - } else { |
60 |
| - // can't traverse more |
61 |
| - if (!value) { |
62 |
| - return callback(new Error('path not available at root')) |
63 |
| - } else { |
64 |
| - parts.unshift(partialPath) |
65 |
| - return callback(null, { |
66 |
| - value: value, |
67 |
| - remainderPath: parts.join('/') |
68 |
| - }) |
69 |
| - } |
| 35 | + remainderPath: parts.join('/') |
70 | 36 | }
|
71 |
| - node = value |
72 | 37 | }
|
73 |
| - }) |
74 |
| -} |
75 |
| - |
76 |
| -function flattenObject (obj, delimiter) { |
77 |
| - delimiter = delimiter || '/' |
78 |
| - |
79 |
| - if (Object.keys(obj).length === 0) { |
80 |
| - return [] |
81 | 38 | }
|
82 | 39 |
|
83 |
| - return traverse(obj).reduce(function (acc, x) { |
84 |
| - if (CID.isCID(x)) { |
85 |
| - this.update(undefined) |
86 |
| - } |
87 |
| - const path = this.path.join(delimiter) |
88 |
| - |
89 |
| - if (path !== '') { |
90 |
| - acc.push({ path: path, value: x }) |
91 |
| - } |
92 |
| - return acc |
93 |
| - }, []) |
| 40 | + return { |
| 41 | + value: node, |
| 42 | + remainderPath: '' |
| 43 | + } |
94 | 44 | }
|
95 | 45 |
|
96 |
| -/* |
97 |
| - * tree: returns a flattened array with paths: values of the project. options |
98 |
| - * are option (i.e. nestness) |
99 |
| - */ |
100 |
| -exports.tree = (binaryBlob, options, callback) => { |
101 |
| - if (typeof options === 'function') { |
102 |
| - callback = options |
103 |
| - options = undefined |
| 46 | +const traverse = function * (node, path) { |
| 47 | + // Traverse only objects and arrays |
| 48 | + if (Buffer.isBuffer(node) || CID.isCID(node) || typeof node === 'string' || |
| 49 | + node === null) { |
| 50 | + return |
| 51 | + } |
| 52 | + for (const item of Object.keys(node)) { |
| 53 | + const nextpath = path === undefined ? item : path + '/' + item |
| 54 | + yield nextpath |
| 55 | + yield * traverse(node[item], nextpath) |
104 | 56 | }
|
105 |
| - |
106 |
| - options = options || {} |
107 |
| - |
108 |
| - util.deserialize(binaryBlob, (err, node) => { |
109 |
| - if (err) { |
110 |
| - return callback(err) |
111 |
| - } |
112 |
| - const flat = flattenObject(node) |
113 |
| - const paths = flat.map((el) => el.path) |
114 |
| - |
115 |
| - callback(null, paths) |
116 |
| - }) |
117 | 57 | }
|
118 | 58 |
|
119 |
| -exports.isLink = (binaryBlob, path, callback) => { |
120 |
| - exports.resolve(binaryBlob, path, (err, result) => { |
121 |
| - if (err) { |
122 |
| - return callback(err) |
123 |
| - } |
124 |
| - |
125 |
| - if (result.remainderPath.length > 0) { |
126 |
| - return callback(new Error('path out of scope')) |
127 |
| - } |
| 59 | +/** |
| 60 | + * Return all available paths of a block. |
| 61 | + * |
| 62 | + * @generator |
| 63 | + * @param {Buffer} binaryBlob - Binary representation of a CBOR block |
| 64 | + * @yields {string} - A single path |
| 65 | + */ |
| 66 | +exports.tree = function * (binaryBlob) { |
| 67 | + const node = util.deserialize(binaryBlob) |
128 | 68 |
|
129 |
| - if (CID.isCID(result.value)) { |
130 |
| - callback(null, result.value) |
131 |
| - } else { |
132 |
| - callback(null, false) |
133 |
| - } |
134 |
| - }) |
| 69 | + yield * traverse(node) |
135 | 70 | }
|
0 commit comments