├── .gitattributes
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── graphs
├── arch.monopic
└── arch.txt
├── package.json
├── src
├── dag-link
│ ├── dagLink.js
│ ├── index.js
│ └── util.js
├── dag-node
│ ├── addLink.js
│ ├── dagNode.js
│ ├── index.js
│ ├── rmLink.js
│ ├── sortLinks.js
│ └── toDagLink.js
├── dag.d.ts
├── dag.js
├── dag.proto
├── genCid.js
├── index.js
├── resolver.js
├── serialize.js
├── types.d.ts
└── util.js
├── test
├── dag-link-test.spec.js
├── dag-node-test.spec.js
├── fixtures
│ ├── test-block-named-links
│ └── test-block-unnamed-links
├── mod.spec.js
├── resolver.spec.js
└── util.spec.js
├── tools
└── pb-cross-language.go
└── tsconfig.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | test/fixtures/** text eol=lf
3 | test/test-repo/** text eol=lf
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | docs
2 | **/node_modules/
3 | **/*.log
4 | test/repo-tests*
5 |
6 | # Logs
7 | logs
8 | *.log
9 |
10 | coverage
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 | .nyc_output
23 |
24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25 | .grunt
26 |
27 | # node-waf configuration
28 | .lock-wscript
29 |
30 | build
31 |
32 | # Dependency directory
33 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
34 | node_modules
35 |
36 | dist
37 |
38 | test/repo-just-for-test*
39 | /test/blocks
40 |
41 | # Vim .swp files
42 | **.swp
43 |
44 | package-lock.json
45 | yarn.lock
46 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache: npm
3 | branches:
4 | only:
5 | - master
6 | - /^release\/.*$/
7 | stages:
8 | - check
9 | - test
10 | - cov
11 |
12 | node_js:
13 | - 'lts/*'
14 | - 'node'
15 |
16 | os:
17 | - linux
18 | - osx
19 | - windows
20 |
21 | script: npx nyc -s npm run test:node -- --bail
22 | after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
23 |
24 | jobs:
25 | include:
26 | - stage: check
27 | script:
28 | - npx aegir dep-check
29 | - npm run lint
30 |
31 | - stage: test
32 | name: chrome
33 | addons:
34 | chrome: stable
35 | script: npx aegir test -t browser -t webworker
36 |
37 | - stage: test
38 | name: firefox
39 | addons:
40 | firefox: latest
41 | script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
42 |
43 | notifications:
44 | email: false
45 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.22.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.22.2...v0.22.3) (2021-08-11)
2 |
3 |
4 |
5 | ## [0.22.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.22.1...v0.22.2) (2021-04-07)
6 |
7 |
8 |
9 | ## [0.22.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.22.0...v0.22.1) (2021-03-12)
10 |
11 |
12 | ### Bug Fixes
13 |
14 | * types again ([e103337](https://github.com/ipld/js-ipld-dag-pb/commit/e1033374cbccb00970220b189ea7c000a8ea3254))
15 |
16 |
17 |
18 | # [0.22.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.21.1...v0.22.0) (2021-03-11)
19 |
20 |
21 | ### Bug Fixes
22 |
23 | * test types ([8dc8301](https://github.com/ipld/js-ipld-dag-pb/commit/8dc8301b25e41ddab87f491c88608952e9f89ccd))
24 |
25 |
26 |
27 | ## [0.21.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.21.0...v0.21.1) (2021-03-03)
28 |
29 |
30 |
31 | # [0.21.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.20.0...v0.21.0) (2021-02-18)
32 |
33 |
34 | ### Features
35 |
36 | * add TypeScript types ([#189](https://github.com/ipld/js-ipld-dag-pb/issues/189)) ([76cfef8](https://github.com/ipld/js-ipld-dag-pb/commit/76cfef856aab2ce82c1e855f152523460e9b6bcb))
37 |
38 |
39 | ### BREAKING CHANGES
40 |
41 | * `is-class` is not longer used, use `instanceof` instead
42 |
43 | The `DAGNode.isDAGNode()` and `DAGLink.isDAGLink()` methods no longer exist,
44 | use `instanceof DAGNode` and `instanceof DAGLink` instead.
45 |
46 | Please note that the newly added TypeScript types might lead to warnings/errors
47 | if you use it in a TypeScript types checking environment.
48 |
49 |
50 |
51 |
52 | # [0.20.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.19.0...v0.20.0) (2020-08-04)
53 |
54 |
55 | ### Bug Fixes
56 |
57 | * replace node Buffers with Uint8Arrays ([bef3f26](https://github.com/ipld/js-ipld-dag-pb/commit/bef3f26))
58 |
59 |
60 | ### BREAKING CHANGES
61 |
62 | * - `dagNode.Data` can now be a `Uint8Array`
63 |
64 |
65 |
66 |
67 | # [0.19.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.18.5...v0.19.0) (2020-07-09)
68 |
69 |
70 | ### Bug Fixes
71 |
72 | * immutable CID test ([d82441c](https://github.com/ipld/js-ipld-dag-pb/commit/d82441c))
73 | * lack of TextEncoder in older nodejs ([afd52fe](https://github.com/ipld/js-ipld-dag-pb/commit/afd52fe))
74 |
75 |
76 | ### Features
77 |
78 | * **package:** backwards compatible pure data model API ([0f11d5c](https://github.com/ipld/js-ipld-dag-pb/commit/0f11d5c))
79 |
80 |
81 |
82 |
83 | ## [0.18.5](https://github.com/ipld/js-ipld-dag-pb/compare/v0.18.4...v0.18.5) (2020-04-24)
84 |
85 |
86 | ### Bug Fixes
87 |
88 | * revert stable module removal ([8640b22](https://github.com/ipld/js-ipld-dag-pb/commit/8640b22))
89 |
90 |
91 |
92 |
93 | ## [0.18.4](https://github.com/ipld/js-ipld-dag-pb/compare/v0.18.3...v0.18.4) (2020-04-21)
94 |
95 |
96 | ### Bug Fixes
97 |
98 | * cleanup and remove deps ([886b06d](https://github.com/ipld/js-ipld-dag-pb/commit/886b06d))
99 | * encode with multibase ([f709ec9](https://github.com/ipld/js-ipld-dag-pb/commit/f709ec9))
100 | * native sort is stable now ([3048e3e](https://github.com/ipld/js-ipld-dag-pb/commit/3048e3e))
101 | * remove node globals ([98d9ac6](https://github.com/ipld/js-ipld-dag-pb/commit/98d9ac6))
102 | * **package:** update cids to version 0.8.0 ([aa2709b](https://github.com/ipld/js-ipld-dag-pb/commit/aa2709b))
103 |
104 |
105 |
106 |
107 | ## [0.18.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.18.2...v0.18.3) (2020-03-12)
108 |
109 |
110 | ### Bug Fixes
111 |
112 | * remove use of assert module ([497850d](https://github.com/ipld/js-ipld-dag-pb/commit/497850d))
113 |
114 |
115 |
116 |
117 | ## [0.18.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.18.1...v0.18.2) (2020-01-13)
118 |
119 |
120 | ### Bug Fixes
121 |
122 | * **package:** update multicodec to version 1.0.0 ([f0fee49](https://github.com/ipld/js-ipld-dag-pb/commit/f0fee49))
123 | * **package:** update multihashing-async to version 0.8.0 ([244665a](https://github.com/ipld/js-ipld-dag-pb/commit/244665a))
124 |
125 |
126 |
127 |
128 | ## [0.18.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.18.0...v0.18.1) (2019-08-19)
129 |
130 |
131 | ### Bug Fixes
132 |
133 | * serialization and size portability problems ([f348cb8](https://github.com/ipld/js-ipld-dag-pb/commit/f348cb8))
134 |
135 |
136 |
137 |
138 | # [0.18.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.17.4...v0.18.0) (2019-07-29)
139 |
140 |
141 | ### Bug Fixes
142 |
143 | * make `addLink()` synchronous ([7f1a00a](https://github.com/ipld/js-ipld-dag-pb/commit/7f1a00a)), closes [#128](https://github.com/ipld/js-ipld-dag-pb/issues/128)
144 |
145 |
146 | ### Features
147 |
148 | * make addLink()/rmLink() instance methods ([a9aa0a0](https://github.com/ipld/js-ipld-dag-pb/commit/a9aa0a0))
149 | * remove cloning ([d5e1135](https://github.com/ipld/js-ipld-dag-pb/commit/d5e1135))
150 | * remove DAGNode.create() ([029174d](https://github.com/ipld/js-ipld-dag-pb/commit/029174d)), closes [#132](https://github.com/ipld/js-ipld-dag-pb/issues/132)
151 |
152 |
153 | ### Performance Improvements
154 |
155 | * remove manual enumerability modifications ([37ffdd5](https://github.com/ipld/js-ipld-dag-pb/commit/37ffdd5)), closes [#152](https://github.com/ipld/js-ipld-dag-pb/issues/152)
156 | * remove named links from object ([4dbe00d](https://github.com/ipld/js-ipld-dag-pb/commit/4dbe00d))
157 |
158 |
159 | ### BREAKING CHANGES
160 |
161 | * `addLink()` and `rmLink()` are now instance methods.
162 |
163 | Prior to this change:
164 |
165 | DAGNode.addLink(node, link)
166 | DAGNode.rmLink(node, name)
167 |
168 | Now:
169 |
170 | node.addLink(link)
171 | node.rmLink(name)
172 | * It's no longer possible to pass a `DAGNode` into `addLink()`.
173 |
174 | Intead of passing in a `DAGNode` into `addLink()`, convert that node into
175 | a `DAGLink` via `toDAGLink()`.
176 |
177 | Example:
178 |
179 | Prior to this change:
180 |
181 | const node = new DAGNode('some data')
182 | const node2 = new DAGNode('use that as link')
183 | await DAGNode.addLink(node, node2)
184 |
185 | Now:
186 |
187 | const node = new DAGNode('some data')
188 | const node2 = new DAGNode('use that as link')
189 | DAGNode.addLink(node, await node2.toDAGLink())
190 | * DAGNode.create() is removed
191 |
192 | Instead of `DAGNode.create()`, please use `new DAGNode()` instead. It
193 | takes the same parameters and is compatible to `create()`.
194 |
195 | Example:
196 |
197 | Prior to this change:
198 |
199 | const node = DAGNode.create('some data', links)
200 |
201 | Now:
202 |
203 | const node = new DAGNode('some data', links)
204 | * `DAGNode.clone()` is removed from public API without any replacement.
205 |
206 | Also the API for `rmLink()` and `addLink()` changed. They no longer
207 | return a new node, but just remove/add the links to/from the current
208 | node.
209 |
210 | Prior to this change:
211 |
212 | const lessLinks = DAGNode.rmLink(node1, 'Link1')
213 | node1 = lessLinks
214 | const moreLinks = await DAGNode.addLink(node2, link)
215 | node2 = moreLinks
216 |
217 | Now:
218 |
219 | DAGNode.rmLink(node, 'Link1')
220 | await DAGNode.addLink(node2, link)
221 | * named links are no longer part of an object
222 |
223 | Access to named links is only possible with calling `resolve()`.
224 | Hence they are also not part of `tree()` anymore.
225 |
226 | Named links are a feature of IPFS and only supported for
227 | backwards compatibility, they are not really part of IPLD.
228 |
229 |
230 |
231 |
232 | ## [0.17.4](https://github.com/ipld/js-ipld-dag-pb/compare/v0.17.3...v0.17.4) (2019-05-22)
233 |
234 |
235 | ### Bug Fixes
236 |
237 | * actually use object keys ([a411eeb](https://github.com/ipld/js-ipld-dag-pb/commit/a411eeb))
238 |
239 |
240 |
241 |
242 | ## [0.17.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.17.2...v0.17.3) (2019-05-20)
243 |
244 |
245 | ### Bug Fixes
246 |
247 | * named links should return the CID ([ee96d28](https://github.com/ipld/js-ipld-dag-pb/commit/ee96d28))
248 |
249 |
250 |
251 |
252 | ## [0.17.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.17.1...v0.17.2) (2019-05-20)
253 |
254 |
255 | ### Bug Fixes
256 |
257 | * support .Size property ([30b5d55](https://github.com/ipld/js-ipld-dag-pb/commit/30b5d55))
258 |
259 |
260 |
261 |
262 | ## [0.17.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.17.0...v0.17.1) (2019-05-17)
263 |
264 |
265 | ### Bug Fixes
266 |
267 | * allow adding links from DAGNode.Links ([a5d300f](https://github.com/ipld/js-ipld-dag-pb/commit/a5d300f))
268 |
269 |
270 |
271 |
272 | # [0.17.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.16.0...v0.17.0) (2019-05-10)
273 |
274 |
275 | ### Bug Fixes
276 |
277 | * **package:** update cids to version 0.7.0 ([2afca2c](https://github.com/ipld/js-ipld-dag-pb/commit/2afca2c))
278 |
279 |
280 | ### BREAKING CHANGES
281 |
282 | * **package:** Returned v1 CIDs now default to base32 encoding
283 |
284 | Previous versions returned a base58 encoded string when `toString()`/
285 | `toBaseEncodedString()` was called on a CIDv1. It now returns a base32
286 | encoded string.
287 |
288 |
289 |
290 |
291 | # [0.16.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.15.3...v0.16.0) (2019-05-08)
292 |
293 |
294 | ### Bug Fixes
295 |
296 | * **package:** update multihashing-async to version 0.6.0 ([63b7986](https://github.com/ipld/js-ipld-dag-pb/commit/63b7986))
297 |
298 |
299 | ### Features
300 |
301 | * new IPLD Format API ([1de1bcc](https://github.com/ipld/js-ipld-dag-pb/commit/1de1bcc))
302 |
303 |
304 | ### BREAKING CHANGES
305 |
306 | * The API is now async/await based
307 |
308 | There are numerous changes, the most significant one is that the API
309 | is no longer callback based, but it using async/await.
310 |
311 | The properties of DAGNode and DAGLink are now in sync with the paths
312 | that are used for resolving. This means that e.g. `name` is now called
313 | `Name` and `size` is `Tsize`.
314 |
315 | All return values from `resolve()` now conform to the [IPLD Data Model],
316 | this means that e.g. links are no longer represented as
317 | `{'/': "baseecodedcid"}`, but as [CID] instances instead.
318 |
319 | For the full new API please see the [IPLD Formats spec].
320 |
321 | [IPLD Data Model]: https://github.com/ipld/specs/blob/master/IPLD-Data-Model-v1.md
322 | [CID]: https://github.com/multiformats/js-cid/
323 | [IPLD Formats spec]: https://github.com/ipld/interface-ipld-format
324 |
325 |
326 |
327 |
328 | ## [0.15.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.15.2...v0.15.3) (2019-03-13)
329 |
330 |
331 | ### Bug Fixes
332 |
333 | * add a return to callback ([eb73e70](https://github.com/ipld/js-ipld-dag-pb/commit/eb73e70))
334 | * **package:** update is-ipfs to version 0.6.0 ([0935e53](https://github.com/ipld/js-ipld-dag-pb/commit/0935e53))
335 |
336 |
337 |
338 |
339 | ## [0.15.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.15.1...v0.15.2) (2018-12-17)
340 |
341 |
342 | ### Performance Improvements
343 |
344 | * memoize the Name buffer in DAGLink to avoid unneeded allocations ([83edf36](https://github.com/ipld/js-ipld-dag-pb/commit/83edf36))
345 |
346 |
347 |
348 |
349 | ## [0.15.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.15.0...v0.15.1) (2018-12-11)
350 |
351 |
352 | ### Performance Improvements
353 |
354 | * remove unneeded Buffer.from() call during deserialize ([4632596](https://github.com/ipld/js-ipld-dag-pb/commit/4632596))
355 |
356 |
357 |
358 |
359 | # [0.15.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.11...v0.15.0) (2018-11-09)
360 |
361 |
362 | ### Performance Improvements
363 |
364 | * fixes [#97](https://github.com/ipld/js-ipld-dag-pb/issues/97) by not sorting DAGNode links unnecessarily ([e5d5d34](https://github.com/ipld/js-ipld-dag-pb/commit/e5d5d34))
365 |
366 |
367 | * BREAKING CHANGE: Remove .cid, .multihash and .serialized properties (#99) ([39cfef1](https://github.com/ipld/js-ipld-dag-pb/commit/39cfef1)), closes [#99](https://github.com/ipld/js-ipld-dag-pb/issues/99) [/github.com/ipld/js-ipld/issues/173#issuecomment-434408680](https://github.com//github.com/ipld/js-ipld/issues/173/issues/issuecomment-434408680)
368 |
369 |
370 | ### BREAKING CHANGES
371 |
372 | * These properties are removed from the DAGNode class.
373 |
374 | * `.multihash` is removed because they aren't multihashes any more
375 | * `.cid` is removed to bring dag-pb in line with other ipld types
376 | * `.serialized` is removed because storing data buffers and the
377 | serialized form uses too much memory - we can use the utils.serialize
378 | method to create the serialized form when we need it, which in this
379 | module is just during the tests
380 |
381 | `.multihash` has also changed to `.cid` in the output of
382 | `DAGLink.toJSON` and `DAGNode.toJSON` because since CIDv1 they are
383 | not just multihashes any more; the multihash is contained within
384 | the CID.
385 |
386 |
387 |
388 |
389 | ## [0.14.11](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.10...v0.14.11) (2018-10-26)
390 |
391 |
392 | ### Features
393 |
394 | * expose cid property on DAGLinks and DAGNodes ([af3b44d](https://github.com/ipld/js-ipld-dag-pb/commit/af3b44d)), closes [/github.com/ipld/js-ipld-dag-pb/pull/81#issuecomment-410681495](https://github.com//github.com/ipld/js-ipld-dag-pb/pull/81/issues/issuecomment-410681495)
395 |
396 |
397 |
398 |
399 | ## [0.14.10](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.9...v0.14.10) (2018-09-24)
400 |
401 |
402 |
403 |
404 | ## [0.14.9](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.8...v0.14.9) (2018-09-24)
405 |
406 |
407 | ### Bug Fixes
408 |
409 | * resolve link Name or Tsize ([981cb9f](https://github.com/ipld/js-ipld-dag-pb/commit/981cb9f)), closes [#85](https://github.com/ipld/js-ipld-dag-pb/issues/85)
410 |
411 |
412 |
413 |
414 | ## [0.14.8](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.7...v0.14.8) (2018-08-13)
415 |
416 |
417 | ### Bug Fixes
418 |
419 | * allow mutating returned .toJSON value ([d8239ad](https://github.com/ipld/js-ipld-dag-pb/commit/d8239ad)), closes [ipld/js-ipld-dag-pb#81](https://github.com/ipld/js-ipld-dag-pb/issues/81)
420 |
421 |
422 |
423 |
424 | ## [0.14.7](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.6...v0.14.7) (2018-08-13)
425 |
426 |
427 | ### Bug Fixes
428 |
429 | * support cids in DagLink ([4c701aa](https://github.com/ipld/js-ipld-dag-pb/commit/4c701aa))
430 |
431 |
432 | ### Performance Improvements
433 |
434 | * make this._json calculation lazy ([d138c95](https://github.com/ipld/js-ipld-dag-pb/commit/d138c95))
435 |
436 |
437 |
438 |
439 | ## [0.14.6](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.5...v0.14.6) (2018-07-20)
440 |
441 |
442 | ### Bug Fixes
443 |
444 | * add support for resolving links by name ([#78](https://github.com/ipld/js-ipld-dag-pb/issues/78)) ([3f6f094](https://github.com/ipld/js-ipld-dag-pb/commit/3f6f094))
445 |
446 |
447 |
448 |
449 | ## [0.14.5](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.4...v0.14.5) (2018-06-29)
450 |
451 |
452 | ### Bug Fixes
453 |
454 | * pass serialized blob to util.cid ([#75](https://github.com/ipld/js-ipld-dag-pb/issues/75)) ([2ae9542](https://github.com/ipld/js-ipld-dag-pb/commit/2ae9542))
455 | * unlock and update stable dep ([40a5e65](https://github.com/ipld/js-ipld-dag-pb/commit/40a5e65))
456 |
457 |
458 | ### Features
459 |
460 | * add defaultHashAlg ([424d2a1](https://github.com/ipld/js-ipld-dag-pb/commit/424d2a1))
461 | * add util.cid options ([#74](https://github.com/ipld/js-ipld-dag-pb/issues/74)) ([1d89fa7](https://github.com/ipld/js-ipld-dag-pb/commit/1d89fa7))
462 |
463 |
464 | ### BREAKING CHANGES
465 |
466 | * the first argument is now the serialized output NOT the dag node.
467 | See https://github.com/ipld/interface-ipld-format/issues/32
468 |
469 |
470 |
471 |
472 | ## [0.14.4](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.3...v0.14.4) (2018-04-25)
473 |
474 |
475 | ### Bug Fixes
476 |
477 | * Initialise the DAGLink name to empty string if a falsey value is passed ([575a03f](https://github.com/ipld/js-ipld-dag-pb/commit/575a03f))
478 |
479 |
480 |
481 |
482 | ## [0.14.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.2...v0.14.3) (2018-04-16)
483 |
484 |
485 | ### Bug Fixes
486 |
487 | * lock stable dep ([4174338](https://github.com/ipld/js-ipld-dag-pb/commit/4174338)), closes [#65](https://github.com/ipld/js-ipld-dag-pb/issues/65)
488 |
489 |
490 |
491 |
492 | ## [0.14.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.1...v0.14.2) (2018-04-11)
493 |
494 |
495 |
496 |
497 | ## [0.14.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.14.0...v0.14.1) (2018-04-10)
498 |
499 |
500 |
501 |
502 | # [0.14.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.13.1...v0.14.0) (2018-04-09)
503 |
504 |
505 | ### Bug Fixes
506 |
507 | * replace constructor.name with instanceof ([881d572](https://github.com/ipld/js-ipld-dag-pb/commit/881d572))
508 |
509 |
510 | ### Features
511 |
512 | * use class-is module for type checks ([621c12c](https://github.com/ipld/js-ipld-dag-pb/commit/621c12c))
513 |
514 |
515 |
516 |
517 | ## [0.13.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.13.0...v0.13.1) (2018-02-26)
518 |
519 |
520 | ### Bug Fixes
521 |
522 | * return deserialized node if no path is given ([bcba192](https://github.com/ipld/js-ipld-dag-pb/commit/bcba192))
523 |
524 |
525 |
526 |
527 | # [0.13.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.12.0...v0.13.0) (2018-02-12)
528 |
529 |
530 |
531 |
532 | # [0.12.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.11.4...v0.12.0) (2018-02-12)
533 |
534 |
535 | ### Bug Fixes
536 |
537 | * Fix false positives in isLink and update test ([#51](https://github.com/ipld/js-ipld-dag-pb/issues/51)) ([73b21c7](https://github.com/ipld/js-ipld-dag-pb/commit/73b21c7))
538 | * use binary blobs directly ([50edc45](https://github.com/ipld/js-ipld-dag-pb/commit/50edc45))
539 |
540 |
541 | ### BREAKING CHANGES
542 |
543 | * Everyone calling the functions of `resolve` need to
544 | pass in the binary data instead of an IPFS block.
545 |
546 | So if your input is an IPFS block, the code changes from
547 |
548 | resolver.resolve(block, path, (err, result) => {…}
549 |
550 | to
551 |
552 | resolver.resolve(block.data, path, (err, result) => {…}
553 |
554 |
555 |
556 |
557 | ## [0.11.4](https://github.com/ipld/js-ipld-dag-pb/compare/v0.11.3...v0.11.4) (2017-12-02)
558 |
559 |
560 | ### Features
561 |
562 | * If link hashes to serializer are base58 encoded, then decode them [Fi… ([#43](https://github.com/ipld/js-ipld-dag-pb/issues/43)) ([9f66bca](https://github.com/ipld/js-ipld-dag-pb/commit/9f66bca)), closes [#28](https://github.com/ipld/js-ipld-dag-pb/issues/28)
563 |
564 |
565 |
566 |
567 | ## [0.11.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.11.2...v0.11.3) (2017-11-07)
568 |
569 |
570 |
571 |
572 | ## [0.11.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.11.0...v0.11.2) (2017-09-07)
573 |
574 |
575 | ### Bug Fixes
576 |
577 | * **package:** update cids to version 0.5.0 ([b1d4b0f](https://github.com/ipld/js-ipld-dag-pb/commit/b1d4b0f))
578 | * switch to protobufjs ([#39](https://github.com/ipld/js-ipld-dag-pb/issues/39)) ([5130844](https://github.com/ipld/js-ipld-dag-pb/commit/5130844))
579 |
580 |
581 | ### Features
582 |
583 | * replace protocol-buffers with protons ([#42](https://github.com/ipld/js-ipld-dag-pb/issues/42)) ([603e11f](https://github.com/ipld/js-ipld-dag-pb/commit/603e11f))
584 |
585 |
586 |
587 |
588 | ## [0.11.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.11.0...v0.11.1) (2017-09-05)
589 |
590 |
591 | ### Bug Fixes
592 |
593 | * **package:** update cids to version 0.5.0 ([b1d4b0f](https://github.com/ipld/js-ipld-dag-pb/commit/b1d4b0f))
594 | * switch to protobufjs ([#39](https://github.com/ipld/js-ipld-dag-pb/issues/39)) ([5130844](https://github.com/ipld/js-ipld-dag-pb/commit/5130844))
595 |
596 |
597 |
598 |
599 | # [0.11.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.10.1...v0.11.0) (2017-03-21)
600 |
601 |
602 | ### Features
603 |
604 | * upgrade to new ipld-block and blockservice ([1dd4dd2](https://github.com/ipld/js-ipld-dag-pb/commit/1dd4dd2))
605 |
606 |
607 |
608 |
609 | ## [0.10.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.10.0...v0.10.1) (2017-03-16)
610 |
611 |
612 |
613 |
614 | # [0.10.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.9.5...v0.10.0) (2017-03-13)
615 |
616 |
617 | ### Bug Fixes
618 |
619 | * **deps:** move bs58 to regular dependecies ([e69c9bc](https://github.com/ipld/js-ipld-dag-pb/commit/e69c9bc))
620 | * remove starting slash ([ad47ffa](https://github.com/ipld/js-ipld-dag-pb/commit/ad47ffa))
621 |
622 |
623 | ### Features
624 |
625 | * change window to self for webworker support ([a68d50e](https://github.com/ipld/js-ipld-dag-pb/commit/a68d50e))
626 | * **ww:** Full support for webworkers ([e073e08](https://github.com/ipld/js-ipld-dag-pb/commit/e073e08))
627 | * update isCID to isLink by spec ([df759c8](https://github.com/ipld/js-ipld-dag-pb/commit/df759c8))
628 |
629 |
630 |
631 |
632 | ## [0.9.5](https://github.com/ipld/js-ipld-dag-pb/compare/v0.9.4...v0.9.5) (2017-02-09)
633 |
634 |
635 | ### Bug Fixes
636 |
637 | * **package:** update is-ipfs to version 0.3.0 ([2620f9d](https://github.com/ipld/js-ipld-dag-pb/commit/2620f9d))
638 |
639 |
640 |
641 |
642 | ## [0.9.4](https://github.com/ipld/js-ipld-dag-pb/compare/v0.9.3...v0.9.4) (2017-01-29)
643 |
644 |
645 |
646 |
647 | ## [0.9.3](https://github.com/ipld/js-ipld-dag-pb/compare/v0.9.2...v0.9.3) (2016-12-07)
648 |
649 |
650 | ### Bug Fixes
651 |
652 | * detect when unvalid dagPB node ([068b1e2](https://github.com/ipld/js-ipld-dag-pb/commit/068b1e2))
653 |
654 |
655 |
656 |
657 | ## [0.9.2](https://github.com/ipld/js-ipld-dag-pb/compare/v0.9.1...v0.9.2) (2016-12-01)
658 |
659 |
660 |
661 |
662 | ## [0.9.1](https://github.com/ipld/js-ipld-dag-pb/compare/v0.9.0...v0.9.1) (2016-11-24)
663 |
664 |
665 | ### Bug Fixes
666 |
667 | * ensure empty link names are preserved ([ad11b7a](https://github.com/ipld/js-ipld-dag-pb/commit/ad11b7a))
668 | * fixtures loading in the browser ([405dd01](https://github.com/ipld/js-ipld-dag-pb/commit/405dd01))
669 | * linting ([d51245c](https://github.com/ipld/js-ipld-dag-pb/commit/d51245c))
670 | * sort links in creation ([8519b3b](https://github.com/ipld/js-ipld-dag-pb/commit/8519b3b))
671 | * use aegir fixtures instead ([c492997](https://github.com/ipld/js-ipld-dag-pb/commit/c492997))
672 |
673 |
674 |
675 |
676 | # [0.9.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.8.0...v0.9.0) (2016-11-24)
677 |
678 |
679 | ### Bug Fixes
680 |
681 | * apply code review ([2b1a356](https://github.com/ipld/js-ipld-dag-pb/commit/2b1a356))
682 | * DAGLink tests ([eec00da](https://github.com/ipld/js-ipld-dag-pb/commit/eec00da))
683 | * serialization of empty node ([ae71f26](https://github.com/ipld/js-ipld-dag-pb/commit/ae71f26))
684 | * that linting ([301d0b0](https://github.com/ipld/js-ipld-dag-pb/commit/301d0b0))
685 |
686 |
687 | ### Features
688 |
689 | * **refactor:** new API proposal ([b56d797](https://github.com/ipld/js-ipld-dag-pb/commit/b56d797))
690 | * add a same create pattern api for DAGLink ([6a3531d](https://github.com/ipld/js-ipld-dag-pb/commit/6a3531d))
691 | * IPLD Resolver updated, all tests passing ([f53e0c8](https://github.com/ipld/js-ipld-dag-pb/commit/f53e0c8))
692 | * refactor, structure code, make DAGNode funcs inside the same folder, make tests pass again ([ea904a7](https://github.com/ipld/js-ipld-dag-pb/commit/ea904a7))
693 | * update DAGLink and DAGNode to have an immutable API ([4bdb48b](https://github.com/ipld/js-ipld-dag-pb/commit/4bdb48b))
694 |
695 |
696 |
697 |
698 | # [0.8.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.1.3...v0.8.0) (2016-11-03)
699 |
700 |
701 |
702 |
703 | # [0.3.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.1.3...v0.3.0) (2016-11-03)
704 |
705 |
706 |
707 |
708 | # [0.2.0](https://github.com/ipld/js-ipld-dag-pb/compare/v0.1.3...v0.2.0) (2016-11-03)
709 |
710 |
711 |
712 |
713 | ## [0.1.3](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.1.2...v0.1.3) (2016-10-27)
714 |
715 |
716 | ### Bug Fixes
717 |
718 | * toJSON should return multihash as a b58String ([3c34563](https://github.com/ipfs/js-ipfs-merkle-dag/commit/3c34563))
719 |
720 |
721 |
722 |
723 | ## [0.1.2](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.1.1...v0.1.2) (2016-10-26)
724 |
725 |
726 | ### Bug Fixes
727 |
728 | * do not call callbacks inside try catch blocks ([9cc237e](https://github.com/ipfs/js-ipfs-merkle-dag/commit/9cc237e))
729 |
730 |
731 |
732 |
733 | ## [0.1.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.1.0...v0.1.1) (2016-10-26)
734 |
735 |
736 | ### Bug Fixes
737 |
738 | * size func ([655a964](https://github.com/ipfs/js-ipfs-merkle-dag/commit/655a964))
739 |
740 |
741 |
742 |
743 | # 0.1.0 (2016-10-26)
744 |
745 |
746 | ### Bug Fixes
747 |
748 | * **dag-node:** ensure links are always DAGLinks ([219cf5d](https://github.com/ipfs/js-ipfs-merkle-dag/commit/219cf5d))
749 | * **deps:** add missing pull-stream dependency ([5ef7176](https://github.com/ipfs/js-ipfs-merkle-dag/commit/5ef7176))
750 | * browser testing (dixie problem) ([0c98929](https://github.com/ipfs/js-ipfs-merkle-dag/commit/0c98929))
751 | * do not convert existing daglinks ([0e4361f](https://github.com/ipfs/js-ipfs-merkle-dag/commit/0e4361f))
752 |
753 |
754 | ### Features
755 |
756 | * generate cid ([6165a91](https://github.com/ipfs/js-ipfs-merkle-dag/commit/6165a91))
757 | * let utils be utils ([82fc2fe](https://github.com/ipfs/js-ipfs-merkle-dag/commit/82fc2fe))
758 | * migrate dag-node size, multihash and util serialize, deserialize and cid to async from sync ([d2bf303](https://github.com/ipfs/js-ipfs-merkle-dag/commit/d2bf303))
759 | * migrate resolver to async API ([2d3d220](https://github.com/ipfs/js-ipfs-merkle-dag/commit/2d3d220))
760 | * new util API, move serialize, deserialize and cid out ([473a991](https://github.com/ipfs/js-ipfs-merkle-dag/commit/473a991))
761 | * resolver, tree + tests ([23ba424](https://github.com/ipfs/js-ipfs-merkle-dag/commit/23ba424))
762 | * s/copy/clone, simplify internal API (encoded stuff) and update tests to understand cid ([bbb5ab9](https://github.com/ipfs/js-ipfs-merkle-dag/commit/bbb5ab9))
763 | * yield remainderPath if not possible to result through ([680bf4e](https://github.com/ipfs/js-ipfs-merkle-dag/commit/680bf4e))
764 |
765 |
766 |
767 |
768 | ## [0.7.3](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.7.2...v0.7.3) (2016-09-09)
769 |
770 |
771 |
772 |
773 | ## [0.7.2](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.7.1...v0.7.2) (2016-09-09)
774 |
775 |
776 |
777 |
778 | ## [0.7.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.7.0...v0.7.1) (2016-09-09)
779 |
780 |
781 | ### Bug Fixes
782 |
783 | * **deps:** add missing pull-stream dependency ([5ef7176](https://github.com/ipfs/js-ipfs-merkle-dag/commit/5ef7176))
784 |
785 |
786 |
787 |
788 | # [0.7.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.6.2...v0.7.0) (2016-09-08)
789 |
790 |
791 |
792 |
793 | ## [0.6.2](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.6.1...v0.6.2) (2016-08-04)
794 |
795 |
796 |
797 |
798 | ## [0.6.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.6.0...v0.6.1) (2016-08-04)
799 |
800 |
801 |
802 |
803 | # [0.6.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.5.1...v0.6.0) (2016-05-16)
804 |
805 |
806 | ### Bug Fixes
807 |
808 | * **dag-node:** ensure links are always DAGLinks ([219cf5d](https://github.com/ipfs/js-ipfs-merkle-dag/commit/219cf5d))
809 | * do not convert existing daglinks ([0e4361f](https://github.com/ipfs/js-ipfs-merkle-dag/commit/0e4361f))
810 |
811 |
812 |
813 |
814 | ## [0.5.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.5.0...v0.5.1) (2016-05-12)
815 |
816 |
817 |
818 |
819 | # [0.5.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.4.3...v0.5.0) (2016-05-02)
820 |
821 |
822 |
823 |
824 | ## [0.4.3](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.4.2...v0.4.3) (2016-04-28)
825 |
826 |
827 |
828 |
829 | ## [0.4.2](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.4.1...v0.4.2) (2016-04-26)
830 |
831 |
832 |
833 |
834 | ## [0.4.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.4.0...v0.4.1) (2016-04-26)
835 |
836 |
837 |
838 |
839 | # [0.4.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.3.0...v0.4.0) (2016-04-13)
840 |
841 |
842 |
843 |
844 | # [0.3.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.2.1...v0.3.0) (2016-03-21)
845 |
846 |
847 |
848 |
849 | ## [0.2.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.2.0...v0.2.1) (2016-02-03)
850 |
851 |
852 |
853 |
854 | # [0.2.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.1.1...v0.2.0) (2016-02-03)
855 |
856 |
857 |
858 |
859 | ## [0.1.1](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.1.0...v0.1.1) (2016-01-31)
860 |
861 |
862 |
863 |
864 | # [0.1.0](https://github.com/ipfs/js-ipfs-merkle-dag/compare/v0.0.2...v0.1.0) (2016-01-31)
865 |
866 |
867 |
868 |
869 | ## 0.0.2 (2016-01-19)
870 |
871 |
872 |
873 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-2018 Protocol Labs
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 | ⛔️ DEPRECATED: This module has been superseded by [@ipld/dag-pb](https://github.com/ipld/js-dag-pb) and [multiformats](https://github.com/multiformats/js-multiformats)
2 | ======
3 |
4 | # js-ipld-dag-pb
5 |
6 | [](http://ipn.io)
7 | [](http://github.com/ipld/ipld)
8 | [](http://webchat.freenode.net/?channels=%23ipfs)
9 | [](https://travis-ci.com/ipld/js-ipld-dag-pb)
10 | [](https://coveralls.io/github/ipld/js-ipld-dag-pb?branch=master)
11 | [](https://david-dm.org/ipld/js-ipld-dag-pb)
12 | [](https://github.com/feross/standard)
13 | [](https://github.com/RichardLitt/standard-readme)
14 | [](https://greenkeeper.io/)
15 | 
16 | 
17 |
18 | > JavaScript Implementation of the IPLD Format MerkleDAG Node in Protobuf. In addition to the IPLD Format methods, this module also provides an API for creating the nodes and manipulating them (adding and removing links, etc).
19 |
20 | ## Lead Maintainer
21 |
22 | [Volker Mische](https://github.com/vmx)
23 |
24 | ## Table of Contents
25 |
26 | - [Install](#install)
27 | - [Usage](#usage)
28 | - [Examples](#examples)
29 | - [Create a DAGNode](#create-a-dagnode)
30 | - [Add and remove a Link](#add-and-remove-a-link)
31 | - [API](#api)
32 | - [DAGNode functions](#dagnode-functions)
33 | - [DAGNode constructor](#dagnode-constructor)
34 | - [DAGNode instance methods and properties](#dagnode-instance-methods-and-properties)
35 | - [`node.Data`](#nodedata)
36 | - [`node.Links`](#nodelinks)
37 | - [`node.size`](#nodesize)
38 | - [`node.toJSON()`](#nodetojson)
39 | - [`node.toString()`](#nodetostring)
40 | - [`node.toDAGLink()`](#nodetodaglink)
41 | - [`node.addLink(link)`](#nodeaddlinklink)
42 | - [`node.rmLink(nameOrCid)`](#nodermlinknameorcid)
43 | - [`node.serialize()`](#nodeserialize)
44 | - [DAGLink functions](#daglink-functions)
45 | - [DAGLink constructor](#daglink-constructor)
46 | - [DAGLink instance methods and properties](#daglink-instance-methods-and-properties)
47 | - [`link.Name`](#linkname)
48 | - [`link.Tsize`](#linktsize)
49 | - [`link.Hash`](#linkhash)
50 | - [`link.toJSON()`](#linktojson)
51 | - [`link.toString()`](#linktostring)
52 | - [IPLD Format Specifics - Local (node/block scope) resolver](#ipld-format-specifics---local-nodeblock-scope-resolver)
53 | - [`dagPB.resolver.resolve`](#dagpbresolverresolve)
54 | - [`dagPB.resolver.tree`](#dagpbresolvertree)
55 | - [IPLD Format Specifics - util](#ipld-format-specifics---util)
56 | - [`dagPB.util.cid`](#dagpbutilcid)
57 | - [`dagPB.util.serialize`](#dagpbutilserialize)
58 | - [`dagPB.util.deserialize`](#dagpbutildeserialize)
59 | - [Contribute](#contribute)
60 | - [License](#license)
61 |
62 | ## Install
63 |
64 | ```bash
65 | > npm install ipld-dag-pb --save
66 | ```
67 |
68 | ## Usage
69 |
70 | ```JavaScript
71 | const dagPB = require('ipld-dag-pb')
72 |
73 | // IPLD Format specifics
74 | dagPB.resolver
75 | dagPB.util
76 | ```
77 |
78 | ### Examples
79 |
80 | #### Create a DAGNode
81 |
82 | ```JavaScript
83 | const node1 = new DAGNode(new TextEncoder('utf8').encode('some data'))
84 |
85 | // node2 will have the same data as node1
86 | const node2 = new DAGNode('some data')
87 | ```
88 |
89 | #### Add and remove a Link
90 |
91 | ```JavaScript
92 | const link = {
93 | Name: 'I am a link',
94 | Hash: 'QmHash..',
95 | Tsize: 42
96 | }
97 |
98 | node.addLink(link)
99 | console.log('with link', node.toJSON())
100 |
101 | nodeA.rmLink('I am a link')
102 | console.log('now without link', node.toJSON())
103 | ```
104 |
105 | ## API
106 |
107 | ### DAGNode functions
108 |
109 | DAGNodes are immutable objects, in order to manipulate them you have to follow a function approach of applying function and getting new instances of the given DAGNode.
110 |
111 | You can incude it in your project with:
112 |
113 | ```JavaScript
114 | const dagPB = require('ipld-dag-pb')
115 | const DAGNode = dagPB.DAGNode
116 | ```
117 |
118 | #### DAGNode constructor
119 |
120 | - `data` - type: Uint8Array or String
121 | - `links`- (optional) type: Array of DAGLink instances or Array of DAGLink instances in its json format (link.toJSON)
122 | - `serializedSize`- (optional) type: Number of bytes the serialized node has. If none is given, it will automatically be calculated.
123 |
124 | Create a DAGNode.
125 |
126 | ```JavaScript
127 | const dagNode = new DAGNode('data', links)
128 | ```
129 |
130 | links can be a single or an array of DAGLinks instances or objects with the following pattern
131 |
132 | ```JavaScript
133 | {
134 | Name: '',
135 | Hash: '',
136 | TSize:
137 | }
138 | ```
139 |
140 | ### DAGNode instance methods and properties
141 |
142 | You have the following methods and properties available in every DAGNode instance.
143 |
144 | #### `node.Data`
145 |
146 | #### `node.Links`
147 |
148 | An array of JSON Objects with fields named `Hash`, `Name`, and `Tsize`.
149 |
150 | #### `node.size`
151 |
152 | Size of the node, in bytes
153 |
154 | #### `node.toJSON()`
155 |
156 | #### `node.toString()`
157 |
158 | #### `node.toDAGLink()`
159 |
160 | - `options` - (optional) type: Object. Currently the only option is `name` to specify a named link.
161 |
162 | Converts a `DAGNode` into a `DAGLink`.
163 |
164 | ```JavaScript
165 | const node = new DAGNode('some data')
166 | const link = node.toDAGLink()
167 | // Named link
168 | const link = node.toDAGLink({ name: 'name-of-the-link' })
169 | ```
170 |
171 | #### `node.addLink(link)`
172 |
173 | - `link` - type: DAGLink or DAGLink in its json format
174 |
175 | Creates a link on node A. Modifies the node.
176 |
177 | `link` can be:
178 | - DAGLink instance
179 | - DAGNode instance
180 | - Object with the following properties:
181 |
182 | ```JavaScript
183 | const link = {
184 | Name: '', // optional
185 | Tsize: ,
186 | Hash: // can be a String CID, CID buffer or CID object
187 | }
188 |
189 | node.addLink(link)
190 | ```
191 |
192 | #### `node.rmLink(nameOrCid)`
193 |
194 | - `nameOrCid` - type: String, CID object or CID buffer
195 |
196 | Removes a link from the node by name. Modifies the node.
197 |
198 | ```JavaScript
199 | node.rmLink('Link1')
200 | ```
201 |
202 | #### `node.serialize()`
203 |
204 | Serialize the DAGNode instance to its portable binary format. Yields the same result as `dagPB.util.serialize(node)`. Returns a `Uint8Array`.
205 |
206 | ### DAGLink functions
207 |
208 | Following the same pattern as [`DAGNode functions`]() above, DAGLink also offers a function for its creation.
209 |
210 | You can incude it in your project with:
211 |
212 | ```JavaScript
213 | const dagPB = require('ipld-dag-pb')
214 | const DAGLink = dagPB.DAGLink
215 | ```
216 |
217 | #### DAGLink constructor
218 |
219 | ```JavaScript
220 | // link is a DAGLink instance
221 | const link = new DAGLink(
222 | 'link-to-file', // name of the link (can be empty)
223 | 10, // size in bytes
224 | 'QmSomeHash...', // can be CID object, CID buffer or string
225 | )
226 | ```
227 |
228 | ### DAGLink instance methods and properties
229 |
230 | #### `link.Name`
231 |
232 | #### `link.Tsize`
233 |
234 | #### `link.Hash`
235 |
236 | #### `link.toJSON()`
237 |
238 | #### `link.toString()`
239 |
240 | ### [IPLD Format Specifics](https://github.com/ipld/interface-ipld-format) - Local (node/block scope) resolver
241 |
242 | > See: https://github.com/ipld/interface-ipld-format#local-resolver-methods
243 |
244 | #### `dagPB.resolver.resolve`
245 |
246 | #### `dagPB.resolver.tree`
247 |
248 | ### [IPLD Format Specifics](https://github.com/ipld/interface-ipld-format) - util
249 |
250 | > See: https://github.com/ipld/interface-ipld-format#ipld-format-utils
251 |
252 | ### `dagPB.util.cid`
253 |
254 | ### `dagPB.util.serialize`
255 |
256 | Serialize the DAGNode instance to its portable binary format. Yields the same result as `node.serialize()`. Returns a `Uint8Array`.
257 |
258 | ### `dagPB.util.deserialize`
259 |
260 | Deserialize a DAGNode instance from its portable binary format. Returns a DAGNode.
261 |
262 | ## Contribute
263 |
264 | Please contribute! [Look at the issues](https://github.com/ipld/js-ipld-dag-pb/issues)!
265 |
266 | Check out our [contributing document](https://github.com/ipld/ipld/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to IPLD are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
267 |
268 | Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
269 |
270 | ## License
271 |
272 | [ISC](LICENSE) © 2016 Protocol Labs Inc.
273 |
--------------------------------------------------------------------------------
/graphs/arch.monopic:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ipld/js-ipld-dag-pb/6b0e011b7917611386cff392d56bfd81c8cacf8c/graphs/arch.monopic
--------------------------------------------------------------------------------
/graphs/arch.txt:
--------------------------------------------------------------------------------
1 | ┌────────────────────┐
2 | │ DAGService │
3 | └────────────────────┘
4 | │
5 | ▼
6 | ┌────────────────────┐
7 | │ BlockService │
8 | └────────────────────┘
9 | │
10 | ┌─────┴─────┐
11 | ▼ ▼
12 | ┌─────────┐ ┌────────┐
13 | │IPFS REPO│ │Exchange│
14 | └─────────┘ └────────┘
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ipld-dag-pb",
3 | "version": "0.22.3",
4 | "description": "JavaScript Implementation of the MerkleDAG Node in Protobuf.",
5 | "leadMaintainer": "Volker Mische ",
6 | "main": "src/index.js",
7 | "scripts": {
8 | "prepare": "run-s prepare:* build",
9 | "prepare:proto": "pbjs -t static-module -w commonjs --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/dag.js ./src/dag.proto",
10 | "prepare:proto:types": "pbts -o src/dag.d.ts src/dag.js",
11 | "build": "aegir build",
12 | "test": "aegir test",
13 | "test:browser": "aegir test --target browser",
14 | "test:node": "aegir test --target node",
15 | "lint": "run-s lint:*",
16 | "lint:js": "aegir lint",
17 | "lint:types": "aegir ts --check",
18 | "release": "aegir release",
19 | "release-minor": "aegir release --type minor",
20 | "release-major": "aegir release --type major",
21 | "coverage": "aegir coverage",
22 | "coverage-publish": "aegir coverage publish"
23 | },
24 | "files": [
25 | "src",
26 | "dist"
27 | ],
28 | "pre-push": [
29 | "lint",
30 | "test"
31 | ],
32 | "contributors": [
33 | "David Dias ",
34 | "Volker Mische ",
35 | "Vijayee Kulkaa ",
36 | "achingbrain ",
37 | "Friedel Ziegelmayer ",
38 | "nginnever ",
39 | "Irakli Gozalishvili ",
40 | "Hugo Dias ",
41 | "Richard Schneider ",
42 | "Diogo Silva ",
43 | "Stephen Whitmore ",
44 | "Richard Littauer ",
45 | "Matteo Collina ",
46 | "Mitar ",
47 | "James Halliday ",
48 | "Chris Joel ",
49 | "ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ ",
50 | "Alan Shaw ",
51 | "Oli Evans ",
52 | "Rod Vagg ",
53 | "Ryan Bell ",
54 | "Stanisław Drozd ",
55 | "Yahya ",
56 | "dmitriy ryajov ",
57 | "dryajov ",
58 | "haad ",
59 | "popmanhe "
60 | ],
61 | "license": "MIT",
62 | "repository": {
63 | "type": "git",
64 | "url": "https://github.com/ipld/js-ipld-dag-pb.git"
65 | },
66 | "engines": {
67 | "node": ">=6.0.0",
68 | "npm": ">=3.0.0"
69 | },
70 | "dependencies": {
71 | "cids": "^1.0.0",
72 | "interface-ipld-format": "^1.0.0",
73 | "multicodec": "^3.0.1",
74 | "multihashing-async": "^2.0.0",
75 | "protobufjs": "^6.10.2",
76 | "stable": "^0.1.8",
77 | "uint8arrays": "^2.0.5"
78 | },
79 | "devDependencies": {
80 | "aegir": "^33.0.0",
81 | "multibase": "^4.0.1",
82 | "npm-run-all": "^4.1.5"
83 | },
84 | "types": "dist/src/index.d.ts",
85 | "typesVersions": {
86 | "*": {
87 | "src/*": [
88 | "dist/src/*",
89 | "dist/src/*/index"
90 | ],
91 | "src/": [
92 | "dist/src/index"
93 | ]
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/dag-link/dagLink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const CID = require('cids')
4 | const uint8ArrayFromString = require('uint8arrays/from-string')
5 |
6 | /**
7 | * Link represents an IPFS Merkle DAG Link between Nodes.
8 | */
9 | class DAGLink {
10 | /**
11 | * @param {string | undefined | null} name
12 | * @param {number} size
13 | * @param {CID | string | Uint8Array} cid
14 | */
15 | constructor (name, size, cid) {
16 | if (!cid) {
17 | throw new Error('A link requires a cid to point to')
18 | }
19 |
20 | // assert(size, 'A link requires a size')
21 | // note - links should include size, but this assert is disabled
22 | // for now to maintain consistency with go-ipfs pinset
23 | this.Name = name || ''
24 | this.Tsize = size
25 | this.Hash = new CID(cid)
26 |
27 | Object.defineProperties(this, {
28 | _nameBuf: { value: null, writable: true, enumerable: false }
29 | })
30 | }
31 |
32 | toString () {
33 | return `DAGLink <${this.Hash.toBaseEncodedString()} - name: "${this.Name}", size: ${this.Tsize}>`
34 | }
35 |
36 | toJSON () {
37 | if (!this._json) {
38 | this._json = Object.freeze({
39 | name: this.Name,
40 | size: this.Tsize,
41 | cid: this.Hash.toBaseEncodedString()
42 | })
43 | }
44 |
45 | return Object.assign({}, this._json)
46 | }
47 |
48 | // Memoize the Uint8Array representation of name
49 | // We need this to sort the links, otherwise
50 | // we will reallocate new Uint8Arrays every time
51 | get nameAsBuffer () {
52 | if (this._nameBuf != null) {
53 | return this._nameBuf
54 | }
55 |
56 | this._nameBuf = uint8ArrayFromString(this.Name)
57 | return this._nameBuf
58 | }
59 | }
60 |
61 | module.exports = DAGLink
62 |
--------------------------------------------------------------------------------
/src/dag-link/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | exports = module.exports = require('./dagLink')
4 |
--------------------------------------------------------------------------------
/src/dag-link/util.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const DAGLink = require('./dagLink')
4 |
5 | /**
6 | * @param {*} link
7 | */
8 | function createDagLinkFromB58EncodedHash (link) {
9 | return new DAGLink(
10 | link.Name || link.name || '',
11 | link.Tsize || link.Size || link.size || 0,
12 | link.Hash || link.hash || link.multihash || link.cid
13 | )
14 | }
15 |
16 | module.exports = {
17 | createDagLinkFromB58EncodedHash
18 | }
19 |
--------------------------------------------------------------------------------
/src/dag-node/addLink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sortLinks = require('./sortLinks')
4 | const DAGLink = require('../dag-link/dagLink')
5 |
6 | /**
7 | * @typedef {import('./dagNode')} DAGNode
8 | * @typedef {import('../types')} DAGLinkLike
9 | */
10 |
11 | /**
12 | * @param {*} link
13 | * @returns {DAGLink}
14 | */
15 | const asDAGLink = (link) => {
16 | if (link instanceof DAGLink) {
17 | // It's a DAGLink instance
18 | // no need to do anything
19 | return link
20 | }
21 |
22 | // DAGNode.isDagNode() would be more appropriate here, but it can't be used
23 | // as it would lead to circular dependencies as `addLink` is called from
24 | // within the DAGNode object.
25 | if (!('cid' in link ||
26 | 'hash' in link ||
27 | 'Hash' in link ||
28 | 'multihash' in link)) {
29 | throw new Error('Link must be a DAGLink or DAGLink-like. Convert the DAGNode into a DAGLink via `node.toDAGLink()`.')
30 | }
31 |
32 | // It's a Object with name, multihash/hash/cid and size
33 | // @ts-ignore
34 | return new DAGLink(link.Name || link.name, link.Tsize || link.size, link.Hash || link.multihash || link.hash || link.cid)
35 | }
36 |
37 | /**
38 | * @param {DAGNode} node
39 | * @param {DAGLink | DAGLinkLike} link
40 | */
41 | const addLink = (node, link) => {
42 | const dagLink = asDAGLink(link)
43 | node.Links.push(dagLink)
44 | sortLinks(node.Links)
45 | }
46 |
47 | module.exports = addLink
48 |
--------------------------------------------------------------------------------
/src/dag-node/dagNode.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sortLinks = require('./sortLinks')
4 | const DAGLink = require('../dag-link/dagLink')
5 | const { createDagLinkFromB58EncodedHash } = require('../dag-link/util')
6 | const { serializeDAGNode } = require('../serialize')
7 | const toDAGLink = require('./toDagLink')
8 | const addLink = require('./addLink')
9 | const rmLink = require('./rmLink')
10 | const uint8ArrayFromString = require('uint8arrays/from-string')
11 | const uint8ArrayToString = require('uint8arrays/to-string')
12 |
13 | /**
14 | * @typedef {import('cids')} CID
15 | * @typedef {import('../types').DAGLinkLike} DAGLinkLike
16 | */
17 |
18 | class DAGNode {
19 | /**
20 | *@param {Uint8Array | string} [data]
21 | * @param {(DAGLink | DAGLinkLike)[]} links
22 | * @param {number | null} [serializedSize]
23 | */
24 | constructor (data, links = [], serializedSize = null) {
25 | if (!data) {
26 | data = new Uint8Array(0)
27 | }
28 | if (typeof data === 'string') {
29 | data = uint8ArrayFromString(data)
30 | }
31 |
32 | if (!(data instanceof Uint8Array)) {
33 | throw new Error('Passed \'data\' is not a Uint8Array or a String!')
34 | }
35 |
36 | if (serializedSize !== null && typeof serializedSize !== 'number') {
37 | throw new Error('Passed \'serializedSize\' must be a number!')
38 | }
39 |
40 | const sortedLinks = links.map((link) => {
41 | return link instanceof DAGLink
42 | ? link
43 | : createDagLinkFromB58EncodedHash(link)
44 | })
45 | sortLinks(sortedLinks)
46 |
47 | this.Data = data
48 | this.Links = sortedLinks
49 |
50 | Object.defineProperties(this, {
51 | _serializedSize: { value: serializedSize, writable: true, enumerable: false },
52 | _size: { value: null, writable: true, enumerable: false }
53 | })
54 | }
55 |
56 | toJSON () {
57 | if (!this._json) {
58 | this._json = Object.freeze({
59 | data: this.Data,
60 | links: this.Links.map((l) => l.toJSON()),
61 | size: this.size
62 | })
63 | }
64 |
65 | return Object.assign({}, this._json)
66 | }
67 |
68 | toString () {
69 | return `DAGNode `
70 | }
71 |
72 | _invalidateCached () {
73 | this._serializedSize = null
74 | this._size = null
75 | }
76 |
77 | /**
78 | * @param {DAGLink | import('../types').DAGLinkLike} link
79 | */
80 | addLink (link) {
81 | this._invalidateCached()
82 | return addLink(this, link)
83 | }
84 |
85 | /**
86 | * @param {DAGLink | string | CID} link
87 | */
88 | rmLink (link) {
89 | this._invalidateCached()
90 | return rmLink(this, link)
91 | }
92 |
93 | /**
94 | * @param {import('./toDagLink').ToDagLinkOptions} [options]
95 | */
96 | toDAGLink (options) {
97 | return toDAGLink(this, options)
98 | }
99 |
100 | serialize () {
101 | const buf = serializeDAGNode(this)
102 |
103 | this._serializedSize = buf.length
104 |
105 | return buf
106 | }
107 |
108 | get size () {
109 | if (this._size == null) {
110 | let serializedSize
111 |
112 | if (serializedSize == null) {
113 | this._serializedSize = this.serialize().length
114 | serializedSize = this._serializedSize
115 | }
116 |
117 | this._size = this.Links.reduce((sum, l) => sum + l.Tsize, serializedSize)
118 | }
119 |
120 | return this._size
121 | }
122 |
123 | set size (size) {
124 | throw new Error("Can't set property: 'size' is immutable")
125 | }
126 | }
127 |
128 | module.exports = DAGNode
129 |
--------------------------------------------------------------------------------
/src/dag-node/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | exports = module.exports = require('./dagNode')
4 |
--------------------------------------------------------------------------------
/src/dag-node/rmLink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const CID = require('cids')
4 | const uint8ArrayEquals = require('uint8arrays/equals')
5 |
6 | /**
7 | * @typedef {import('../dag-link/dagLink')} DAGLink
8 | */
9 |
10 | /**
11 | *
12 | * @param {import('./dagNode')} dagNode
13 | * @param {string | CID | Uint8Array | DAGLink} nameOrCid
14 | */
15 | const rmLink = (dagNode, nameOrCid) => {
16 | let predicate = null
17 |
18 | // It's a name
19 | if (typeof nameOrCid === 'string') {
20 | predicate = (/** @type {DAGLink} */ link) => link.Name === nameOrCid
21 | } else if (nameOrCid instanceof Uint8Array) {
22 | predicate = (/** @type {DAGLink} */ link) => uint8ArrayEquals(link.Hash.bytes, nameOrCid)
23 | } else if (CID.isCID(nameOrCid)) {
24 | predicate = (/** @type {DAGLink} */ link) => uint8ArrayEquals(link.Hash.bytes, nameOrCid.bytes)
25 | }
26 |
27 | if (predicate) {
28 | const links = dagNode.Links
29 | let index = 0
30 | while (index < links.length) {
31 | const link = links[index]
32 | if (predicate(link)) {
33 | links.splice(index, 1)
34 | } else {
35 | index++
36 | }
37 | }
38 | } else {
39 | throw new Error('second arg needs to be a name or CID')
40 | }
41 | }
42 |
43 | module.exports = rmLink
44 |
--------------------------------------------------------------------------------
/src/dag-node/sortLinks.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sort = require('stable')
4 | const uint8ArrayCompare = require('uint8arrays/compare')
5 |
6 | /**
7 | * @typedef {import('../dag-link/dagLink')} DAGLink
8 | */
9 |
10 | /**
11 | *
12 | * @param {DAGLink} a
13 | * @param {DAGLink} b
14 | */
15 | const linkSort = (a, b) => {
16 | const buf1 = a.nameAsBuffer
17 | const buf2 = b.nameAsBuffer
18 |
19 | return uint8ArrayCompare(buf1, buf2)
20 | }
21 |
22 | /**
23 | * Sorts links in place (mutating given array)
24 | *
25 | * @param {DAGLink[]} links
26 | * @returns {void}
27 | */
28 | const sortLinks = (links) => {
29 | sort.inplace(links, linkSort)
30 | }
31 |
32 | module.exports = sortLinks
33 |
--------------------------------------------------------------------------------
/src/dag-node/toDagLink.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const DAGLink = require('../dag-link/dagLink')
4 | const genCid = require('../genCid')
5 |
6 | /**
7 | * toDAGLink converts a DAGNode to a DAGLink
8 | *
9 | * @typedef {import('../genCid').GenCIDOptions} GenCIDOptions
10 | *
11 | * @typedef {object} ToDagLinkExtraOptions
12 | * @property {string} [name]
13 | *
14 | * @typedef {GenCIDOptions & ToDagLinkExtraOptions} ToDagLinkOptions
15 | *
16 | * @param {import('./dagNode')} node
17 | * @param {ToDagLinkOptions} options
18 | */
19 | const toDAGLink = async (node, options = {}) => {
20 | const buf = node.serialize()
21 | const nodeCid = await genCid.cid(buf, options)
22 | return new DAGLink(options.name || '', node.size, nodeCid)
23 | }
24 |
25 | module.exports = toDAGLink
26 |
--------------------------------------------------------------------------------
/src/dag.d.ts:
--------------------------------------------------------------------------------
1 | import * as $protobuf from "protobufjs";
2 | /** Properties of a PBLink. */
3 | export interface IPBLink {
4 |
5 | /** PBLink Hash */
6 | Hash?: (Uint8Array|null);
7 |
8 | /** PBLink Name */
9 | Name?: (string|null);
10 |
11 | /** PBLink Tsize */
12 | Tsize?: (number|null);
13 | }
14 |
15 | /** Represents a PBLink. */
16 | export class PBLink implements IPBLink {
17 |
18 | /**
19 | * Constructs a new PBLink.
20 | * @param [p] Properties to set
21 | */
22 | constructor(p?: IPBLink);
23 |
24 | /** PBLink Hash. */
25 | public Hash: Uint8Array;
26 |
27 | /** PBLink Name. */
28 | public Name: string;
29 |
30 | /** PBLink Tsize. */
31 | public Tsize: number;
32 |
33 | /**
34 | * Encodes the specified PBLink message. Does not implicitly {@link PBLink.verify|verify} messages.
35 | * @param m PBLink message or plain object to encode
36 | * @param [w] Writer to encode to
37 | * @returns Writer
38 | */
39 | public static encode(m: IPBLink, w?: $protobuf.Writer): $protobuf.Writer;
40 |
41 | /**
42 | * Decodes a PBLink message from the specified reader or buffer.
43 | * @param r Reader or buffer to decode from
44 | * @param [l] Message length if known beforehand
45 | * @returns PBLink
46 | * @throws {Error} If the payload is not a reader or valid buffer
47 | * @throws {$protobuf.util.ProtocolError} If required fields are missing
48 | */
49 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): PBLink;
50 |
51 | /**
52 | * Creates a PBLink message from a plain object. Also converts values to their respective internal types.
53 | * @param d Plain object
54 | * @returns PBLink
55 | */
56 | public static fromObject(d: { [k: string]: any }): PBLink;
57 |
58 | /**
59 | * Creates a plain object from a PBLink message. Also converts values to other types if specified.
60 | * @param m PBLink
61 | * @param [o] Conversion options
62 | * @returns Plain object
63 | */
64 | public static toObject(m: PBLink, o?: $protobuf.IConversionOptions): { [k: string]: any };
65 |
66 | /**
67 | * Converts this PBLink to JSON.
68 | * @returns JSON object
69 | */
70 | public toJSON(): { [k: string]: any };
71 | }
72 |
73 | /** Properties of a PBNode. */
74 | export interface IPBNode {
75 |
76 | /** PBNode Links */
77 | Links?: (IPBLink[]|null);
78 |
79 | /** PBNode Data */
80 | Data?: (Uint8Array|null);
81 | }
82 |
83 | /** Represents a PBNode. */
84 | export class PBNode implements IPBNode {
85 |
86 | /**
87 | * Constructs a new PBNode.
88 | * @param [p] Properties to set
89 | */
90 | constructor(p?: IPBNode);
91 |
92 | /** PBNode Links. */
93 | public Links: IPBLink[];
94 |
95 | /** PBNode Data. */
96 | public Data: Uint8Array;
97 |
98 | /**
99 | * Encodes the specified PBNode message. Does not implicitly {@link PBNode.verify|verify} messages.
100 | * @param m PBNode message or plain object to encode
101 | * @param [w] Writer to encode to
102 | * @returns Writer
103 | */
104 | public static encode(m: IPBNode, w?: $protobuf.Writer): $protobuf.Writer;
105 |
106 | /**
107 | * Decodes a PBNode message from the specified reader or buffer.
108 | * @param r Reader or buffer to decode from
109 | * @param [l] Message length if known beforehand
110 | * @returns PBNode
111 | * @throws {Error} If the payload is not a reader or valid buffer
112 | * @throws {$protobuf.util.ProtocolError} If required fields are missing
113 | */
114 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): PBNode;
115 |
116 | /**
117 | * Creates a PBNode message from a plain object. Also converts values to their respective internal types.
118 | * @param d Plain object
119 | * @returns PBNode
120 | */
121 | public static fromObject(d: { [k: string]: any }): PBNode;
122 |
123 | /**
124 | * Creates a plain object from a PBNode message. Also converts values to other types if specified.
125 | * @param m PBNode
126 | * @param [o] Conversion options
127 | * @returns Plain object
128 | */
129 | public static toObject(m: PBNode, o?: $protobuf.IConversionOptions): { [k: string]: any };
130 |
131 | /**
132 | * Converts this PBNode to JSON.
133 | * @returns JSON object
134 | */
135 | public toJSON(): { [k: string]: any };
136 | }
137 |
--------------------------------------------------------------------------------
/src/dag.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable*/
2 | "use strict";
3 |
4 | var $protobuf = require("protobufjs/minimal");
5 |
6 | // Common aliases
7 | var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
8 |
9 | // Exported root namespace
10 | var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
11 |
12 | $root.PBLink = (function() {
13 |
14 | /**
15 | * Properties of a PBLink.
16 | * @exports IPBLink
17 | * @interface IPBLink
18 | * @property {Uint8Array|null} [Hash] PBLink Hash
19 | * @property {string|null} [Name] PBLink Name
20 | * @property {number|null} [Tsize] PBLink Tsize
21 | */
22 |
23 | /**
24 | * Constructs a new PBLink.
25 | * @exports PBLink
26 | * @classdesc Represents a PBLink.
27 | * @implements IPBLink
28 | * @constructor
29 | * @param {IPBLink=} [p] Properties to set
30 | */
31 | function PBLink(p) {
32 | if (p)
33 | for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
34 | if (p[ks[i]] != null)
35 | this[ks[i]] = p[ks[i]];
36 | }
37 |
38 | /**
39 | * PBLink Hash.
40 | * @member {Uint8Array} Hash
41 | * @memberof PBLink
42 | * @instance
43 | */
44 | PBLink.prototype.Hash = $util.newBuffer([]);
45 |
46 | /**
47 | * PBLink Name.
48 | * @member {string} Name
49 | * @memberof PBLink
50 | * @instance
51 | */
52 | PBLink.prototype.Name = "";
53 |
54 | /**
55 | * PBLink Tsize.
56 | * @member {number} Tsize
57 | * @memberof PBLink
58 | * @instance
59 | */
60 | PBLink.prototype.Tsize = $util.Long ? $util.Long.fromBits(0,0,true) : 0;
61 |
62 | /**
63 | * Encodes the specified PBLink message. Does not implicitly {@link PBLink.verify|verify} messages.
64 | * @function encode
65 | * @memberof PBLink
66 | * @static
67 | * @param {IPBLink} m PBLink message or plain object to encode
68 | * @param {$protobuf.Writer} [w] Writer to encode to
69 | * @returns {$protobuf.Writer} Writer
70 | */
71 | PBLink.encode = function encode(m, w) {
72 | if (!w)
73 | w = $Writer.create();
74 | if (m.Hash != null && Object.hasOwnProperty.call(m, "Hash"))
75 | w.uint32(10).bytes(m.Hash);
76 | if (m.Name != null && Object.hasOwnProperty.call(m, "Name"))
77 | w.uint32(18).string(m.Name);
78 | if (m.Tsize != null && Object.hasOwnProperty.call(m, "Tsize"))
79 | w.uint32(24).uint64(m.Tsize);
80 | return w;
81 | };
82 |
83 | /**
84 | * Decodes a PBLink message from the specified reader or buffer.
85 | * @function decode
86 | * @memberof PBLink
87 | * @static
88 | * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
89 | * @param {number} [l] Message length if known beforehand
90 | * @returns {PBLink} PBLink
91 | * @throws {Error} If the payload is not a reader or valid buffer
92 | * @throws {$protobuf.util.ProtocolError} If required fields are missing
93 | */
94 | PBLink.decode = function decode(r, l) {
95 | if (!(r instanceof $Reader))
96 | r = $Reader.create(r);
97 | var c = l === undefined ? r.len : r.pos + l, m = new $root.PBLink();
98 | while (r.pos < c) {
99 | var t = r.uint32();
100 | switch (t >>> 3) {
101 | case 1:
102 | m.Hash = r.bytes();
103 | break;
104 | case 2:
105 | m.Name = r.string();
106 | break;
107 | case 3:
108 | m.Tsize = r.uint64();
109 | break;
110 | default:
111 | r.skipType(t & 7);
112 | break;
113 | }
114 | }
115 | return m;
116 | };
117 |
118 | /**
119 | * Creates a PBLink message from a plain object. Also converts values to their respective internal types.
120 | * @function fromObject
121 | * @memberof PBLink
122 | * @static
123 | * @param {Object.} d Plain object
124 | * @returns {PBLink} PBLink
125 | */
126 | PBLink.fromObject = function fromObject(d) {
127 | if (d instanceof $root.PBLink)
128 | return d;
129 | var m = new $root.PBLink();
130 | if (d.Hash != null) {
131 | if (typeof d.Hash === "string")
132 | $util.base64.decode(d.Hash, m.Hash = $util.newBuffer($util.base64.length(d.Hash)), 0);
133 | else if (d.Hash.length)
134 | m.Hash = d.Hash;
135 | }
136 | if (d.Name != null) {
137 | m.Name = String(d.Name);
138 | }
139 | if (d.Tsize != null) {
140 | if ($util.Long)
141 | (m.Tsize = $util.Long.fromValue(d.Tsize)).unsigned = true;
142 | else if (typeof d.Tsize === "string")
143 | m.Tsize = parseInt(d.Tsize, 10);
144 | else if (typeof d.Tsize === "number")
145 | m.Tsize = d.Tsize;
146 | else if (typeof d.Tsize === "object")
147 | m.Tsize = new $util.LongBits(d.Tsize.low >>> 0, d.Tsize.high >>> 0).toNumber(true);
148 | }
149 | return m;
150 | };
151 |
152 | /**
153 | * Creates a plain object from a PBLink message. Also converts values to other types if specified.
154 | * @function toObject
155 | * @memberof PBLink
156 | * @static
157 | * @param {PBLink} m PBLink
158 | * @param {$protobuf.IConversionOptions} [o] Conversion options
159 | * @returns {Object.} Plain object
160 | */
161 | PBLink.toObject = function toObject(m, o) {
162 | if (!o)
163 | o = {};
164 | var d = {};
165 | if (o.defaults) {
166 | if (o.bytes === String)
167 | d.Hash = "";
168 | else {
169 | d.Hash = [];
170 | if (o.bytes !== Array)
171 | d.Hash = $util.newBuffer(d.Hash);
172 | }
173 | d.Name = "";
174 | if ($util.Long) {
175 | var n = new $util.Long(0, 0, true);
176 | d.Tsize = o.longs === String ? n.toString() : o.longs === Number ? n.toNumber() : n;
177 | } else
178 | d.Tsize = o.longs === String ? "0" : 0;
179 | }
180 | if (m.Hash != null && m.hasOwnProperty("Hash")) {
181 | d.Hash = o.bytes === String ? $util.base64.encode(m.Hash, 0, m.Hash.length) : o.bytes === Array ? Array.prototype.slice.call(m.Hash) : m.Hash;
182 | }
183 | if (m.Name != null && m.hasOwnProperty("Name")) {
184 | d.Name = m.Name;
185 | }
186 | if (m.Tsize != null && m.hasOwnProperty("Tsize")) {
187 | if (typeof m.Tsize === "number")
188 | d.Tsize = o.longs === String ? String(m.Tsize) : m.Tsize;
189 | else
190 | d.Tsize = o.longs === String ? $util.Long.prototype.toString.call(m.Tsize) : o.longs === Number ? new $util.LongBits(m.Tsize.low >>> 0, m.Tsize.high >>> 0).toNumber(true) : m.Tsize;
191 | }
192 | return d;
193 | };
194 |
195 | /**
196 | * Converts this PBLink to JSON.
197 | * @function toJSON
198 | * @memberof PBLink
199 | * @instance
200 | * @returns {Object.} JSON object
201 | */
202 | PBLink.prototype.toJSON = function toJSON() {
203 | return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
204 | };
205 |
206 | return PBLink;
207 | })();
208 |
209 | $root.PBNode = (function() {
210 |
211 | /**
212 | * Properties of a PBNode.
213 | * @exports IPBNode
214 | * @interface IPBNode
215 | * @property {Array.|null} [Links] PBNode Links
216 | * @property {Uint8Array|null} [Data] PBNode Data
217 | */
218 |
219 | /**
220 | * Constructs a new PBNode.
221 | * @exports PBNode
222 | * @classdesc Represents a PBNode.
223 | * @implements IPBNode
224 | * @constructor
225 | * @param {IPBNode=} [p] Properties to set
226 | */
227 | function PBNode(p) {
228 | this.Links = [];
229 | if (p)
230 | for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
231 | if (p[ks[i]] != null)
232 | this[ks[i]] = p[ks[i]];
233 | }
234 |
235 | /**
236 | * PBNode Links.
237 | * @member {Array.} Links
238 | * @memberof PBNode
239 | * @instance
240 | */
241 | PBNode.prototype.Links = $util.emptyArray;
242 |
243 | /**
244 | * PBNode Data.
245 | * @member {Uint8Array} Data
246 | * @memberof PBNode
247 | * @instance
248 | */
249 | PBNode.prototype.Data = $util.newBuffer([]);
250 |
251 | /**
252 | * Encodes the specified PBNode message. Does not implicitly {@link PBNode.verify|verify} messages.
253 | * @function encode
254 | * @memberof PBNode
255 | * @static
256 | * @param {IPBNode} m PBNode message or plain object to encode
257 | * @param {$protobuf.Writer} [w] Writer to encode to
258 | * @returns {$protobuf.Writer} Writer
259 | */
260 | PBNode.encode = function encode(m, w) {
261 | if (!w)
262 | w = $Writer.create();
263 | if (m.Data != null && Object.hasOwnProperty.call(m, "Data"))
264 | w.uint32(10).bytes(m.Data);
265 | if (m.Links != null && m.Links.length) {
266 | for (var i = 0; i < m.Links.length; ++i)
267 | $root.PBLink.encode(m.Links[i], w.uint32(18).fork()).ldelim();
268 | }
269 | return w;
270 | };
271 |
272 | /**
273 | * Decodes a PBNode message from the specified reader or buffer.
274 | * @function decode
275 | * @memberof PBNode
276 | * @static
277 | * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from
278 | * @param {number} [l] Message length if known beforehand
279 | * @returns {PBNode} PBNode
280 | * @throws {Error} If the payload is not a reader or valid buffer
281 | * @throws {$protobuf.util.ProtocolError} If required fields are missing
282 | */
283 | PBNode.decode = function decode(r, l) {
284 | if (!(r instanceof $Reader))
285 | r = $Reader.create(r);
286 | var c = l === undefined ? r.len : r.pos + l, m = new $root.PBNode();
287 | while (r.pos < c) {
288 | var t = r.uint32();
289 | switch (t >>> 3) {
290 | case 2:
291 | if (!(m.Links && m.Links.length))
292 | m.Links = [];
293 | m.Links.push($root.PBLink.decode(r, r.uint32()));
294 | break;
295 | case 1:
296 | m.Data = r.bytes();
297 | break;
298 | default:
299 | r.skipType(t & 7);
300 | break;
301 | }
302 | }
303 | return m;
304 | };
305 |
306 | /**
307 | * Creates a PBNode message from a plain object. Also converts values to their respective internal types.
308 | * @function fromObject
309 | * @memberof PBNode
310 | * @static
311 | * @param {Object.} d Plain object
312 | * @returns {PBNode} PBNode
313 | */
314 | PBNode.fromObject = function fromObject(d) {
315 | if (d instanceof $root.PBNode)
316 | return d;
317 | var m = new $root.PBNode();
318 | if (d.Links) {
319 | if (!Array.isArray(d.Links))
320 | throw TypeError(".PBNode.Links: array expected");
321 | m.Links = [];
322 | for (var i = 0; i < d.Links.length; ++i) {
323 | if (typeof d.Links[i] !== "object")
324 | throw TypeError(".PBNode.Links: object expected");
325 | m.Links[i] = $root.PBLink.fromObject(d.Links[i]);
326 | }
327 | }
328 | if (d.Data != null) {
329 | if (typeof d.Data === "string")
330 | $util.base64.decode(d.Data, m.Data = $util.newBuffer($util.base64.length(d.Data)), 0);
331 | else if (d.Data.length)
332 | m.Data = d.Data;
333 | }
334 | return m;
335 | };
336 |
337 | /**
338 | * Creates a plain object from a PBNode message. Also converts values to other types if specified.
339 | * @function toObject
340 | * @memberof PBNode
341 | * @static
342 | * @param {PBNode} m PBNode
343 | * @param {$protobuf.IConversionOptions} [o] Conversion options
344 | * @returns {Object.} Plain object
345 | */
346 | PBNode.toObject = function toObject(m, o) {
347 | if (!o)
348 | o = {};
349 | var d = {};
350 | if (o.arrays || o.defaults) {
351 | d.Links = [];
352 | }
353 | if (o.defaults) {
354 | if (o.bytes === String)
355 | d.Data = "";
356 | else {
357 | d.Data = [];
358 | if (o.bytes !== Array)
359 | d.Data = $util.newBuffer(d.Data);
360 | }
361 | }
362 | if (m.Data != null && m.hasOwnProperty("Data")) {
363 | d.Data = o.bytes === String ? $util.base64.encode(m.Data, 0, m.Data.length) : o.bytes === Array ? Array.prototype.slice.call(m.Data) : m.Data;
364 | }
365 | if (m.Links && m.Links.length) {
366 | d.Links = [];
367 | for (var j = 0; j < m.Links.length; ++j) {
368 | d.Links[j] = $root.PBLink.toObject(m.Links[j], o);
369 | }
370 | }
371 | return d;
372 | };
373 |
374 | /**
375 | * Converts this PBNode to JSON.
376 | * @function toJSON
377 | * @memberof PBNode
378 | * @instance
379 | * @returns {Object.} JSON object
380 | */
381 | PBNode.prototype.toJSON = function toJSON() {
382 | return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
383 | };
384 |
385 | return PBNode;
386 | })();
387 |
388 | module.exports = $root;
389 |
--------------------------------------------------------------------------------
/src/dag.proto:
--------------------------------------------------------------------------------
1 | // An IPFS MerkleDAG Link
2 | message PBLink {
3 |
4 | // multihash of the target object
5 | optional bytes Hash = 1;
6 |
7 | // utf string name. should be unique per object
8 | optional string Name = 2;
9 |
10 | // cumulative size of target object
11 | optional uint64 Tsize = 3;
12 | }
13 |
14 | // An IPFS MerkleDAG Node
15 | message PBNode {
16 |
17 | // refs to other objects
18 | repeated PBLink Links = 2;
19 |
20 | // opaque user data
21 | optional bytes Data = 1;
22 | }
23 |
--------------------------------------------------------------------------------
/src/genCid.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const CID = require('cids')
4 | const multicodec = require('multicodec')
5 | const multihashing = require('multihashing-async')
6 | const { multihash } = multihashing
7 |
8 | const codec = multicodec.DAG_PB
9 | const defaultHashAlg = multihash.names['sha2-256']
10 |
11 | /**
12 | * @typedef {object} GenCIDOptions - Options to create the CID
13 | * @property {CID.CIDVersion} [cidVersion=1] - CID version number
14 | * @property {multihashing.multihash.HashCode} [hashAlg=multihash.names['sha2-256']] - Defaults to the defaultHashAlg of the format
15 | */
16 |
17 | /**
18 | * Calculate the CID of the binary blob.
19 | *
20 | * @param {Uint8Array} binaryBlob - Encoded IPLD Node
21 | * @param {GenCIDOptions} [userOptions] - Options to create the CID
22 | */
23 | const cid = async (binaryBlob, userOptions = {}) => {
24 | const options = {
25 | cidVersion: userOptions.cidVersion == null ? 1 : userOptions.cidVersion,
26 | hashAlg: userOptions.hashAlg == null ? defaultHashAlg : userOptions.hashAlg
27 | }
28 |
29 | const hashName = multihash.codes[options.hashAlg]
30 | const hash = await multihashing(binaryBlob, hashName)
31 | const codecName = multicodec.getNameFromCode(codec)
32 | const cid = new CID(options.cidVersion, codecName, hash)
33 |
34 | return cid
35 | }
36 |
37 | module.exports = {
38 | codec,
39 | defaultHashAlg,
40 | cid
41 | }
42 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const resolver = require('./resolver')
4 | const util = require('./util')
5 | const DAGNodeClass = require('./dag-node/dagNode')
6 | const DAGLinkClass = require('./dag-link/dagLink')
7 |
8 | /**
9 | * @typedef {import('./types').DAGLinkLike} DAGLinkLike
10 | * @typedef {import('./types').DAGNodeLike} DAGNodeLike
11 | * @typedef {import('./dag-node/dagNode')} DAGNode
12 | * @typedef {import('./dag-link/dagLink')} DAGLink
13 | */
14 |
15 | /**
16 | * @type {import('./types').DAGNodeFormat}
17 | */
18 | const format = {
19 | DAGNode: DAGNodeClass,
20 | DAGLink: DAGLinkClass,
21 |
22 | /**
23 | * Functions to fulfil IPLD Format interface
24 | * https://github.com/ipld/interface-ipld-format
25 | */
26 | resolver,
27 | util,
28 | codec: util.codec,
29 | defaultHashAlg: util.defaultHashAlg
30 | }
31 |
32 | module.exports = format
33 |
--------------------------------------------------------------------------------
/src/resolver.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const CID = require('cids')
4 |
5 | const util = require('./util')
6 |
7 | /**
8 | * Resolves a path within a PB block.
9 | *
10 | * If the path resolves half-way to a link, then the `remainderPath` is the part
11 | * after the link that can be used for further resolving
12 | *
13 | * Returns the value or a link and the partial missing path. This way the
14 | * IPLD Resolver can fetch the link and continue to resolve.
15 | *
16 | * @param {Uint8Array} binaryBlob - Binary representation of a PB block
17 | * @param {string} [path='/'] - Path that should be resolved
18 | */
19 | exports.resolve = (binaryBlob, path = '/') => {
20 | let node = util.deserialize(binaryBlob)
21 |
22 | const parts = path.split('/').filter(Boolean)
23 | while (parts.length) {
24 | const key = parts.shift()
25 | // @ts-ignore
26 | if (node[key] === undefined) {
27 | // There might be a matching named link
28 | for (const link of node.Links) {
29 | if (link.Name === key) {
30 | return {
31 | value: link.Hash,
32 | remainderPath: parts.join('/')
33 | }
34 | }
35 | }
36 |
37 | // There wasn't even a matching named link
38 | throw new Error(`Object has no property '${key}'`)
39 | }
40 |
41 | // @ts-ignore
42 | node = node[key]
43 | if (CID.isCID(node)) {
44 | return {
45 | value: node,
46 | remainderPath: parts.join('/')
47 | }
48 | }
49 | }
50 |
51 | return {
52 | value: node,
53 | remainderPath: ''
54 | }
55 | }
56 |
57 | /**
58 | * Return all available paths of a block.
59 | *
60 | * @generator
61 | * @param {Uint8Array} binaryBlob - Binary representation of a PB block
62 | * @yields {string} - A single path
63 | */
64 | exports.tree = function * (binaryBlob) {
65 | const node = util.deserialize(binaryBlob)
66 |
67 | // There is always a `Data` and `Links` property
68 | yield 'Data'
69 | yield 'Links'
70 | for (let ii = 0; ii < node.Links.length; ii++) {
71 | yield `Links/${ii}`
72 | yield `Links/${ii}/Name`
73 | yield `Links/${ii}/Tsize`
74 | yield `Links/${ii}/Hash`
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/serialize.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const protobuf = require('protobufjs/minimal')
4 | const {
5 | PBLink
6 | } = require('./dag')
7 |
8 | const {
9 | createDagLinkFromB58EncodedHash
10 | } = require('./dag-link/util')
11 |
12 | /**
13 | * @typedef {import('./dag-link/dagLink')} DAGLink
14 | * @typedef {import('./types').DAGLinkLike} DAGLinkLike
15 | * @typedef {import('./types').SerializableDAGNode} SerializableDAGNode
16 | * @typedef {import('cids')} CID
17 | */
18 |
19 | /**
20 | * @param { { Data?: Uint8Array, Links: (DAGLink | DAGLinkLike)[] }} node
21 | * @returns {SerializableDAGNode}
22 | */
23 | const toProtoBuf = (node) => {
24 | const pbn = {}
25 |
26 | if (node.Data && node.Data.byteLength > 0) {
27 | pbn.Data = node.Data
28 | } else {
29 | // NOTE: this has to be null in order to match go-ipfs serialization
30 | // `null !== new Uint8Array(0)`
31 | pbn.Data = null
32 | }
33 |
34 | if (node.Links && node.Links.length > 0) {
35 | pbn.Links = node.Links
36 | .map((link) => ({
37 | Hash: link.Hash.bytes,
38 | Name: link.Name,
39 | Tsize: link.Tsize
40 | }))
41 | } else {
42 | pbn.Links = null
43 | }
44 |
45 | return pbn
46 | }
47 |
48 | /**
49 | * Serialize internal representation into a binary PB block.
50 | *
51 | * @param {import('./dag-node/dagNode')} node - Internal representation of a PB block
52 | */
53 | const serializeDAGNode = (node) => {
54 | return encode(toProtoBuf(node))
55 | }
56 |
57 | /**
58 | * Serialize an object where the `Links` might not be a `DAGLink` instance yet
59 | *
60 | * @param {Uint8Array} [data]
61 | * @param {(DAGLink | string | DAGLinkLike)[]} [links]
62 | */
63 | const serializeDAGNodeLike = (data, links = []) => {
64 | const node = {
65 | Data: data,
66 | Links: links.map((link) => {
67 | return createDagLinkFromB58EncodedHash(link)
68 | })
69 | }
70 |
71 | return encode(toProtoBuf(node))
72 | }
73 |
74 | module.exports = {
75 | serializeDAGNode,
76 | serializeDAGNodeLike
77 | }
78 |
79 | /**
80 | * The fields in PBNode are the wrong way round - `id: 2` comes before
81 | * `id: 1`. protobufjs writes them out in id order but go-IPFS does not so
82 | * we have to use the protobuf.Writer interface directly to get the same
83 | * serialized form as go-IPFS
84 | *
85 | * @param {SerializableDAGNode} pbf
86 | */
87 | function encode (pbf) {
88 | const writer = protobuf.Writer.create()
89 |
90 | if (pbf.Links != null) {
91 | for (let i = 0; i < pbf.Links.length; i++) {
92 | PBLink.encode(pbf.Links[i], writer.uint32(18).fork()).ldelim()
93 | }
94 | }
95 |
96 | if (pbf.Data != null) {
97 | writer.uint32(10).bytes(pbf.Data)
98 | }
99 |
100 | return writer.finish()
101 | }
102 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | import CID from 'cids'
2 | import { Format } from 'interface-ipld-format'
3 | import DAGNode from './dag-node/dagNode'
4 | import DAGLink from './dag-link/dagLink'
5 |
6 | export interface DAGLinkLike {
7 | Hash: CID
8 | Name: string
9 | Tsize: number
10 | }
11 |
12 | export interface DAGNodeLike {
13 | Data?: Uint8Array
14 | Links?: DAGLinkLike[]
15 | }
16 |
17 | export interface SerializableDAGLink {
18 | Hash: Uint8Array
19 | Name: string
20 | Tsize: number
21 | }
22 |
23 | export interface SerializableDAGNode {
24 | Data?: Uint8Array | null
25 | Links?: SerializableDAGLink[] | null
26 | }
27 |
28 | export interface DAGNodeFormat extends Format {
29 | DAGNode: typeof DAGNode
30 | DAGLink: typeof DAGLink
31 | }
32 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const {
4 | PBNode
5 | } = require('./dag')
6 | const DAGLink = require('./dag-link/dagLink')
7 | const DAGNode = require('./dag-node/dagNode')
8 | const { serializeDAGNode, serializeDAGNodeLike } = require('./serialize')
9 | const genCid = require('./genCid')
10 |
11 | /**
12 | * @typedef {import('./types').DAGLinkLike} DAGLinkLike
13 | */
14 |
15 | /**
16 | * Calculate the CID of the binary blob
17 | *
18 | * @param {Uint8Array} binaryBlob - Encoded IPLD Node
19 | * @param {import('./genCid').GenCIDOptions} [userOptions] - Options to create the CID
20 | */
21 | const cid = (binaryBlob, userOptions) => {
22 | return genCid.cid(binaryBlob, userOptions)
23 | }
24 |
25 | /**
26 | * Serialize internal representation into a binary PB block
27 | *
28 | * @param {DAGNode | { Data?: Uint8Array, Links?: (DAGLink | DAGLinkLike)[]}} node
29 | */
30 | const serialize = (node) => {
31 | if (node instanceof DAGNode) {
32 | return serializeDAGNode(node)
33 | } else {
34 | return serializeDAGNodeLike(node.Data, node.Links)
35 | }
36 | }
37 |
38 | /**
39 | * Deserialize PB block into the internal representation.
40 | *
41 | * @param {Uint8Array} buffer - Binary representation of a PB block
42 | */
43 | const deserialize = (buffer) => {
44 | const message = PBNode.decode(buffer)
45 | const pbn = PBNode.toObject(message, {
46 | defaults: false,
47 | arrays: true,
48 | longs: Number,
49 | objects: false
50 | })
51 |
52 | /** @type {DAGLink[]} */
53 | const links = pbn.Links.map((/** @type {DAGLinkLike} */ link) => {
54 | // @ts-ignore
55 | return new DAGLink(link.Name, link.Tsize, link.Hash)
56 | })
57 |
58 | const data = pbn.Data == null ? new Uint8Array(0) : pbn.Data
59 |
60 | return new DAGNode(data, links, buffer.byteLength)
61 | }
62 |
63 | module.exports = {
64 | codec: genCid.codec,
65 | defaultHashAlg: genCid.defaultHashAlg,
66 | serialize,
67 | deserialize,
68 | cid
69 | }
70 |
--------------------------------------------------------------------------------
/test/dag-link-test.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | 'use strict'
3 |
4 | const chai = require('aegir/utils/chai')
5 | const expect = chai.expect
6 | const CID = require('cids')
7 | const DAGLink = require('../src/dag-link/dagLink')
8 | const uint8ArrayFromString = require('uint8arrays/from-string')
9 | const uint8ArrayToString = require('uint8arrays/to-string')
10 |
11 | describe('DAGLink', () => {
12 | describe('create with multihash as b58 encoded string', () => {
13 | it('string', () => {
14 | const link = new DAGLink('hello', 3, 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
15 |
16 | expect(uint8ArrayToString(link.Hash.bytes, 'base16'))
17 | .to.equal('12208ab7a6c5e74737878ac73863cb76739d15d4666de44e5756bf55a2f9e9ab5f43')
18 | })
19 |
20 | it('empty string', () => {
21 | const link = new DAGLink('', 4, 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
22 | expect(link.Name).to.be.eql('')
23 | })
24 |
25 | it('create with multihash as a multihash Buffer', () => {
26 | const link = new DAGLink('hello', 3, uint8ArrayFromString('12208ab7a6c5e74737878ac73863cb76739d15d4666de44e5756bf55a2f9e9ab5f43', 'base16'))
27 |
28 | expect(new CID(link.Hash).toBaseEncodedString())
29 | .to.equal('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
30 | })
31 |
32 | it('fail to create without multihash', () => {
33 | expect(() => {
34 | // @ts-expect-error cid is required
35 | const link = new DAGLink('hello', 3)
36 | expect(link).to.not.exist()
37 | }).to.throw()
38 | })
39 | })
40 |
41 | it('toJSON', () => {
42 | const link = new DAGLink('hello', 3, 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
43 |
44 | expect(link.toJSON()).to.eql({
45 | name: 'hello',
46 | size: 3,
47 | cid: 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U'
48 | })
49 | })
50 |
51 | it('toString', () => {
52 | const link = new DAGLink('hello', 3, 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
53 |
54 | expect(link.toString()).to.equal('DAGLink ')
55 | })
56 |
57 | it('exposes a CID', () => {
58 | const cid = 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U'
59 | const link = new DAGLink('hello', 3, cid)
60 | expect(link.Hash.toBaseEncodedString()).to.equal(cid)
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/test/dag-node-test.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | 'use strict'
3 |
4 | const chai = require('aegir/utils/chai')
5 | const expect = chai.expect
6 |
7 | const dagPB = require('../src')
8 | const DAGLink = require('../src/dag-link/dagLink')
9 | const DAGNode = require('../src/dag-node/dagNode')
10 | const multihashing = require('multihashing-async')
11 | const {
12 | multihash
13 | } = multihashing
14 |
15 | const CID = require('cids')
16 | const multibase = require('multibase')
17 | // @ts-ignore
18 | const loadFixture = require('aegir/utils/fixtures')
19 | const uint8ArrayFromString = require('uint8arrays/from-string')
20 |
21 | const testBlockNamedLinks = loadFixture('test/fixtures/test-block-named-links')
22 | const testBlockUnnamedLinks = loadFixture('test/fixtures/test-block-unnamed-links')
23 |
24 | describe('DAGNode', () => {
25 | it('create a node', () => {
26 | const data = uint8ArrayFromString('some data')
27 |
28 | const node = new DAGNode(data)
29 | expect(node.Data.length).to.be.above(0)
30 | expect(node.Data).to.be.an.instanceOf(Uint8Array)
31 | expect(node.size).to.be.above(0)
32 |
33 | const serialized = dagPB.util.serialize(node)
34 | const deserialized = dagPB.util.deserialize(serialized)
35 | expect(node.Data).to.eql(deserialized.Data)
36 | })
37 |
38 | it('dagPB.util.serialize same as node.serialize()', () => {
39 | const node = new DAGNode(uint8ArrayFromString('some data'))
40 | const serialized = dagPB.util.serialize(node)
41 | expect(serialized).to.eql(node.serialize())
42 | })
43 |
44 | it('create a node with string data', () => {
45 | const data = 'some data'
46 |
47 | const node = new DAGNode(data)
48 | expect(node.Data.length).to.be.above(0)
49 | expect(node.Data).to.be.an.instanceOf(Uint8Array)
50 | expect(node.size).to.be.above(0)
51 |
52 | const serialized = dagPB.util.serialize(node)
53 |
54 | const deserialized = dagPB.util.deserialize(serialized)
55 | expect(node.Data).to.eql(deserialized.Data)
56 | })
57 |
58 | it('create a node with links', () => {
59 | const l1 = [{
60 | Name: 'some other link',
61 | Hash: new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V'),
62 | Tsize: 8
63 | }, {
64 | Name: 'some link',
65 | Hash: new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U'),
66 | Tsize: 10
67 | }]
68 |
69 | const someData = uint8ArrayFromString('some data')
70 |
71 | const node1 = new DAGNode(someData, l1)
72 | const l2 = l1.map((l) => {
73 | return new DAGLink(l.Name, l.Tsize, l.Hash)
74 | })
75 |
76 | const node2 = new DAGNode(someData, l2)
77 | expect(node2.Links).to.containSubset([l1[1], l1[0]])
78 | expect(node1.toJSON()).to.eql(node2.toJSON())
79 |
80 | // check sorting
81 | expect(node1.Links.map((l) => l.Name)).to.be.eql([
82 | 'some link',
83 | 'some other link'
84 | ])
85 | })
86 |
87 | it('create a node with sorted links', () => {
88 | const links = [{
89 | Name: '',
90 | Hash: new CID('QmUGhP2X8xo9dsj45vqx1H6i5WqPqLqmLQsHTTxd3ke8mp'),
91 | Tsize: 262158
92 | }, {
93 | Name: '',
94 | Hash: new CID('QmP7SrR76KHK9A916RbHG1ufy2TzNABZgiE23PjZDMzZXy'),
95 | Tsize: 262158
96 | }, {
97 | Name: '',
98 | Hash: new CID('QmQg1v4o9xdT3Q14wh4S7dxZkDjyZ9ssFzFzyep1YrVJBY'),
99 | Tsize: 262158
100 | }, {
101 | Name: '',
102 | Hash: new CID('QmdP6fartWRrydZCUjHgrJ4XpxSE4SAoRsWJZ1zJ4MWiuf'),
103 | Tsize: 262158
104 | }, {
105 | Name: '',
106 | Hash: new CID('QmNNjUStxtMC1WaSZYiDW6CmAUrvd5Q2e17qnxPgVdwrwW'),
107 | Tsize: 262158
108 | }, {
109 | Name: '',
110 | Hash: new CID('QmWJwqZBJWerHsN1b7g4pRDYmzGNnaMYuD3KSbnpaxsB2h'),
111 | Tsize: 262158
112 | }, {
113 | Name: '',
114 | Hash: new CID('QmRXPSdysBS3dbUXe6w8oXevZWHdPQWaR2d3fggNsjvieL'),
115 | Tsize: 262158
116 | }, {
117 | Name: '',
118 | Hash: new CID('QmTUZAXfws6zrhEksnMqLxsbhXZBQs4FNiarjXSYQqVrjC'),
119 | Tsize: 262158
120 | }, {
121 | Name: '',
122 | Hash: new CID('QmNNk7dTdh8UofwgqLNauq6N78DPc6LKK2yBs1MFdx7Mbg'),
123 | Tsize: 262158
124 | }, {
125 | Name: '',
126 | Hash: new CID('QmW5mrJfyqh7B4ywSvraZgnWjS3q9CLiYURiJpCX3aro5i'),
127 | Tsize: 262158
128 | }, {
129 | Name: '',
130 | Hash: new CID('QmTFHZL5CkgNz19MdPnSuyLAi6AVq9fFp81zmPpaL2amED'),
131 | Tsize: 262158
132 | }]
133 |
134 | const node = new DAGNode(uint8ArrayFromString('some data'), links)
135 | const serialized = node.serialize()
136 | const deserialized = dagPB.util.deserialize(serialized)
137 |
138 | // check sorting
139 | expect(deserialized.Links.map((l) => l.Hash)).to.be.eql(links.map(l => l.Hash))
140 | })
141 |
142 | it('create with empty link name', () => {
143 | const node = new DAGNode(uint8ArrayFromString('hello'), [
144 | new DAGLink('', 10, 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
145 | ])
146 | expect(node.Links[0].Name).to.be.eql('')
147 | })
148 |
149 | it('create with undefined link name', () => {
150 | const node = new DAGNode(uint8ArrayFromString('hello'), [
151 | new DAGLink(undefined, 10, 'QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U')
152 | ])
153 | expect(node.Links[0].Name).to.be.eql('')
154 | const serialized = node.serialize()
155 | const deserialized = dagPB.util.deserialize(serialized)
156 | for (const key of Object.keys(node)) {
157 | if (key !== '_serializedSize') {
158 | // @ts-ignore
159 | expect(node[key]).to.deep.equal(deserialized[key])
160 | }
161 | }
162 | })
163 |
164 | it('create an empty node', () => {
165 | // this node is not in the repo as we don't copy node data to the browser
166 | const node = new DAGNode(new Uint8Array(0))
167 | expect(node.Data.length).to.be.equal(0)
168 | expect(node.Data).to.be.an.instanceOf(Uint8Array)
169 | expect(node.size).to.be.equal(0)
170 |
171 | const serialized = dagPB.util.serialize(node)
172 | const deserialized = dagPB.util.deserialize(serialized)
173 | expect(node.Data).to.eql(deserialized.Data)
174 | })
175 |
176 | it('fail to create a node with other data types', () => {
177 | expect(() => {
178 | // @ts-ignore invalid constructor args
179 | return new DAGNode({})
180 | }).to.throw(
181 | 'Passed \'data\' is not a Uint8Array or a String!'
182 | )
183 | expect(() => {
184 | // @ts-ignore invalid constructor args
185 | return new DAGNode([])
186 | }).to.throw(
187 | 'Passed \'data\' is not a Uint8Array or a String!'
188 | )
189 | })
190 |
191 | it('addLink by DAGNode', async () => {
192 | const node1 = new DAGNode(uint8ArrayFromString('1'))
193 | const node2 = new DAGNode(uint8ArrayFromString('2'))
194 | node1.addLink(await node2.toDAGLink())
195 | expect(node1.Links.length).to.equal(1)
196 | expect(node1.Links[0].Tsize).to.eql(node2.size)
197 | expect(node1.Links[0].Name).to.be.eql('')
198 | })
199 |
200 | it('addLink by DAGLink', async () => {
201 | const node1 = new DAGNode(uint8ArrayFromString('1'))
202 | const node2 = new DAGNode(uint8ArrayFromString('2'))
203 | const link = await node2.toDAGLink()
204 | node1.addLink(link)
205 | expect(node1.Links.length).to.equal(1)
206 | expect(node1.Links[0].Tsize).to.eql(node2.size)
207 | expect(node1.Links[0].Name).to.be.eql('')
208 | })
209 |
210 | it('addLink by object', async () => {
211 | const node1 = new DAGNode(uint8ArrayFromString('1'))
212 | const node2 = new DAGNode(uint8ArrayFromString('2'))
213 | const link = await node2.toDAGLink()
214 | const linkObject = link.toJSON()
215 | // @ts-ignore
216 | node1.addLink(linkObject)
217 | expect(node1.Links.length).to.equal(1)
218 | expect(node1.Links[0].Tsize).to.eql(node2.size)
219 | expect(node1.Links[0].Name).to.be.eql('')
220 | })
221 |
222 | it('addLink by name', async () => {
223 | const node1 = new DAGNode(uint8ArrayFromString('1'))
224 | const node2 = new DAGNode(uint8ArrayFromString('2'))
225 | const link = await node2.toDAGLink({ name: 'banana' })
226 | expect(node1.Links.length).to.equal(0)
227 | node1.addLink(link)
228 | expect(node1.Links.length).to.equal(1)
229 | expect(node1.Links[0].Tsize).to.eql(node2.size)
230 | expect(node1.Links[0].Name).to.eql('banana')
231 | })
232 |
233 | it('addLink - add several links', async () => {
234 | const node1 = new DAGNode(uint8ArrayFromString('1'))
235 | expect(node1.Links.length).to.equal(0)
236 |
237 | const node2 = new DAGNode(uint8ArrayFromString('2'))
238 | node1.addLink(await node2.toDAGLink())
239 | expect(node1.Links.length).to.equal(1)
240 |
241 | const node3 = new DAGNode(uint8ArrayFromString('3'))
242 | node1.addLink(await node3.toDAGLink())
243 | expect(node1.Links.length).to.equal(2)
244 | })
245 |
246 | it('addLink by DAGNode.Links', async () => {
247 | const linkName = 'link-name'
248 | const remote = new DAGNode(uint8ArrayFromString('2'))
249 | const source = new DAGNode(uint8ArrayFromString('1'))
250 | source.addLink(await remote.toDAGLink({ name: linkName }))
251 |
252 | expect(source.Links.length).to.equal(1)
253 |
254 | const target = new DAGNode('', [], 0)
255 | target.addLink(source.Links[0])
256 |
257 | expect(target.Links.length).to.equal(1)
258 | expect(target.Links[0].Tsize).to.eql(remote.size)
259 | expect(target.Links[0].Name).to.be.eql(linkName)
260 | })
261 |
262 | it('rmLink by name', async () => {
263 | const node1 = new DAGNode(uint8ArrayFromString('1'))
264 | expect(node1.Links.length).to.eql(0)
265 | const withoutLink = node1.toJSON()
266 |
267 | const node2 = new DAGNode(uint8ArrayFromString('2'))
268 | const link = await node2.toDAGLink({ name: 'banana' })
269 |
270 | node1.addLink(link)
271 | expect(node1.Links.length).to.eql(1)
272 | node1.rmLink('banana')
273 | expect(node1.Links.length).to.eql(0)
274 | expect(node1.toJSON()).to.eql(withoutLink)
275 | })
276 |
277 | it('rmLink by hash', async () => {
278 | const node1 = new DAGNode(uint8ArrayFromString('1'))
279 | expect(node1.Links.length).to.eql(0)
280 | const withoutLink = node1.toJSON()
281 |
282 | const node2 = new DAGNode(uint8ArrayFromString('2'))
283 | const link = await node2.toDAGLink({ name: 'banana' })
284 |
285 | node1.addLink(link)
286 | expect(node1.Links.length).to.eql(1)
287 | node1.rmLink(node1.Links[0].Hash)
288 | expect(node1.Links.length).to.eql(0)
289 | expect(node1.toJSON()).to.eql(withoutLink)
290 | })
291 |
292 | it('get node CID', async () => {
293 | const node = new DAGNode(uint8ArrayFromString('some data'))
294 | const serialized = dagPB.util.serialize(node)
295 | const cid = await dagPB.util.cid(serialized)
296 | expect(cid.multihash).to.exist()
297 | expect(cid.codec).to.equal('dag-pb')
298 | expect(cid.version).to.equal(1)
299 | const mh = multihash.decode(cid.multihash)
300 | expect(mh.name).to.equal('sha2-256')
301 | })
302 |
303 | it('get node CID with version', async () => {
304 | const node = new DAGNode(uint8ArrayFromString('some data'))
305 | const serialized = dagPB.util.serialize(node)
306 | const cid = await dagPB.util.cid(serialized, { cidVersion: 0 })
307 | expect(cid.multihash).to.exist()
308 | expect(cid.codec).to.equal('dag-pb')
309 | expect(cid.version).to.equal(0)
310 | const mh = multihash.decode(cid.multihash)
311 | expect(mh.name).to.equal('sha2-256')
312 | })
313 |
314 | it('get node CID with hashAlg', async () => {
315 | const node = new DAGNode(uint8ArrayFromString('some data'))
316 | const serialized = dagPB.util.serialize(node)
317 | const cid = await dagPB.util.cid(serialized, { hashAlg: multihash.names['sha2-512'] })
318 | expect(cid.multihash).to.exist()
319 | expect(cid.codec).to.equal('dag-pb')
320 | expect(cid.version).to.equal(1)
321 | const mh = multihash.decode(cid.multihash)
322 | expect(mh.name).to.equal('sha2-512')
323 | })
324 |
325 | it('node size updates with mutation', async () => {
326 | // see pbcross.go for the source of the sizes and CIDs here
327 |
328 | /**
329 | * @param {DAGNode} node
330 | */
331 | async function cid (node) {
332 | const serialized = dagPB.util.serialize(node)
333 | const cid = await dagPB.util.cid(serialized, { cidVersion: 0 })
334 | return cid.toBaseEncodedString()
335 | }
336 |
337 | /**
338 | *
339 | * @param {string} str
340 | */
341 | async function rawBlockCid (str) {
342 | const raw = uint8ArrayFromString(str)
343 | const rawHash = await multihashing(raw, 'sha2-256')
344 | return new CID(1, 'raw', rawHash)
345 | }
346 |
347 | // raw nodes
348 | const rnd1 = await rawBlockCid('aaaa')
349 | const rnd2 = await rawBlockCid('bbbb')
350 | const rnd3 = await rawBlockCid('cccc')
351 |
352 | // empty PB nodes
353 | const pnd1 = new DAGNode()
354 | const pnd2 = new DAGNode()
355 | const pnd3 = new DAGNode()
356 |
357 | // sanity check empty nodes
358 | for (const node of [pnd1, pnd2, pnd3]) {
359 | expect(node.size).to.equal(0)
360 | expect(await cid(node)).to.equal('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n')
361 | }
362 |
363 | // named PB links to a raw nodes
364 | const cat = new DAGLink('cat', 4, rnd1)
365 | const dog = new DAGLink('dog', 4, rnd2)
366 | const bear = new DAGLink('bear', 4, rnd3)
367 |
368 | // pnd1
369 | // links by constructor and addLink should yield the same node
370 | const pnd1ByConstructor = new DAGNode(undefined, [cat])
371 | expect(pnd1ByConstructor.size).to.equal(51)
372 | expect(await cid(pnd1ByConstructor)).to.equal('QmdwjhxpxzcMsR3qUuj7vUL8pbA7MgR3GAxWi2GLHjsKCT')
373 |
374 | pnd1.addLink(cat)
375 | expect(pnd1.size).to.equal(51)
376 | expect(await cid(pnd1)).to.equal('QmdwjhxpxzcMsR3qUuj7vUL8pbA7MgR3GAxWi2GLHjsKCT')
377 |
378 | // pnd2
379 | const pnd1Link = await pnd1.toDAGLink({ name: 'first', cidVersion: 0 })
380 | const pnd2ByConstructor = new DAGNode(undefined, [pnd1Link, dog])
381 | expect(pnd2ByConstructor.size).to.equal(149)
382 | expect(await cid(pnd2ByConstructor)).to.equal('QmWXZxVQ9yZfhQxLD35eDR8LiMRsYtHxYqTFCBbJoiJVys')
383 |
384 | pnd2.addLink(pnd1Link)
385 | pnd2.addLink(dog)
386 | expect(pnd2.size).to.equal(149)
387 | expect(await cid(pnd2)).to.equal('QmWXZxVQ9yZfhQxLD35eDR8LiMRsYtHxYqTFCBbJoiJVys')
388 |
389 | // pnd3
390 | const pnd2Link = await pnd2.toDAGLink({ name: 'second', cidVersion: 0 })
391 | const pnd3ByConstructor = new DAGNode(undefined, [pnd2Link, bear])
392 | expect(pnd3ByConstructor.size).to.equal(250)
393 | expect(await cid(pnd3ByConstructor)).to.equal('QmNX6Tffavsya4xgBi2VJQnSuqy9GsxongxZZ9uZBqp16d')
394 |
395 | pnd3.addLink(pnd2Link)
396 | pnd3.addLink(bear)
397 | expect(pnd3.size).to.equal(250)
398 | expect(await cid(pnd3)).to.equal('QmNX6Tffavsya4xgBi2VJQnSuqy9GsxongxZZ9uZBqp16d')
399 | })
400 |
401 | it('deserialize go-ipfs block with unnamed links', async () => {
402 | const buf = testBlockUnnamedLinks
403 |
404 | const expectedLinks = [
405 | {
406 | name: '',
407 | cid: 'QmSbCgdsX12C4KDw3PDmpBN9iCzS87a5DjgSCoW9esqzXk',
408 | size: 45623854
409 | },
410 | {
411 | name: '',
412 | cid: 'Qma4GxWNhywSvWFzPKtEswPGqeZ9mLs2Kt76JuBq9g3fi2',
413 | size: 45623854
414 | },
415 | {
416 | name: '',
417 | cid: 'QmQfyxyys7a1e3mpz9XsntSsTGc8VgpjPj5BF1a1CGdGNc',
418 | size: 45623854
419 | },
420 | {
421 | name: '',
422 | cid: 'QmSh2wTTZT4N8fuSeCFw7wterzdqbE93j1XDhfN3vQHzDV',
423 | size: 45623854
424 | },
425 | {
426 | name: '',
427 | cid: 'QmVXsSVjwxMsCwKRCUxEkGb4f4B98gXVy3ih3v4otvcURK',
428 | size: 45623854
429 | },
430 | {
431 | name: '',
432 | cid: 'QmZjhH97MEYwQXzCqSQbdjGDhXWuwW4RyikR24pNqytWLj',
433 | size: 45623854
434 | },
435 | {
436 | name: '',
437 | cid: 'QmRs6U5YirCqC7taTynz3x2GNaHJZ3jDvMVAzaiXppwmNJ',
438 | size: 32538395
439 | }
440 | ]
441 |
442 | const node = dagPB.util.deserialize(buf)
443 | const nodeJSON = node.toJSON()
444 | expect(nodeJSON.links).to.eql(expectedLinks)
445 |
446 | const cid = await dagPB.util.cid(buf, { cidVersion: 0 })
447 | expect(cid.toBaseEncodedString()).to.eql(
448 | 'QmQqy2SiEkKgr2cw5UbQ93TtLKEMsD8TdcWggR8q9JabjX')
449 | })
450 |
451 | it('deserialize go-ipfs block with named links', async () => {
452 | const buf = testBlockNamedLinks
453 |
454 | const expectedLinks = [
455 | {
456 | name: 'audio_only.m4a',
457 | cid: 'QmaUAwAQJNtvUdJB42qNbTTgDpzPYD1qdsKNtctM5i7DGB',
458 | size: 23319629
459 | },
460 | {
461 | name: 'chat.txt',
462 | cid: 'QmNVrxbB25cKTRuKg2DuhUmBVEK9NmCwWEHtsHPV6YutHw',
463 | size: 996
464 | },
465 | {
466 | name: 'playback.m3u',
467 | cid: 'QmUcjKzDLXBPmB6BKHeKSh6ZoFZjss4XDhMRdLYRVuvVfu',
468 | size: 116
469 | },
470 | {
471 | name: 'zoom_0.mp4',
472 | cid: 'QmQqy2SiEkKgr2cw5UbQ93TtLKEMsD8TdcWggR8q9JabjX',
473 | size: 306281879
474 | }
475 | ]
476 |
477 | const node = dagPB.util.deserialize(buf)
478 | const nodeJSON = node.toJSON()
479 | expect(nodeJSON.links).to.eql(expectedLinks)
480 |
481 | const cid = await dagPB.util.cid(buf, { cidVersion: 0 })
482 | expect(cid.toBaseEncodedString()).to.eql(
483 | 'QmbSAC58x1tsuPBAoarwGuTQAgghKvdbKSBC8yp5gKCj5M')
484 | })
485 |
486 | it('dagNode.toJSON with empty Node', () => {
487 | const node = new DAGNode(new Uint8Array(0))
488 | expect(node.toJSON().data).to.eql(new Uint8Array(0))
489 | expect(node.toJSON().links).to.eql([])
490 | expect(node.toJSON().size).to.exist()
491 | })
492 |
493 | it('dagNode.toJSON with data no links', () => {
494 | const data = uint8ArrayFromString('La cucaracha')
495 | const node = new DAGNode(data)
496 | expect(node.toJSON().data).to.eql(data)
497 | expect(node.toJSON().links).to.eql([])
498 | expect(node.toJSON().size).to.exist()
499 | })
500 |
501 | it('add two nameless links to a node', () => {
502 | const l1 = {
503 | Name: '',
504 | Hash: 'QmbAmuwox51c91FmC2jEX5Ng4zS4HyVgpA5GNPBF5QsWMA',
505 | Tsize: 57806
506 | }
507 |
508 | const l2 = {
509 | Name: '',
510 | Hash: 'QmP7SrR76KHK9A916RbHG1ufy2TzNABZgiE23PjZDMzZXy',
511 | Tsize: 262158
512 | }
513 | const link1 = new DAGLink(
514 | l1.Name,
515 | l1.Tsize,
516 | multibase.decode('z' + l1.Hash)
517 | )
518 | const link2 = new DAGLink(
519 | l2.Name,
520 | l2.Tsize,
521 | multibase.decode('z' + l2.Hash)
522 | )
523 |
524 | const node = new DAGNode(uint8ArrayFromString('hiya'), [link1, link2])
525 | expect(node.Links).to.have.lengthOf(2)
526 | })
527 |
528 | it('toString', () => {
529 | const node = new DAGNode(uint8ArrayFromString('hello world'))
530 | const expected = 'DAGNode '
531 | expect(node.toString()).to.equal(expected)
532 | })
533 |
534 | it('deserializing a node and an object should yield the same result', () => {
535 | const obj = {
536 | Data: uint8ArrayFromString('Hello World'),
537 | Links: [{
538 | Hash: new CID('QmUxD5gZfKzm8UN4WaguAMAZjw2TzZ2ZUmcqm2qXPtais7'),
539 | Name: 'payload',
540 | Tsize: 819
541 | }]
542 | }
543 |
544 | const node = new DAGNode(obj.Data, obj.Links)
545 | expect(node.Data.length).to.be.above(0)
546 | expect(node.Data).to.be.an.instanceOf(Uint8Array)
547 | expect(node.size).to.be.above(0)
548 |
549 | const serialized = dagPB.util.serialize(node)
550 | // @ts-ignore this is not part of interface-ipld-format
551 | const serializedObject = dagPB.util.serialize(obj)
552 | const deserialized = dagPB.util.deserialize(serialized)
553 | const deserializedObject = dagPB.util.deserialize(serializedObject)
554 | expect(deserialized.toJSON()).to.deep.equal(deserializedObject.toJSON())
555 | })
556 |
557 | it('creates links from objects with .Size properties', () => {
558 | const node = new DAGNode(uint8ArrayFromString('some data'), [{
559 | // @ts-ignore
560 | Hash: 'QmUxD5gZfKzm8UN4WaguAMAZjw2TzZ2ZUmcqm2qXPtais7',
561 | Size: 9001
562 | }])
563 |
564 | expect(node.Links[0].Tsize).to.eql(9001)
565 | })
566 |
567 | it('serializing should set _serializedSize', () => {
568 | const node = new DAGNode('hello')
569 |
570 | expect(node._serializedSize).to.be.null()
571 |
572 | node.serialize()
573 |
574 | expect(node._serializedSize).to.be.greaterThan(0)
575 | })
576 |
577 | it('removes dag link by uint8array CID', () => {
578 | const cid = new CID('QmUxD5gZfKzm8UN4WaguAMAZjw2TzZ2ZUmcqm2qXPtais7')
579 |
580 | const node = new DAGNode('hello world', [{
581 | Hash: cid,
582 | Tsize: 5,
583 | Name: ''
584 | }])
585 |
586 | expect(node).to.have.property('Links').that.has.lengthOf(1)
587 |
588 | // @ts-ignore only supposed to support CIDs or strings or DAGLinks
589 | node.rmLink(cid.bytes)
590 |
591 | expect(node).to.have.property('Links').that.has.lengthOf(0)
592 | })
593 | })
594 |
--------------------------------------------------------------------------------
/test/fixtures/test-block-named-links:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ipld/js-ipld-dag-pb/6b0e011b7917611386cff392d56bfd81c8cacf8c/test/fixtures/test-block-named-links
--------------------------------------------------------------------------------
/test/fixtures/test-block-unnamed-links:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ipld/js-ipld-dag-pb/6b0e011b7917611386cff392d56bfd81c8cacf8c/test/fixtures/test-block-unnamed-links
--------------------------------------------------------------------------------
/test/mod.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | 'use strict'
3 |
4 | const chai = require('aegir/utils/chai')
5 | const expect = chai.expect
6 | const multicodec = require('multicodec')
7 |
8 | const mod = require('../src')
9 |
10 | describe('IPLD Format)', () => {
11 | it('codec is dag-pb', () => {
12 | expect(mod.codec).to.equal(multicodec.DAG_PB)
13 | })
14 |
15 | it('defaultHashAlg is sha2-256', () => {
16 | expect(mod.defaultHashAlg).to.equal(multicodec.SHA2_256)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/resolver.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 |
3 | 'use strict'
4 |
5 | const chai = require('aegir/utils/chai')
6 | const expect = chai.expect
7 | const CID = require('cids')
8 |
9 | const { resolver } = require('../src')
10 | const DAGNode = require('../src/dag-node/dagNode')
11 | const utils = require('../src/util')
12 | const uint8ArrayFromString = require('uint8arrays/from-string')
13 |
14 | /**
15 | * @typedef {import('../src/dag-link/dagLink')} DAGLink
16 | * @typedef {import('../src/types').DAGLinkLike} DAGLinkLike
17 | */
18 |
19 | describe('IPLD Format resolver (local)', () => {
20 | const links = [{
21 | Name: '',
22 | Hash: new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39U'),
23 | Tsize: 10
24 | }, {
25 | Name: 'named link',
26 | Hash: new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V'),
27 | Tsize: 8
28 | }]
29 |
30 | /**
31 | * @param {Uint8Array} data
32 | * @param {(DAGLink | DAGLinkLike)[]} links
33 | */
34 | const create = (data, links) => {
35 | const node = new DAGNode(data, links)
36 | return utils.serialize(node)
37 | }
38 |
39 | /**
40 | * @param {Uint8Array} data
41 | * @param {(DAGLink | DAGLinkLike)[]} links
42 | */
43 | const createPlain = (data, links) => {
44 | const node = {
45 | Data: data,
46 | Links: links
47 | }
48 | return utils.serialize(node)
49 | }
50 |
51 | const emptyNodeBlobs = [
52 | { kind: 'DAGNode', blob: create(new Uint8Array(), []) },
53 | { kind: '{data:Uint8Array}', blob: createPlain(new Uint8Array(), []) }
54 | ]
55 |
56 | const linksNodeBlobs = [
57 | { kind: 'DAGNode', blob: create(new Uint8Array(), links) },
58 | { kind: '{data:Uint8Array}', blob: createPlain(new Uint8Array(), links) }
59 | ]
60 |
61 | const dataLinksNodeBlobs = [
62 | { kind: 'DAGNode', blob: create(uint8ArrayFromString('aaah the data'), links) },
63 | { kind: '{data:Uint8Array}', blob: createPlain(uint8ArrayFromString('aaah the data'), links) }
64 | ]
65 |
66 | for (const { kind, blob } of emptyNodeBlobs) {
67 | describe(`empty node (${kind})`, () => {
68 | describe('resolver.resolve', () => {
69 | it('links path', () => {
70 | const result = resolver.resolve(blob, 'Links')
71 | expect(result).to.have.deep.property('value', [])
72 | expect(result).to.have.property('remainderPath', '')
73 | })
74 |
75 | it('data path', () => {
76 | const result = resolver.resolve(blob, 'Data')
77 | expect(result).to.have.property('value').that.is.an.instanceOf(Uint8Array).with.lengthOf(0)
78 | expect(result).to.have.property('remainderPath', '')
79 | })
80 |
81 | it('non existent path', () => {
82 | expect(() =>
83 | resolver.resolve(blob, 'pathThatDoesNotExist')
84 | ).to.throw(
85 | "Object has no property 'pathThatDoesNotExist'"
86 | )
87 | })
88 |
89 | it('empty path', () => {
90 | const result = resolver.resolve(blob, '')
91 | expect(result).to.have.nested.property('value.Data').that.is.an.instanceOf(Uint8Array).with.lengthOf(0)
92 | expect(result).to.have.deep.nested.property('value.Links', [])
93 | expect(result).to.have.property('remainderPath', '')
94 | })
95 | })
96 |
97 | it('resolver.tree', () => {
98 | const tree = resolver.tree(blob)
99 | const paths = [...tree]
100 | expect(paths).to.have.members([
101 | 'Links',
102 | 'Data'
103 | ])
104 | })
105 | })
106 | }
107 |
108 | for (const { kind, blob } of linksNodeBlobs) {
109 | describe(`links node ${kind}`, () => {
110 | describe('resolver.resolve', () => {
111 | it('links path', () => {
112 | const result = resolver.resolve(blob, 'Links')
113 | expect(result).to.have.property('value').that.containSubset(links)
114 | expect(result).to.have.property('remainderPath', '')
115 | })
116 |
117 | it('links position path Hash', () => {
118 | const result = resolver.resolve(blob, 'Links/1/Hash')
119 | expect(result).to.have.deep.property('value', links[1].Hash)
120 | expect(result).to.have.property('remainderPath', '')
121 | })
122 |
123 | it('links position path Name', () => {
124 | const result = resolver.resolve(blob, 'Links/1/Name')
125 | expect(result).to.have.property('value', links[1].Name)
126 | expect(result).to.have.property('remainderPath', '')
127 | })
128 |
129 | it('links position path Tsize', () => {
130 | const result = resolver.resolve(blob, 'Links/1/Tsize')
131 | expect(result).to.have.property('value', links[1].Tsize)
132 | expect(result).to.have.property('remainderPath', '')
133 | })
134 |
135 | it('links by name', () => {
136 | const result = resolver.resolve(blob, 'named link')
137 | expect(result).to.have.deep.property('value', links[1].Hash)
138 | expect(result).to.have.property('remainderPath', '')
139 | })
140 |
141 | it('missing link by name', () => {
142 | expect(() =>
143 | resolver.resolve(blob, 'missing link')
144 | ).to.throw(
145 | "Object has no property 'missing link'"
146 | )
147 | })
148 |
149 | it('yield remainderPath if impossible to resolve through (a)', () => {
150 | const result = resolver.resolve(blob, 'Links/1/Hash/Data')
151 | expect(result).to.have.deep.property('value', new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V'))
152 | expect(result).to.have.property('remainderPath', 'Data')
153 | })
154 |
155 | it('yield remainderPath if impossible to resolve through (b)', () => {
156 | const result = resolver.resolve(blob, 'Links/1/Hash/Links/0/Hash/Data')
157 | expect(result).to.have.deep.property('value', new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V'))
158 | expect(result).to.have.property('remainderPath', 'Links/0/Hash/Data')
159 | })
160 |
161 | it('yield remainderPath if impossible to resolve through named link (a)', () => {
162 | const result = resolver.resolve(blob, 'named link/Data')
163 | expect(result).to.have.deep.property('value', new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V'))
164 | expect(result).to.have.property('remainderPath', 'Data')
165 | })
166 |
167 | it('yield remainderPath if impossible to resolve through named link (b)', () => {
168 | const result = resolver.resolve(blob, 'named link/Links/0/Hash/Data')
169 | expect(result).to.have.deep.property('value', new CID('QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V'))
170 | expect(result).to.have.property('remainderPath', 'Links/0/Hash/Data')
171 | })
172 | })
173 |
174 | it('resolver.tree', () => {
175 | const tree = resolver.tree(blob)
176 | const paths = [...tree]
177 | expect(paths).to.have.members([
178 | 'Links',
179 | 'Links/0',
180 | 'Links/0/Name',
181 | 'Links/0/Tsize',
182 | 'Links/0/Hash',
183 | 'Links/1',
184 | 'Links/1/Name',
185 | 'Links/1/Tsize',
186 | 'Links/1/Hash',
187 | 'Data'
188 | ])
189 | })
190 | })
191 | }
192 |
193 | for (const { kind, blob } of dataLinksNodeBlobs) {
194 | describe(`links and data node (${kind})`, () => {
195 | describe('resolver.resolve', () => {
196 | it('links path', () => {
197 | const result = resolver.resolve(blob, 'Links')
198 | expect(result.value).to.containSubset(links)
199 | expect(result.remainderPath).to.eql('')
200 | })
201 |
202 | it('data path', () => {
203 | const result = resolver.resolve(blob, 'Data')
204 | expect(result.value).to.eql(uint8ArrayFromString('aaah the data'))
205 | expect(result.remainderPath).to.eql('')
206 | })
207 |
208 | it('non existent path', () => {
209 | expect(() =>
210 | resolver.resolve(blob, 'pathThatDoesNotExist')
211 | ).to.throw(
212 | "Object has no property 'pathThatDoesNotExist'"
213 | )
214 | })
215 |
216 | it('empty path', () => {
217 | const result = resolver.resolve(blob, '')
218 | expect(result).to.have.deep.nested.property('value.Data', uint8ArrayFromString('aaah the data'))
219 | expect(result).to.have.nested.property('value.Links').that.containSubset(links)
220 | expect(result.remainderPath).to.eql('')
221 | })
222 | })
223 |
224 | it('resolver.tree', () => {
225 | const tree = resolver.tree(blob)
226 | const paths = [...tree]
227 | expect(paths).to.have.members([
228 | 'Links',
229 | 'Links/0',
230 | 'Links/0/Name',
231 | 'Links/0/Tsize',
232 | 'Links/0/Hash',
233 | 'Links/1',
234 | 'Links/1/Name',
235 | 'Links/1/Tsize',
236 | 'Links/1/Hash',
237 | 'Data'
238 | ])
239 | })
240 | })
241 | }
242 | })
243 |
--------------------------------------------------------------------------------
/test/util.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 |
3 | 'use strict'
4 |
5 | const CID = require('cids')
6 | const chai = require('aegir/utils/chai')
7 | const expect = chai.expect
8 | const {
9 | multihash
10 | } = require('multihashing-async')
11 |
12 | const DAGLink = require('../src/dag-link/dagLink')
13 | const {
14 | serialize,
15 | deserialize,
16 | cid
17 | } = require('../src/util')
18 |
19 | describe('util', () => {
20 | it('should serialize an empty node', () => {
21 | const result = serialize({})
22 | expect(result).to.be.an.instanceof(Uint8Array)
23 | expect(result).to.be.empty()
24 | })
25 |
26 | it('should serialize a node with data', () => {
27 | const data = Uint8Array.from([0, 1, 2, 3])
28 | const result = serialize({ Data: data })
29 | expect(result).to.be.an.instanceof(Uint8Array)
30 |
31 | const node = deserialize(result)
32 | expect(node.Data).to.deep.equal(data)
33 | })
34 |
35 | it('should serialize a node with Uint8Array data', () => {
36 | const data = Uint8Array.from([0, 1, 2, 3])
37 | const result = serialize({ Data: data })
38 | expect(result).to.be.an.instanceof(Uint8Array)
39 |
40 | const node = deserialize(result)
41 | expect(node.Data).to.deep.equal(Uint8Array.from([0, 1, 2, 3]))
42 | })
43 |
44 | it('should serialize a node with links', () => {
45 | const links = [
46 | new DAGLink('', 0, 'QmWDtUQj38YLW8v3q4A6LwPn4vYKEbuKWpgSm6bjKW6Xfe')
47 | ]
48 | const result = serialize({ Links: links })
49 | expect(result).to.be.an.instanceof(Uint8Array)
50 |
51 | const node = deserialize(result)
52 | expect(node.Links).to.containSubset([{
53 | Name: '',
54 | Tsize: 0,
55 | Hash: new CID('QmWDtUQj38YLW8v3q4A6LwPn4vYKEbuKWpgSm6bjKW6Xfe')
56 | }])
57 | })
58 |
59 | it('should serialize a node with links as plain objects', () => {
60 | const links = [{
61 | Name: '',
62 | Tsize: 0,
63 | Hash: new CID('QmWDtUQj38YLW8v3q4A6LwPn4vYKEbuKWpgSm6bjKW6Xfe')
64 | }]
65 | const result = serialize({ Links: links })
66 | expect(result).to.be.an.instanceof(Uint8Array)
67 |
68 | const node = deserialize(result)
69 | expect(node.Links).to.containSubset(links)
70 | })
71 |
72 | it('should ignore invalid properties when serializing', () => {
73 | // @ts-ignore invalid properties
74 | const result = serialize({ foo: 'bar' })
75 | expect(result).to.be.empty()
76 | })
77 |
78 | describe('cid', () => {
79 | it('should allow the identity hash', async () => {
80 | const buffer = serialize({ Data: new Uint8Array(0), Links: [] })
81 | const id = await cid(buffer, {
82 | hashAlg: multihash.names.identity
83 | })
84 |
85 | const result = multihash.decode(id.multihash)
86 |
87 | expect(result).to.have.property('name', 'identity')
88 | })
89 | })
90 | })
91 |
--------------------------------------------------------------------------------
/tools/pb-cross-language.go:
--------------------------------------------------------------------------------
1 | // For testing cross-language PB data to make sure we're producing the same thing
2 | // `go get github.com/ipfs/go-merkledag`
3 | // `go run pb-cross-language.go`
4 | /*
5 | pnd1 cid: QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n, size: 0
6 | pnd2 cid: QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n, size: 0
7 | pnd3 cid: QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n, size: 0
8 | pnd1 cid: QmdwjhxpxzcMsR3qUuj7vUL8pbA7MgR3GAxWi2GLHjsKCT, size: 51
9 | pnd2 cid: QmWXZxVQ9yZfhQxLD35eDR8LiMRsYtHxYqTFCBbJoiJVys, size: 149
10 | pnd3 cid: QmNX6Tffavsya4xgBi2VJQnSuqy9GsxongxZZ9uZBqp16d, size: 250
11 | */
12 |
13 | // based on test data from https://github.com/ipfs/go-car
14 |
15 | package main
16 |
17 | import (
18 | "fmt"
19 |
20 | dag "github.com/ipfs/go-merkledag"
21 | )
22 |
23 | func main() {
24 | var rnd1 = dag.NewRawNode([]byte("aaaa"))
25 | var rnd2 = dag.NewRawNode([]byte("bbbb"))
26 | var rnd3 = dag.NewRawNode([]byte("cccc"))
27 |
28 | var pnd1 = &dag.ProtoNode{}
29 | var pnd2 = &dag.ProtoNode{}
30 | var pnd3 = &dag.ProtoNode{}
31 |
32 | sizer := func(pnd *dag.ProtoNode) (size uint64) { size, _ = pnd.Size(); return size }
33 |
34 | fmt.Printf("pnd1 cid: %s, size: %d\n", pnd1.Cid(), sizer(pnd1))
35 | fmt.Printf("pnd2 cid: %s, size: %d\n", pnd2.Cid(), sizer(pnd2))
36 | fmt.Printf("pnd3 cid: %s, size: %d\n", pnd3.Cid(), sizer(pnd3))
37 |
38 | pnd1.AddNodeLink("cat", rnd1)
39 | pnd2.AddNodeLink("first", pnd1)
40 | pnd2.AddNodeLink("dog", rnd2)
41 | pnd3.AddNodeLink("second", pnd2)
42 | pnd3.AddNodeLink("bear", rnd3)
43 |
44 | fmt.Printf("pnd1 cid: %s, size: %d\n", pnd1.Cid(), sizer(pnd1))
45 | fmt.Printf("pnd2 cid: %s, size: %d\n", pnd2.Cid(), sizer(pnd2))
46 | fmt.Printf("pnd3 cid: %s, size: %d\n", pnd3.Cid(), sizer(pnd3))
47 | }
48 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "aegir/src/config/tsconfig.aegir.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "test",
8 | "src"
9 | ],
10 | "exclude": [
11 | "src/dag.js" // exclude generated file
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------