Skip to content
This repository was archived by the owner on Aug 11, 2021. It is now read-only.

Commit cfc8519

Browse files
committed
feat: new IPLD Format API
BREAKING CHANGE: The API is now async/await based There are numerous changes, the most significant one is that the API is no longer callback based, but it using async/await. For the full new API please see the [IPLD Formats spec]. [IPLD Formats spec]: https://github.com/ipld/interface-ipld-format
1 parent ba28199 commit cfc8519

File tree

9 files changed

+306
-543
lines changed

9 files changed

+306
-543
lines changed

README.md

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,42 +72,34 @@ const file = {
7272
size: 11
7373
}
7474

75-
dagCBOR.util.serialize(file, (err, serialized) => {
76-
if (err) {
77-
throw err
78-
}
75+
const serialized = dagCBOR.util.serialize(file)
76+
console.log(`Encoded as a ${serialized.length} byte Buffer`)
7977

80-
console.log(`Encoded as a ${serialized.length} byte Buffer`)
81-
82-
dagCBOR.util.deserialize(serialized, (err, node) => {
83-
if (err) {
84-
throw err
85-
}
86-
87-
console.log('Decoded as:', node)
88-
require('assert').deepEqual(node, file) // should match
89-
})
90-
})
78+
const node = dagCBOR.util.deserialize(serialized)
79+
console.log('Decoded as:', node)
80+
require('assert').deepEqual(node, file) // should match
9181

9282
// → Encoded as a 22 byte Buffer
9383
// → Decoded as: { name: 'hello.txt', size: 11 }
9484
```
9585

9686
## API
9787

98-
### `dagCBOR.util.serialize(obj, callback)`
88+
### `dagCBOR.util.serialize(obj)`
9989

10090
Encodes an object into IPLD CBOR form, replacing any CIDs found within the object to CBOR tags (with an id of `42`).
10191

10292
- `obj` (any): any object able to be serialized as CBOR
103-
- `callback` (`Function`): function to be called when serialization is complete, arguments are: `error` and the serialized node if no error was encountered.
10493

105-
### `dagCBOR.util.deserialize(serialized, callback)`
94+
Returns the serialized node.
95+
96+
### `dagCBOR.util.deserialize(serialized)`
10697

10798
Decodes an IPLD CBOR encoded representation, restoring any CBOR tags (id `42`) to CIDs.
10899

109100
- `serialized` (`Buffer` or `String`): a binary blob representing an IPLD CBOR encoded object.
110-
- `callback` (`Function`): function to be called when deserialization is complete, arguments are: `error` and the deserialized object if no error was encountered.
101+
102+
Returns the deserialized object.
111103

112104
### `dagCBOR.util.configureDecoder([options])`
113105

@@ -123,7 +115,7 @@ Possible values in the `options` argument are:
123115

124116
Calling `dagCBOR.util.configureDecoder()` with no arguments will reset to the default decoder `size`, `maxSize` and `tags`.
125117

126-
### `dagCBOR.util.cid(obj[, options,] callback)`
118+
### `dagCBOR.util.cid(obj[, options,])`
127119

128120
Create a [CID](https://github.com/multiformats/js-cid) for the given unserialized object.
129121

@@ -132,7 +124,8 @@ Create a [CID](https://github.com/multiformats/js-cid) for the given unserialize
132124
* `hashAlg` (`String`): a [registered multicodec](https://github.com/multiformats/multicodec/blob/master/table.csv) hash algorithm.
133125
* `hashLen` (`String`): an optional hash length
134126
* `version` (`Number`): CID version number, defaults to `1`
135-
- `callback` (`Function`): function to be called when the object is serialized and a CID is created from that serialized form, arguments are: `error` and the created CID if no error was encountered.
127+
128+
Returns a Promise with the created CID.
136129

137130
## Contribute
138131

package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,11 @@
4040
"borc": "^2.1.0",
4141
"cids": "~0.6.0",
4242
"is-circular": "^1.0.2",
43-
"multihashing-async": "~0.6.0",
44-
"traverse": "~0.6.6"
43+
"multicodec": "~0.5.0",
44+
"multihashing-async": "~0.7.0"
4545
},
4646
"devDependencies": {
4747
"aegir": "^18.2.0",
48-
"async": "^2.6.2",
49-
"bs58": "^4.0.1",
5048
"chai": "^4.2.0",
5149
"detect-node": "^2.0.4",
5250
"dirty-chai": "^2.0.1",

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22

33
exports.util = require('./util.js')
44
exports.resolver = require('./resolver.js')
5+
exports.codec = exports.util.codec
6+
exports.defaultHashAlg = exports.util.defaultHashAlg

src/resolver.js

Lines changed: 50 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,70 @@
11
'use strict'
22

3-
const util = require('./util')
4-
const traverse = require('traverse')
53
const CID = require('cids')
64

7-
exports = module.exports
8-
9-
exports.multicodec = 'dag-cbor'
10-
exports.defaultHashAlg = 'sha2-256'
5+
const util = require('./util')
116

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
1520
*/
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}'`)
2529
}
2630

27-
// root
28-
29-
if (!path || path === '/') {
30-
return callback(null, {
31+
node = node[key]
32+
if (CID.isCID(node)) {
33+
return {
3134
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('/')
7036
}
71-
node = value
7237
}
73-
})
74-
}
75-
76-
function flattenObject (obj, delimiter) {
77-
delimiter = delimiter || '/'
78-
79-
if (Object.keys(obj).length === 0) {
80-
return []
8138
}
8239

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+
}
9444
}
9545

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)
10456
}
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-
})
11757
}
11858

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)
12868

129-
if (CID.isCID(result.value)) {
130-
callback(null, result.value)
131-
} else {
132-
callback(null, false)
133-
}
134-
})
69+
yield * traverse(node)
13570
}

0 commit comments

Comments
 (0)