├── .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 | [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) 7 | [![](https://img.shields.io/badge/project-IPLD-blue.svg?style=flat-square)](http://github.com/ipld/ipld) 8 | [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) 9 | [![Travis CI](https://flat.badgen.net/travis/ipld/js-ipld-dag-pb)](https://travis-ci.com/ipld/js-ipld-dag-pb) 10 | [![Coverage Status](https://coveralls.io/repos/github/ipld/js-ipld-dag-pb/badge.svg?branch=master)](https://coveralls.io/github/ipld/js-ipld-dag-pb?branch=master) 11 | [![Dependency Status](https://david-dm.org/ipld/js-ipld-dag-pb.svg?style=flat-square)](https://david-dm.org/ipld/js-ipld-dag-pb) 12 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) 13 | [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) 14 | [![Greenkeeper badge](https://badges.greenkeeper.io/ipld/js-ipld-dag-pb.svg)](https://greenkeeper.io/) 15 | ![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square) 16 | ![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square) 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 { 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 | --------------------------------------------------------------------------------