diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 43f463c6..00000000 --- a/.eslintrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": [ - "standard", - "plugin:@typescript-eslint/recommended" - ], - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/ban-ts-comment": "off" - } -} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f4689933..0bc3b42d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,16 +1,11 @@ version: 2 updates: - - package-ecosystem: 'github-actions' - directory: '/' - schedule: - interval: 'daily' - commit-message: - prefix: 'chore' - include: 'scope' - - package-ecosystem: 'npm' - directory: '/' - schedule: - interval: 'daily' - commit-message: - prefix: 'chore' - include: 'scope' +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 10 + commit-message: + prefix: "deps" + prefix-development: "deps(dev)" diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000..d57c2a02 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,8 @@ +name: Automerge +on: [ pull_request ] + +jobs: + automerge: + uses: protocol/.github/.github/workflows/automerge.yml@master + with: + job: 'automerge' diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml new file mode 100644 index 00000000..5025f7c9 --- /dev/null +++ b/.github/workflows/js-test-and-release.yml @@ -0,0 +1,139 @@ +name: test & maybe release +on: [push, pull_request] + +jobs: + + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present lint + - run: npm run --if-present dep-check + + test-node: + needs: check + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + node: [16] + fail-fast: true + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node }} + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:node + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: node + + test-chrome: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:chrome + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: chrome + + test-chrome-webworker: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:chrome-webworker + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: chrome-webworker + + test-firefox: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:firefox + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: firefox + + test-firefox-webworker: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npm run --if-present test:firefox-webworker + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: firefox-webworker + + test-electron-main: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npx xvfb-maybe npm run --if-present test:electron-main + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: electron-main + + test-electron-renderer: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - run: npx xvfb-maybe npm run --if-present test:electron-renderer + - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 + with: + flags: electron-renderer + + release: + needs: [test-node, test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-electron-main, test-electron-renderer] + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + - uses: ipfs/aegir/actions/cache-node-modules@master + - uses: ipfs/aegir/actions/docker-login@master + with: + docker-token: ${{ secrets.DOCKER_TOKEN }} + docker-username: ${{ secrets.DOCKER_USERNAME }} + - run: npm run --if-present release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test-and-release.yml b/.github/workflows/test-and-release.yml deleted file mode 100644 index c2f9eb50..00000000 --- a/.github/workflows/test-and-release.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Test & Maybe Release -on: [push, pull_request] -jobs: - test: - strategy: - fail-fast: false - matrix: - node: [14.x, 16.x] - os: [macos-latest, ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout Repository - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v3.5.0 - with: - node-version: ${{ matrix.node }} - - name: Install Dependencies - run: | - npm install --no-progress - - name: Run tests - run: | - npm config set script-shell bash - npm run test:ci - release: - name: Release - needs: test - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v3.5.0 - with: - node-version: 14 - - name: Install dependencies - run: | - npm install --no-progress --no-package-lock --no-save - - name: Build - run: | - npm run build - - name: Install plugins - run: | - npm install \ - @semantic-release/commit-analyzer \ - conventional-changelog-conventionalcommits \ - @semantic-release/release-notes-generator \ - @semantic-release/npm \ - @semantic-release/github \ - @semantic-release/git \ - @semantic-release/changelog \ - --no-progress --no-package-lock --no-save - - name: Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npx semantic-release - diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 6b6f96ae..facca92b 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -26,4 +26,4 @@ jobs: - name: Typecheck uses: gozala/typescript-error-reporter-action@v1.0.8 with: - project: test/tsconfig.json + project: tsconfig.json diff --git a/.gitignore b/.gitignore index 4f8f0207..75adb8e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,9 @@ -.nyc_output -build -dist -coverage +.coverage package-lock.json node_modules .DS_Store yarn.lock types test/ts-use/tsconfig.tsbuildinfo -test/tsconfig.tsbuildinfo +types/tsconfig.tsbuildinfo +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..20ce483c --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +This project is dual licensed under MIT and Apache-2.0. + +MIT: https://www.opensource.org/licenses/mit +Apache-2.0: https://www.apache.org/licenses/license-2.0 diff --git a/LICENSE-APACHE b/LICENSE-APACHE index fcd6df33..14478a3b 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -1,13 +1,5 @@ -Copyright 2020 Protocol Labs +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index 9bddb3e8..72dc60d8 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,7 +1,19 @@ -Copyright 2020 Protocol Labs +The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 613af3dd..5515e280 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,40 @@ -# multiformats - -* [Interfaces](#interfaces) - * [Creating Blocks](#creating-blocks) - * [Multibase Encoders / Decoders / Codecs](#multibase-encoders--decoders--codecs) - * [Multicodec Encoders / Decoders / Codecs](#multicodec-encoders--decoders--codecs) - * [Multihash Hashers](#multihash-hashers) - * [Traversal](#traversal) -* [Legacy interface](#legacy-interface) -* [Implementations](#implementations) - * [Multibase codecs](#multibase-codecs) - * [Multihash hashers](#multihash-hashers-1) - * [IPLD codecs (multicodec)](#ipld-codecs-multicodec) -* [License](#license) - * [Contribution](#contribution) +# multiformats + +[![multiformats.io](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://multiformats.io) +[![codecov](https://img.shields.io/codecov/c/github/multiformats/js-multiformats.svg?style=flat-square)](https://codecov.io/gh/multiformats/js-multiformats) +[![CI](https://img.shields.io/github/workflow/status/multiformats/js-multiformats/test%20&%20maybe%20release/esm-migration?style=flat-square)](https://github.com/multiformats/js-multiformats/actions/workflows/js-test-and-release.yml) + +> Interface for multihash, multicodec, multibase and CID + +## Table of contents + +- [Install](#install) +- [Interfaces](#interfaces) + - [Creating Blocks](#creating-blocks) + - [Multibase Encoders / Decoders / Codecs](#multibase-encoders--decoders--codecs) + - [Multicodec Encoders / Decoders / Codecs](#multicodec-encoders--decoders--codecs) + - [Multihash Hashers](#multihash-hashers) + - [Traversal](#traversal) +- [Legacy interface](#legacy-interface) +- [Implementations](#implementations) + - [Multibase codecs](#multibase-codecs) + - [Multihash hashers](#multihash-hashers-1) + - [IPLD codecs (multicodec)](#ipld-codecs-multicodec) +- [License](#license) +- [Contribution](#contribution) + +## Install + +```console +$ npm i multiformats +``` + +## Interfaces This library defines common interfaces and low level building blocks for various interrelated multiformat technologies (multicodec, multihash, multibase, and CID). They can be used to implement custom base encoders / decoders / codecs, codec encoders /decoders and multihash hashers that comply to the interface that layers above assume. This library provides implementations for most basics and many others can be found in linked repositories. -## Interfaces - ```js import { CID } from 'multiformats/cid' import * as json from 'multiformats/codecs/json' @@ -77,7 +92,6 @@ them as `encoder` and `decoder` properties. For added convenience codecs also implement `MultibaseEncoder` and `MultibaseDecoder` interfaces so they could be used as either or both: - ```js cid.toString(base64) CID.parse(cid.toString(base64), base64) @@ -189,43 +203,43 @@ import the ones you need yourself. ### Multibase codecs -| bases | import | repo | - --- | --- | --- | -`base16` | `multiformats/bases/base16` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | -`base32`, `base32pad`, `base32hex`, `base32hexpad`, `base32z` | `multiformats/bases/base32` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | -`base64`, `base64pad`, `base64url`, `base64urlpad` | `multiformats/bases/base64` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | -`base58btc`, `base58flick4` | `multiformats/bases/base58` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | +| bases | import | repo | +| ------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------- | +| `base16` | `multiformats/bases/base16` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | +| `base32`, `base32pad`, `base32hex`, `base32hexpad`, `base32z` | `multiformats/bases/base32` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | +| `base64`, `base64pad`, `base64url`, `base64urlpad` | `multiformats/bases/base64` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | +| `base58btc`, `base58flick4` | `multiformats/bases/base58` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) | Other (less useful) bases implemented in [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) include: `base2`, `base8`, `base10`, `base36` and `base256emoji`. ### Multihash hashers -| hashes | import | repo | -| --- | --- | --- | -| `sha2-256`, `sha2-512` | `multiformats/hashes/sha2` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/hashes) | -| `sha3-224`, `sha3-256`, `sha3-384`,`sha3-512`, `shake-128`, `shake-256`, `keccak-224`, `keccak-256`, `keccak-384`, `keccak-512` | `@multiformats/sha3` | [multiformats/js-sha3](https://github.com/multiformats/js-sha3) | -| `identity` | `multiformats/hashes/identity` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/hashes/identity.js) | -| `murmur3-128`, `murmur3-32` | `@multiformats/murmur3` | [multiformats/js-murmur3](https://github.com/multiformats/js-murmur3) | -| `blake2b-*`, `blake2s-*` | `@multiformats/blake2` | [multiformats/js-blake2](https://github.com/multiformats/js-blake2) | +| hashes | import | repo | +| ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `sha2-256`, `sha2-512` | `multiformats/hashes/sha2` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/hashes) | +| `sha3-224`, `sha3-256`, `sha3-384`,`sha3-512`, `shake-128`, `shake-256`, `keccak-224`, `keccak-256`, `keccak-384`, `keccak-512` | `@multiformats/sha3` | [multiformats/js-sha3](https://github.com/multiformats/js-sha3) | +| `identity` | `multiformats/hashes/identity` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/hashes/identity.js) | +| `murmur3-128`, `murmur3-32` | `@multiformats/murmur3` | [multiformats/js-murmur3](https://github.com/multiformats/js-murmur3) | +| `blake2b-*`, `blake2s-*` | `@multiformats/blake2` | [multiformats/js-blake2](https://github.com/multiformats/js-blake2) | ### IPLD codecs (multicodec) -| codec | import | repo | -| --- | --- | --- | -| `raw` | `multiformats/codecs/raw` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/codecs) | -| `json` | `multiformats/codecs/json` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/codecs) | -| `dag-cbor` | `@ipld/dag-cbor` | [ipld/js-dag-cbor](https://github.com/ipld/js-dag-cbor) | -| `dag-json` | `@ipld/dag-json` | [ipld/js-dag-json](https://github.com/ipld/js-dag-json) | -| `dag-pb` | `@ipld/dag-pb` | [ipld/js-dag-pb](https://github.com/ipld/js-dag-pb) | -| `dag-jose` | `dag-jose`| [ceramicnetwork/js-dag-jose](https://github.com/ceramicnetwork/js-dag-jose) | +| codec | import | repo | +| ---------- | -------------------------- | ------------------------------------------------------------------------------------------------------ | +| `raw` | `multiformats/codecs/raw` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/codecs) | +| `json` | `multiformats/codecs/json` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/src/codecs) | +| `dag-cbor` | `@ipld/dag-cbor` | [ipld/js-dag-cbor](https://github.com/ipld/js-dag-cbor) | +| `dag-json` | `@ipld/dag-json` | [ipld/js-dag-json](https://github.com/ipld/js-dag-json) | +| `dag-pb` | `@ipld/dag-pb` | [ipld/js-dag-pb](https://github.com/ipld/js-dag-pb) | +| `dag-jose` | `dag-jose` | [ceramicnetwork/js-dag-jose](https://github.com/ceramicnetwork/js-dag-jose) | ## License Licensed under either of - * Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / http://www.apache.org/licenses/LICENSE-2.0) - * MIT ([LICENSE-MIT](LICENSE-MIT) / http://opensource.org/licenses/MIT) +- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) +- MIT ([LICENSE-MIT](LICENSE-MIT) / ) -### Contribution +## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/package.json b/package.json index c61b9dae..90123e01 100644 --- a/package.json +++ b/package.json @@ -1,159 +1,168 @@ { "name": "multiformats", - "version": "0.0.0-dev", + "version": "10.0.0-pre.4", "description": "Interface for multihash, multicodec, multibase and CID", - "main": "./src/index.js", - "types": "./types/src/index.d.ts", - "type": "module", - "scripts": { - "build": "npm run build:js && npm run build:types", - "build:js": "ipjs build --tests --main && npm run build:copy", - "build:copy": "cp -a tsconfig.json src vendor test dist/ && rm -rf dist/test/ts-use", - "build:types": "npm run build:copy && cd dist && tsc --build", - "build:vendor": "npm run build:vendor:varint && npm run build:vendor:base-x", - "build:vendor:varint": "npm_config_yes=true npx brrp -x varint > vendor/varint.js", - "build:vendor:base-x": "npm_config_yes=true npx brrp -x @multiformats/base-x > vendor/base-x.js", - "lint": "standard", - "test:cjs": "npm run build:js && mocha dist/cjs/node-test/test-*.js && npm run test:cjs:browser", - "test:esm": "npm run build:js && mocha dist/esm/node-test/test-*.js && npm run test:esm:browser", - "test:node": "c8 --check-coverage --branches 100 --functions 100 --lines 100 mocha test/test-*.js", - "test:cjs:browser": "polendina --page --worker --serviceworker --cleanup dist/cjs/browser-test/test-*.js", - "test:esm:browser": "polendina --page --worker --serviceworker --cleanup dist/esm/browser-test/test-*.js", - "test:ts": "npm run build:types && npm run test --prefix test/ts-use", - "test": "npm run lint && npm run test:node && npm run test:esm && npm run test:ts", - "test:ci": "npm run lint && npm run test:node && npm run test:esm && npm run test:cjs && npm run test:ts", - "coverage": "c8 --reporter=html mocha test/test-*.js && npm_config_yes=true npx st -d coverage -p 8080" + "author": "Mikeal Rogers (https://www.mikealrogers.com/)", + "license": "Apache-2.0 OR MIT", + "homepage": "https://github.com/multiformats/js-multiformats#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/multiformats/js-multiformats.git" }, - "c8": { - "exclude": [ - "test/**", - "vendor/**" - ] + "bugs": { + "url": "https://github.com/multiformats/js-multiformats/issues" }, "keywords": [ "ipfs", "ipld", "multiformats" ], - "author": "Mikeal Rogers (https://www.mikealrogers.com/)", - "license": "(Apache-2.0 AND MIT)", + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + }, + "type": "module", + "types": "./dist/types/src/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "*", + "dist/types/*", + "dist/types/src/*", + "dist/types/src/*/index" + ], + "src/*": [ + "*", + "dist/types/*", + "dist/types/src/*", + "dist/types/src/*/index" + ] + } + }, + "files": [ + "CHANGELOG.md", + "examples", + "LICENSE*", + "src", + "test", + "tsconfig.json", + "dist/types", + "vendor", + "!**/*.tsbuildinfo", + "!test/ts-use/node_modules" + ], "exports": { ".": { + "types": "./dist/types/src/index.d.ts", "import": "./src/index.js" }, - "./cid": { - "import": "./src/cid.js" - }, - "./basics": { - "import": "./src/basics.js" - }, - "./block": { - "import": "./src/block.js" - }, - "./traversal": { - "import": "./src/traversal.js" - }, - "./bases/identity": { - "import": "./src/bases/identity.js" - }, - "./bases/base2": { - "import": "./src/bases/base2.js" - }, - "./bases/base8": { - "import": "./src/bases/base8.js" - }, "./bases/base10": { + "types": "./dist/types/src/bases/base10.d.ts", "import": "./src/bases/base10.js" }, "./bases/base16": { + "types": "./dist/types/src/bases/base16.d.ts", "import": "./src/bases/base16.js" }, + "./bases/base2": { + "types": "./dist/types/src/bases/base2.d.ts", + "import": "./src/bases/base2.js" + }, + "./bases/base256emoji": { + "types": "./dist/types/src/bases/base256emoji.d.ts", + "import": "./src/bases/base256emoji.js" + }, "./bases/base32": { + "types": "./dist/types/src/bases/base32.d.ts", "import": "./src/bases/base32.js" }, "./bases/base36": { + "types": "./dist/types/src/bases/base36.d.ts", "import": "./src/bases/base36.js" }, "./bases/base58": { + "types": "./dist/types/src/bases/base58.d.ts", "import": "./src/bases/base58.js" }, "./bases/base64": { + "types": "./dist/types/src/bases/base64.d.ts", "import": "./src/bases/base64.js" }, - "./bases/base256emoji": { - "import": "./src/bases/base256emoji.js" + "./bases/base8": { + "types": "./dist/types/src/bases/base8.d.ts", + "import": "./src/bases/base8.js" }, - "./hashes/hasher": { - "import": "./src/hashes/hasher.js" + "./bases/identity": { + "types": "./dist/types/src/bases/identity.d.ts", + "import": "./src/bases/identity.js" + }, + "./basics": { + "types": "./dist/types/src/basics.d.ts", + "import": "./src/basics.js" + }, + "./block": { + "types": "./dist/types/src/block.d.ts", + "import": "./src/block.js" + }, + "./bytes": { + "types": "./dist/types/src/bytes.d.ts", + "import": "./src/bytes.js" + }, + "./cid": { + "types": "./dist/types/src/cid.d.ts", + "import": "./src/cid.js" + }, + "./codecs/json": { + "types": "./dist/types/src/codecs/json.d.ts", + "import": "./src/codecs/json.js" + }, + "./codecs/raw": { + "types": "./dist/types/src/codecs/raw.d.ts", + "import": "./src/codecs/raw.js" }, "./hashes/digest": { + "types": "./dist/types/src/hashes/digest.d.ts", "import": "./src/hashes/digest.js" }, + "./hashes/hasher": { + "types": "./dist/types/src/hashes/hasher.d.ts", + "import": "./src/hashes/hasher.js" + }, + "./hashes/identity": { + "types": "./dist/types/src/hashes/identity.d.ts", + "import": "./src/hashes/identity.js" + }, "./hashes/sha2": { + "types": "./dist/types/src/hashes/sha2.d.ts", "browser": "./src/hashes/sha2-browser.js", "import": "./src/hashes/sha2.js" }, - "./hashes/identity": { - "import": "./src/hashes/identity.js" + "./interface": { + "types": "./dist/types/src/interface.d.ts", + "import": "./src/interface.js" }, - "./codecs/json": { - "import": "./src/codecs/json.js" + "./link": { + "types": "./dist/types/src/link.d.ts", + "import": "./src/link.js" }, - "./codecs/raw": { - "import": "./src/codecs/raw.js" + "./traversal": { + "types": "./dist/types/src/traversal.d.ts", + "import": "./src/traversal.js" } }, - "devDependencies": { - "@ipld/dag-pb": "^2.1.14", - "@stablelib/sha256": "^1.0.1", - "@stablelib/sha512": "^1.0.1", - "@types/chai": "^4.3.0", - "@types/chai-as-promised": "^7.1.4", - "@types/mocha": "^10.0.0", - "@types/node": "^18.0.0", - "@typescript-eslint/eslint-plugin": "^5.6.0", - "@typescript-eslint/parser": "^5.6.0", - "buffer": "^6.0.3", - "c8": "^7.10.0", - "chai": "^4.3.4", - "chai-as-promised": "^7.1.1", - "cids": "^1.1.9", - "ipjs": "^5.2.0", - "mocha": "^10.0.0", - "polendina": "^3.0.0", - "standard": "^17.0.0", - "typescript": "^4.5.4" - }, - "standard": { - "ignore": [ - "dist", - "vendor" - ] - }, - "directories": { - "test": "test" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/multiformats/js-multiformats.git" - }, - "bugs": { - "url": "https://github.com/multiformats/js-multiformats/issues" + "browser": { + "./hashes/sha2": "./src/hashes/sha2-browser.js", + "./src/hashes/sha2.js": "./src/hashes/sha2-browser.js" }, - "homepage": "https://github.com/multiformats/js-multiformats#readme", - "typesVersions": { - "*": { - "*": [ - "types/src/*" - ], - "types/*": [ - "types/*" - ] + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "sourceType": "module" } }, "release": { "branches": [ - "master" + "esm-migration" ], "plugins": [ [ @@ -178,15 +187,15 @@ "release": "patch" }, { - "type": "chore", + "type": "docs", "release": "patch" }, { - "type": "docs", + "type": "test", "release": "patch" }, { - "type": "test", + "type": "deps", "release": "patch" }, { @@ -216,7 +225,11 @@ }, { "type": "docs", - "section": "Trivial Changes" + "section": "Documentation" + }, + { + "type": "deps", + "section": "Dependencies" }, { "type": "test", @@ -227,14 +240,47 @@ } ], "@semantic-release/changelog", - [ - "@semantic-release/npm", - { - "pkgRoot": "dist" - } - ], + "@semantic-release/npm", "@semantic-release/github", "@semantic-release/git" ] + }, + "scripts": { + "clean": "aegir clean", + "lint": "aegir lint", + "build": "aegir build", + "release": "aegir release", + "test": "npm run lint && npm run test:node && npm run test:chrome && npm run test:ts", + "test:ts": "npm run test --prefix test/ts-use", + "test:node": "aegir test -t node --cov", + "test:chrome": "aegir test -t browser --cov", + "test:chrome-webworker": "aegir test -t webworker", + "test:firefox": "aegir test -t browser -- --browser firefox", + "test:firefox-webworker": "aegir test -t webworker -- --browser firefox", + "test:electron-main": "aegir test -t electron-main" + }, + "devDependencies": { + "@stablelib/sha256": "^1.0.1", + "@stablelib/sha512": "^1.0.1", + "@types/chai": "^4.3.0", + "@types/chai-as-promised": "^7.1.4", + "@types/mocha": "^10.0.0", + "@types/node": "^18.0.0", + "aegir": "^37.5.1", + "buffer": "^6.0.3", + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "cids": "^1.1.9" + }, + "aegir": { + "build": { + "bundle": false + }, + "test": { + "target": [ + "node", + "browser" + ] + } } } diff --git a/src/bases/base.js b/src/bases/base.js index 00bc80b9..4ace5ff4 100644 --- a/src/bases/base.js +++ b/src/bases/base.js @@ -1,30 +1,19 @@ import basex from '../../vendor/base-x.js' import { coerce } from '../bytes.js' - -/** - * @typedef {import('./interface').BaseEncoder} BaseEncoder - * @typedef {import('./interface').BaseDecoder} BaseDecoder - * @typedef {import('./interface').BaseCodec} BaseCodec - */ - -/** - * @template {string} T - * @typedef {import('./interface').Multibase} Multibase - */ -/** - * @template {string} T - * @typedef {import('./interface').MultibaseEncoder} MultibaseEncoder - */ +// Linter can't see that API is used in types. +// eslint-disable-next-line +import * as API from './interface.js' /** * Class represents both BaseEncoder and MultibaseEncoder meaning it * can be used to encode to multibase or base encode without multibase * prefix. + * * @class * @template {string} Base * @template {string} Prefix - * @implements {MultibaseEncoder} - * @implements {BaseEncoder} + * @implements {API.MultibaseEncoder} + * @implements {API.BaseEncoder} */ class Encoder { /** @@ -40,7 +29,7 @@ class Encoder { /** * @param {Uint8Array} bytes - * @returns {Multibase} + * @returns {API.Multibase} */ encode (bytes) { if (bytes instanceof Uint8Array) { @@ -51,16 +40,6 @@ class Encoder { } } -/** - * @template {string} Prefix - * @typedef {import('./interface').MultibaseDecoder} MultibaseDecoder - */ - -/** - * @template {string} Prefix - * @typedef {import('./interface').UnibaseDecoder} UnibaseDecoder - */ - /** * @template {string} Prefix */ @@ -68,12 +47,13 @@ class Encoder { * Class represents both BaseDecoder and MultibaseDecoder so it could be used * to decode multibases (with matching prefix) or just base decode strings * with corresponding base encoding. + * * @class * @template {string} Base * @template {string} Prefix - * @implements {MultibaseDecoder} - * @implements {UnibaseDecoder} - * @implements {BaseDecoder} + * @implements {API.MultibaseDecoder} + * @implements {API.UnibaseDecoder} + * @implements {API.BaseDecoder} */ class Decoder { /** @@ -109,7 +89,7 @@ class Decoder { /** * @template {string} OtherPrefix - * @param {UnibaseDecoder|ComposedDecoder} decoder + * @param {API.UnibaseDecoder|ComposedDecoder} decoder * @returns {ComposedDecoder} */ or (decoder) { @@ -119,22 +99,17 @@ class Decoder { /** * @template {string} Prefix - * @typedef {import('./interface').CombobaseDecoder} CombobaseDecoder - */ - -/** - * @template {string} Prefix - * @typedef {Record>} Decoders + * @typedef {Record>} Decoders */ /** * @template {string} Prefix - * @implements {MultibaseDecoder} - * @implements {CombobaseDecoder} + * @implements {API.MultibaseDecoder} + * @implements {API.CombobaseDecoder} */ class ComposedDecoder { /** - * @param {Record>} decoders + * @param {Decoders} decoders */ constructor (decoders) { this.decoders = decoders @@ -142,7 +117,7 @@ class ComposedDecoder { /** * @template {string} OtherPrefix - * @param {UnibaseDecoder|ComposedDecoder} decoder + * @param {API.UnibaseDecoder|ComposedDecoder} decoder * @returns {ComposedDecoder} */ or (decoder) { @@ -167,30 +142,25 @@ class ComposedDecoder { /** * @template {string} L * @template {string} R - * @param {UnibaseDecoder|CombobaseDecoder} left - * @param {UnibaseDecoder|CombobaseDecoder} right + * @param {API.UnibaseDecoder|API.CombobaseDecoder} left + * @param {API.UnibaseDecoder|API.CombobaseDecoder} right * @returns {ComposedDecoder} */ export const or = (left, right) => new ComposedDecoder(/** @type {Decoders} */({ - ...(left.decoders || { [/** @type UnibaseDecoder */(left).prefix]: left }), - ...(right.decoders || { [/** @type UnibaseDecoder */(right).prefix]: right }) + ...(left.decoders || { [/** @type API.UnibaseDecoder */(left).prefix]: left }), + ...(right.decoders || { [/** @type API.UnibaseDecoder */(right).prefix]: right }) })) -/** - * @template T - * @typedef {import('./interface').MultibaseCodec} MultibaseCodec - */ - /** * @class * @template {string} Base * @template {string} Prefix - * @implements {MultibaseCodec} - * @implements {MultibaseEncoder} - * @implements {MultibaseDecoder} - * @implements {BaseCodec} - * @implements {BaseEncoder} - * @implements {BaseDecoder} + * @implements {API.MultibaseCodec} + * @implements {API.MultibaseEncoder} + * @implements {API.MultibaseDecoder} + * @implements {API.BaseCodec} + * @implements {API.BaseEncoder} + * @implements {API.BaseDecoder} */ export class Codec { /** @@ -226,7 +196,7 @@ export class Codec { /** * @template {string} Base * @template {string} Prefix - * @param {Object} options + * @param {object} options * @param {Base} options.name * @param {Prefix} options.prefix * @param {(bytes:Uint8Array) => string} options.encode @@ -239,7 +209,7 @@ export const from = ({ name, prefix, encode, decode }) => /** * @template {string} Base * @template {string} Prefix - * @param {Object} options + * @param {object} options * @param {Base} options.name * @param {Prefix} options.prefix * @param {string} options.alphabet @@ -357,7 +327,7 @@ const encode = (data, alphabet, bitsPerChar) => { * * @template {string} Base * @template {string} Prefix - * @param {Object} options + * @param {object} options * @param {Base} options.name * @param {Prefix} options.prefix * @param {string} options.alphabet diff --git a/src/bases/interface.js b/src/bases/interface.js new file mode 100644 index 00000000..d9b3f4f7 --- /dev/null +++ b/src/bases/interface.js @@ -0,0 +1 @@ +// this is dummy module overlayed by interface.ts diff --git a/src/bases/interface.ts b/src/bases/interface.ts index dd4374e2..fd8617ca 100644 --- a/src/bases/interface.ts +++ b/src/bases/interface.ts @@ -8,9 +8,10 @@ export interface BaseEncoder { /** * Base encodes to a **plain** (and not a multibase) string. Unlike * `encode` no multibase prefix is added. + * * @param bytes */ - baseEncode(bytes: Uint8Array): string + baseEncode: (bytes: Uint8Array) => string } /** @@ -20,9 +21,10 @@ export interface BaseDecoder { /** * Decodes **plain** (and not a multibase) string. Unlike * decode + * * @param text */ - baseDecode(text: string): Uint8Array + baseDecode: (text: string) => Uint8Array } /** @@ -58,7 +60,7 @@ export interface MultibaseEncoder { * Encodes binary data into **multibase** string (which will have a * prefix added). */ - encode(bytes: Uint8Array): Multibase + encode: (bytes: Uint8Array) => Multibase } /** @@ -71,9 +73,10 @@ export interface MultibaseDecoder { /** * Decodes **multibase** string (which must have a multibase prefix added). * If prefix does not match + * * @param multibase */ - decode(multibase: Multibase): Uint8Array + decode: (multibase: Multibase) => Uint8Array } /** @@ -86,7 +89,6 @@ export interface MultibaseCodec { decoder: MultibaseDecoder } - export interface UnibaseDecoder extends MultibaseDecoder { // Reserve this property so it can be used to derive type. readonly decoders?: null diff --git a/src/block.js b/src/block.js index ce20bafb..2e1a7dbf 100644 --- a/src/block.js +++ b/src/block.js @@ -1,10 +1,39 @@ import { bytes as binary, CID } from './index.js' +// Linter can see that API is used in types. +// eslint-disable-next-line +import * as API from './interface.js' -const readonly = ({ enumerable = true, configurable = false } = {}) => ({ - enumerable, - configurable, - writable: false -}) +function readonly ({ enumerable = true, configurable = false } = {}) { + return { enumerable, configurable, writable: false } +} + +/** + * @param {[string|number, string]} path + * @param {any} value + * @returns {Iterable<[string, CID]>} + */ +function * linksWithin (path, value) { + if (value != null && typeof value === 'object') { + if (Array.isArray(value)) { + for (const [index, element] of value.entries()) { + const elementPath = [...path, index] + const cid = CID.asCID(element) + if (cid) { + yield [elementPath.join('/'), cid] + } else if (typeof element === 'object') { + yield * links(element, elementPath) + } + } + } else { + const cid = CID.asCID(value) + if (cid) { + yield [path.join('/'), cid] + } else { + yield * links(value, path) + } + } + } +} /** * @template T @@ -12,31 +41,32 @@ const readonly = ({ enumerable = true, configurable = false } = {}) => ({ * @param {Array} base * @returns {Iterable<[string, CID]>} */ -const links = function * (source, base) { - if (source == null) return - if (source instanceof Uint8Array) return +function * links (source, base) { + if (source == null || source instanceof Uint8Array) { + return + } for (const [key, value] of Object.entries(source)) { - const path = [...base, key] - if (value != null && typeof value === 'object') { - if (Array.isArray(value)) { - for (const [index, element] of value.entries()) { - const elementPath = [...path, index] - const cid = CID.asCID(element) - if (cid) { - yield [elementPath.join('/'), cid] - } else if (typeof element === 'object') { - yield * links(element, elementPath) - } - } - } else { - const cid = CID.asCID(value) - if (cid) { - yield [path.join('/'), cid] - } else { - yield * links(value, path) - } + const path = /** @type {[string|number, string]} */ ([...base, key]) + yield * linksWithin(path, value) + } +} + +/** + * @param {[string|number, string]} path + * @param {any} value + * @returns {Iterable} + */ +function * treeWithin (path, value) { + if (Array.isArray(value)) { + for (const [index, element] of value.entries()) { + const elementPath = [...path, index] + yield elementPath.join('/') + if (typeof element === 'object' && !CID.asCID(element)) { + yield * tree(element, elementPath) } } + } else { + yield * tree(value, path) } } @@ -46,34 +76,28 @@ const links = function * (source, base) { * @param {Array} base * @returns {Iterable} */ -const tree = function * (source, base) { - if (source == null) return +function * tree (source, base) { + if (source == null || typeof source !== 'object') { + return + } for (const [key, value] of Object.entries(source)) { - const path = [...base, key] + const path = /** @type {[string|number, string]} */ ([...base, key]) yield path.join('/') if (value != null && !(value instanceof Uint8Array) && typeof value === 'object' && !CID.asCID(value)) { - if (Array.isArray(value)) { - for (const [index, element] of value.entries()) { - const elementPath = [...path, index] - yield elementPath.join('/') - if (typeof element === 'object' && !CID.asCID(element)) { - yield * tree(element, elementPath) - } - } - } else { - yield * tree(value, path) - } + yield * treeWithin(path, value) } } } /** + * * @template T * @param {T} source * @param {string[]} path + * @returns {API.BlockCursorView} */ -const get = (source, path) => { - let node = /** @type {Record} */ (source) +function get (source, path) { + let node = /** @type {Record} */(source) for (const [index, key] of path.entries()) { node = node[key] if (node == null) { @@ -88,17 +112,21 @@ const get = (source, path) => { } /** - * @template T + * @template {unknown} T - Logical type of the data encoded in the block + * @template {number} C - multicodec code corresponding to codec used to encode the block + * @template {number} A - multicodec code corresponding to the hashing algorithm used in CID creation. + * @template {API.Version} V - CID version + * @implements {API.BlockView} */ class Block { /** - * @param {Object} options - * @param {CID} options.cid - * @param {ByteView} options.bytes + * @param {object} options + * @param {CID} options.cid + * @param {API.ByteView} options.bytes * @param {T} options.value */ constructor ({ cid, bytes, value }) { - if (!cid || !bytes || typeof value === 'undefined') throw new Error('Missing required argument') + if (!cid || !bytes || typeof value === 'undefined') { throw new Error('Missing required argument') } this.cid = cid this.bytes = bytes @@ -123,88 +151,104 @@ class Block { } /** - * @param {string} [path] - */ + * + * @param {string} [path] + * @returns {API.BlockCursorView} + */ get (path = '/') { return get(this.value, path.split('/').filter(Boolean)) } } /** - * @template T - * @template {number} Code - * @template {number} Algorithm - * @param {Object} options + * @template {unknown} T - Logical type of the data encoded in the block + * @template {number} Code - multicodec code corresponding to codec used to encode the block + * @template {number} Alg - multicodec code corresponding to the hashing algorithm used in CID creation. + * @param {object} options * @param {T} options.value - * @param {BlockEncoder} options.codec - * @param {Hasher} options.hasher - * @returns {Promise>} + * @param {API.BlockEncoder} options.codec + * @param {API.MultihashHasher} options.hasher + * @returns {Promise>} */ -const encode = async ({ value, codec, hasher }) => { +async function encode ({ value, codec, hasher }) { if (typeof value === 'undefined') throw new Error('Missing required argument "value"') if (!codec || !hasher) throw new Error('Missing required argument: codec or hasher') const bytes = codec.encode(value) const hash = await hasher.digest(bytes) - const cid = CID.create(1, codec.code, hash) + /** @type {CID} */ + const cid = CID.create( + 1, + codec.code, + hash + ) return new Block({ value, bytes, cid }) } /** - * @template T - * @template {number} Code - * @template {number} Algorithm - * @param {Object} options - * @param {ByteView} options.bytes - * @param {BlockDecoder} options.codec - * @param {Hasher} options.hasher - * @returns {Promise>} + * @template {unknown} T - Logical type of the data encoded in the block + * @template {number} Code - multicodec code corresponding to codec used to encode the block + * @template {number} Alg - multicodec code corresponding to the hashing algorithm used in CID creation. + * @param {object} options + * @param {API.ByteView} options.bytes + * @param {API.BlockDecoder} options.codec + * @param {API.MultihashHasher} options.hasher + * @returns {Promise>} */ -const decode = async ({ bytes, codec, hasher }) => { +async function decode ({ bytes, codec, hasher }) { if (!bytes) throw new Error('Missing required argument "bytes"') if (!codec || !hasher) throw new Error('Missing required argument: codec or hasher') const value = codec.decode(bytes) const hash = await hasher.digest(bytes) + /** @type {CID} */ const cid = CID.create(1, codec.code, hash) return new Block({ value, bytes, cid }) } /** - * @typedef {Object} RequiredCreateOptions + * @typedef {object} RequiredCreateOptions * @property {CID} options.cid */ /** - * @template T - * @template {number} Code - * @param {{ cid: CID, value:T, codec?: BlockDecoder, bytes: ByteView }|{cid:CID, bytes:ByteView, value?:void, codec:BlockDecoder}} options - * @returns {Block} + * @template {unknown} T - Logical type of the data encoded in the block + * @template {number} Code - multicodec code corresponding to codec used to encode the block + * @template {number} Alg - multicodec code corresponding to the hashing algorithm used in CID creation. + * @template {API.Version} V - CID version + * @param {{ cid: API.Link, value:T, codec?: API.BlockDecoder, bytes: API.ByteView }|{cid:API.Link, bytes:API.ByteView, value?:void, codec:API.BlockDecoder}} options + * @returns {API.BlockView} */ -const createUnsafe = ({ bytes, cid, value: maybeValue, codec }) => { +function createUnsafe ({ bytes, cid, value: maybeValue, codec }) { const value = maybeValue !== undefined ? maybeValue : (codec && codec.decode(bytes)) if (value === undefined) throw new Error('Missing required argument, must either provide "value" or "codec"') - return new Block({ cid, bytes, value }) + return new Block({ + // eslint-disable-next-line object-shorthand + cid: /** @type {CID} */ (cid), + bytes, + value + }) } /** - * @template T - * @template {number} Code - * @template {number} Algorithm - * @param {Object} options - * @param {CID} options.cid - * @param {ByteView} options.bytes - * @param {BlockDecoder} options.codec - * @param {Hasher} options.hasher - * @returns {Promise>} + * @template {unknown} T - Logical type of the data encoded in the block + * @template {number} Code - multicodec code corresponding to codec used to encode the block + * @template {number} Alg - multicodec code corresponding to the hashing algorithm used in CID creation. + * @template {API.Version} V - CID version + * @param {object} options + * @param {API.Link} options.cid + * @param {API.ByteView} options.bytes + * @param {API.BlockDecoder} options.codec + * @param {API.MultihashHasher} options.hasher + * @returns {Promise>} */ -const create = async ({ bytes, cid, hasher, codec }) => { +async function create ({ bytes, cid, hasher, codec }) { if (!bytes) throw new Error('Missing required argument "bytes"') if (!hasher) throw new Error('Missing required argument "hasher"') const value = codec.decode(bytes) @@ -213,29 +257,12 @@ const create = async ({ bytes, cid, hasher, codec }) => { throw new Error('CID hash does not match bytes') } - return createUnsafe({ bytes, cid, value, codec }) + return createUnsafe({ + bytes, + cid, + value, + codec + }) } export { encode, decode, create, createUnsafe, Block } - -/** - * @template T - * @typedef {import('./codecs/interface').ByteView} ByteView - */ - -/** - * @template {number} Code - * @template T - * @typedef {import('./codecs/interface').BlockEncoder} BlockEncoder - */ - -/** - * @template {number} Code - * @template T - * @typedef {import('./codecs/interface').BlockDecoder} BlockDecoder - */ - -/** - * @template Algorithm - * @typedef {import('./hashes/interface').MultihashHasher} Hasher - */ diff --git a/src/block/interface.js b/src/block/interface.js new file mode 100644 index 00000000..d9b3f4f7 --- /dev/null +++ b/src/block/interface.js @@ -0,0 +1 @@ +// this is dummy module overlayed by interface.ts diff --git a/src/block/interface.ts b/src/block/interface.ts new file mode 100644 index 00000000..c87ff74f --- /dev/null +++ b/src/block/interface.ts @@ -0,0 +1,73 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */ +/* eslint-disable no-use-before-define */ +import { Link, Version } from '../link/interface.js' +import { CID } from '../cid.js' + +/** + * A byte-encoded representation of some type of `Data`. + * + * A `ByteView` is essentially a `Uint8Array` that's been "tagged" with + * a `Data` type parameter indicating the type of encoded data. + * + * For example, a `ByteView<{ hello: "world" }>` is a `Uint8Array` containing a + * binary representation of a `{hello: "world"}. + */ +export interface ByteView extends Uint8Array, Phantom {} + +declare const Marker: unique symbol + +/** + * A utility type to retain an unused type parameter `T`. + * Similar to [phantom type parameters in Rust](https://doc.rust-lang.org/rust-by-example/generics/phantom.html). + * + * Capturing unused type parameters allows us to define "nominal types," which + * TypeScript does not natively support. Nominal types in turn allow us to capture + * semantics not represented in the actual type structure, without requring us to define + * new classes or pay additional runtime costs. + * + * For a concrete example, see {@link ByteView}, which extends the `Uint8Array` type to capture + * type information about the structure of the data encoded into the array. + */ +export interface Phantom { + // This field can not be represented because field name is nonexistent + // unique symbol. But given that field is optional any object will valid + // type contstraint. + [Marker]?: T +} + +/** + * Represents an IPLD block (including its CID) that can be decoded to data of + * type `T`. + * + * @template T - Logical type of the data encoded in the block + * @template C - multicodec code corresponding to codec used to encode the block + * @template A - multicodec code corresponding to the hashing algorithm used in CID creation. + * @template V - CID version + */ +export interface Block< + T = unknown, + C extends number = number, + A extends number = number, + V extends Version = 1 +> { + bytes: ByteView + cid: Link +} + +export type BlockCursorView = + | { value: T, remaining?: undefined } + | { value: CID, remaining: string } + +export interface BlockView< + T = unknown, + C extends number = number, + A extends number = number, + V extends Version = 1 +> extends Block { + cid: CID + value: T + + links: () => Iterable<[string, CID]> + tree: () => Iterable + get: (path: string) => BlockCursorView +} diff --git a/src/cid.js b/src/cid.js index 403c3104..4736898f 100644 --- a/src/cid.js +++ b/src/cid.js @@ -3,73 +3,101 @@ import * as Digest from './hashes/digest.js' import { base58btc } from './bases/base58.js' import { base32 } from './bases/base32.js' import { coerce } from './bytes.js' +// Linter can see that API is used in types. +// eslint-disable-next-line +import * as API from "./link/interface.js" + +// This way TS will also expose all the types from module +export * from './link/interface.js' /** - * @typedef {import('./hashes/interface').MultihashDigest} MultihashDigest - * @typedef {0 | 1} CIDVersion + * @template {API.Link} T + * @template {string} Prefix + * @param {T} link + * @param {API.MultibaseEncoder} [base] + * @returns {API.ToString} */ +export const format = (link, base) => { + const { bytes, version } = link + switch (version) { + case 0: + return toStringV0( + bytes, + baseCache(link), + /** @type {API.MultibaseEncoder<"z">} */ (base) || base58btc.encoder + ) + default: + return toStringV1( + bytes, + baseCache(link), + /** @type {API.MultibaseEncoder} */ (base || base32.encoder) + ) + } +} + +/** @type {WeakMap>} */ +const cache = new WeakMap() /** - * @template Prefix - * @typedef {import('./bases/interface').MultibaseEncoder} MultibaseEncoder + * @param {API.UnknownLink} cid + * @returns {Map} */ +const baseCache = cid => { + const baseCache = cache.get(cid) + if (baseCache == null) { + const baseCache = new Map() + cache.set(cid, baseCache) + return baseCache + } + return baseCache +} /** - * @template Prefix - * @typedef {import('./bases/interface').MultibaseDecoder} MultibaseDecoder + * @template {unknown} [Data=unknown] + * @template {number} [Format=number] + * @template {number} [Alg=number] + * @template {API.Version} [Version=API.Version] + * @implements {API.Link} */ export class CID { /** - * @param {CIDVersion} version - * @param {number} code - multicodec code, see https://github.com/multiformats/multicodec/blob/master/table.csv - * @param {MultihashDigest} multihash + * @param {Version} version - Version of the CID + * @param {Format} code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv + * @param {API.MultihashDigest} multihash - (Multi)hash of the of the content. * @param {Uint8Array} bytes * */ constructor (version, code, multihash, bytes) { + /** @readonly */ this.code = code + /** @readonly */ this.version = version + /** @readonly */ this.multihash = multihash + /** @readonly */ this.bytes = bytes // ArrayBufferView + /** @readonly */ this.byteOffset = bytes.byteOffset + /** @readonly */ this.byteLength = bytes.byteLength // Circular reference - /** @private */ + /** @readonly */ this.asCID = this - /** - * @type {Map} - * @private - */ - this._baseCache = new Map() - - // Configure private properties - Object.defineProperties(this, { - byteOffset: hidden, - byteLength: hidden, - - code: readonly, - version: readonly, - multihash: readonly, - bytes: readonly, - - _baseCache: hidden, - asCID: hidden - }) } /** - * @returns {CID} + * @returns {CID} */ toV0 () { switch (this.version) { case 0: { - return this + return /** @type {CID} */ (this) } - default: { + case 1: { const { code, multihash } = this if (code !== DAG_PB_CODE) { @@ -81,53 +109,79 @@ export class CID { throw new Error('Cannot convert non sha2-256 multihash CID to CIDv0') } - return CID.createV0(multihash) + return /** @type {CID} */ ( + CID.createV0( + /** @type {API.MultihashDigest} */ (multihash) + ) + ) + } + default: { + throw Error( + `Can not convert CID version ${this.version} to version 0. This is a bug please report` + ) } } } /** - * @returns {CID} + * @returns {CID} */ toV1 () { switch (this.version) { case 0: { const { code, digest } = this.multihash const multihash = Digest.create(code, digest) - return CID.createV1(this.code, multihash) + return /** @type {CID} */ ( + CID.createV1(this.code, multihash) + ) } case 1: { - return this + return /** @type {CID} */ (this) } - /* c8 ignore next 3 */ default: { - throw Error(`Can not convert CID version ${this.version} to version 0. This is a bug please report`) + throw Error( + `Can not convert CID version ${this.version} to version 1. This is a bug please report` + ) } } } /** - * @param {any} other + * @param {unknown} other + * @returns {other is CID} */ equals (other) { - return other && - this.code === other.code && - this.version === other.version && - Digest.equals(this.multihash, other.multihash) + return CID.equals(this, other) + } + + /** + * @template {unknown} Data + * @template {number} Format + * @template {number} Alg + * @template {API.Version} Version + * @param {API.Link} self + * @param {unknown} other + * @returns {other is CID} + */ + static equals (self, other) { + const unknown = + /** @type {{code?:unknown, version?:unknown, multihash?:unknown}} */ ( + other + ) + return ( + unknown && + self.code === unknown.code && + self.version === unknown.version && + Digest.equals(self.multihash, unknown.multihash) + ) } /** - * @param {MultibaseEncoder} [base] + * @param {API.MultibaseEncoder} [base] * @returns {string} */ toString (base) { - const { bytes, version, _baseCache } = this - switch (version) { - case 0: - return toStringV0(bytes, _baseCache, base || base58btc.encoder) - default: - return toStringV1(bytes, _baseCache, base || base32.encoder) - } + return format(this, base) } toJSON () { @@ -138,6 +192,10 @@ export class CID { } } + link () { + return this + } + get [Symbol.toStringTag] () { return 'CID' } @@ -145,38 +203,7 @@ export class CID { // Legacy [Symbol.for('nodejs.util.inspect.custom')] () { - return 'CID(' + this.toString() + ')' - } - - // Deprecated - - /** - * @param {any} value - * @returns {value is CID} - */ - static isCID (value) { - deprecate(/^0\.0/, IS_CID_DEPRECATION) - return !!(value && (value[cidSymbol] || value.asCID === value)) - } - - get toBaseEncodedString () { - throw new Error('Deprecated, use .toString()') - } - - get codec () { - throw new Error('"codec" property is deprecated, use integer "code" property instead') - } - - get buffer () { - throw new Error('Deprecated .buffer property, use .bytes to get Uint8Array instead') - } - - get multibaseName () { - throw new Error('"multibaseName" property is deprecated') - } - - get prefix () { - throw new Error('"prefix" property is deprecated') + return `CID(${this.toString()})` } /** @@ -188,42 +215,60 @@ export class CID { * * This allows two different incompatible versions of CID library to * co-exist and interop as long as binary interface is compatible. - * @param {any} value - * @returns {CID|null} + * + * @template {unknown} Data + * @template {number} Format + * @template {number} Alg + * @template {API.Version} Version + * @template {unknown} U + * @param {API.Link|U} input + * @returns {CID|null} */ - static asCID (value) { + static asCID (input) { + const value = /** @type {any} */ (input) if (value instanceof CID) { - // If value is instance of CID then we're all set. + // If value is instance of CID then we're all set. return value } else if (value != null && value.asCID === value) { - // If value isn't instance of this CID class but `this.asCID === this` is - // true it is CID instance coming from a different implementation (diff - // version or duplicate). In that case we rebase it to this `CID` - // implementation so caller is guaranteed to get instance with expected - // API. + // If value isn't instance of this CID class but `this.asCID === this` is + // true it is CID instance coming from a different implementation (diff + // version or duplicate). In that case we rebase it to this `CID` + // implementation so caller is guaranteed to get instance with expected + // API. const { version, code, multihash, bytes } = value - return new CID(version, code, multihash, bytes || encodeCID(version, code, multihash.bytes)) + return new CID( + version, + code, + /** @type {API.MultihashDigest} */ (multihash), + bytes || encodeCID(version, code, multihash.bytes) + ) } else if (value != null && value[cidSymbol] === true) { - // If value is a CID from older implementation that used to be tagged via - // symbol we still rebase it to this `CID` implementation by - // delegating that to a constructor. + // If value is a CID from older implementation that used to be tagged via + // symbol we still rebase it to the this `CID` implementation by + // delegating that to a constructor. const { version, multihash, code } = value - const digest = Digest.decode(multihash) + const digest = + /** @type {API.MultihashDigest} */ + (Digest.decode(multihash)) return CID.create(version, code, digest) } else { - // Otherwise value is not a CID (or an incompatible version of it) in - // which case we return `null`. + // Otherwise value is not a CID (or an incompatible version of it) in + // which case we return `null`. return null } } /** - * - * @param {CIDVersion} version - Version of the CID - * @param {number} code - Code of the codec content is encoded in. - * @param {MultihashDigest} digest - (Multi)hash of the of the content. - * @returns {CID} - */ + * + * @template {unknown} Data + * @template {number} Format + * @template {number} Alg + * @template {API.Version} Version + * @param {Version} version - Version of the CID + * @param {Format} code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv + * @param {API.MultihashDigest} digest - (Multi)hash of the of the content. + * @returns {CID} + */ static create (version, code, digest) { if (typeof code !== 'number') { throw new Error('String codecs are no longer supported') @@ -232,7 +277,9 @@ export class CID { switch (version) { case 0: { if (code !== DAG_PB_CODE) { - throw new Error(`Version 0 CID must use dag-pb (code: ${DAG_PB_CODE}) block encoding`) + throw new Error( + `Version 0 CID must use dag-pb (code: ${DAG_PB_CODE}) block encoding` + ) } else { return new CID(version, code, digest, digest.bytes) } @@ -249,19 +296,25 @@ export class CID { /** * Simplified version of `create` for CIDv0. - * @param {MultihashDigest} digest - Multihash. + * + * @template {unknown} [T=unknown] + * @param {API.MultihashDigest} digest - Multihash. + * @returns {CID} */ static createV0 (digest) { return CID.create(0, DAG_PB_CODE, digest) } /** - * Simplified version of `create` for CIDv1. - * @template {number} Code - * @param {Code} code - Content encoding format code. - * @param {MultihashDigest} digest - Miltihash of the content. - * @returns {CID} - */ + * Simplified version of `create` for CIDv1. + * + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @param {Code} code - Content encoding format code. + * @param {API.MultihashDigest} digest - Miltihash of the content. + * @returns {CID} + */ static createV1 (code, digest) { return CID.create(1, code, digest) } @@ -273,8 +326,12 @@ export class CID { * An error will be thrown if the bytes provided do not contain a valid * binary representation of a CID. * - * @param {Uint8Array} bytes - * @returns {CID} + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @template {API.Version} Ver + * @param {API.ByteView>} bytes + * @returns {CID} */ static decode (bytes) { const [cid, remainder] = CID.decodeFirst(bytes) @@ -293,20 +350,36 @@ export class CID { * will be a zero-length byte array if the provided bytes only contained a * binary CID representation. * - * @param {Uint8Array} bytes - * @returns {[CID, Uint8Array]} + * @template {unknown} T + * @template {number} C + * @template {number} A + * @template {API.Version} V + * @param {API.ByteView>} bytes + * @returns {[CID, Uint8Array]} */ static decodeFirst (bytes) { const specs = CID.inspectBytes(bytes) const prefixSize = specs.size - specs.multihashSize - const multihashBytes = coerce(bytes.subarray(prefixSize, prefixSize + specs.multihashSize)) + const multihashBytes = coerce( + bytes.subarray(prefixSize, prefixSize + specs.multihashSize) + ) if (multihashBytes.byteLength !== specs.multihashSize) { throw new Error('Incorrect length') } - const digestBytes = multihashBytes.subarray(specs.multihashSize - specs.digestSize) - const digest = new Digest.Digest(specs.multihashCode, specs.digestSize, digestBytes, multihashBytes) - const cid = specs.version === 0 ? CID.createV0(digest) : CID.createV1(specs.codec, digest) - return [cid, bytes.subarray(specs.size)] + const digestBytes = multihashBytes.subarray( + specs.multihashSize - specs.digestSize + ) + const digest = new Digest.Digest( + specs.multihashCode, + specs.digestSize, + digestBytes, + multihashBytes + ) + const cid = + specs.version === 0 + ? CID.createV0(/** @type {API.MultihashDigest} */ (digest)) + : CID.createV1(specs.codec, digest) + return [/** @type {CID} */(cid), bytes.subarray(specs.size)] } /** @@ -318,8 +391,12 @@ export class CID { * 10 bytes be made available in the `initialBytes` argument for a complete * inspection. * - * @param {Uint8Array} initialBytes - * @returns {{ version:CIDVersion, codec:number, multihashCode:number, digestSize:number, multihashSize:number, size:number }} + * @template {unknown} T + * @template {number} C + * @template {number} A + * @template {API.Version} V + * @param {API.ByteView>} initialBytes + * @returns {{ version:V, codec:C, multihashCode:A, digestSize:number, multihashSize:number, size:number }} */ static inspectBytes (initialBytes) { let offset = 0 @@ -329,13 +406,14 @@ export class CID { return i } - let version = next() - let codec = DAG_PB_CODE - if (version === 18) { // CIDv0 - version = 0 + let version = /** @type {V} */ (next()) + let codec = /** @type {C} */ (DAG_PB_CODE) + if (/** @type {number} */(version) === 18) { + // CIDv0 + version = /** @type {V} */ (0) offset = 0 - } else if (version === 1) { - codec = next() + } else { + codec = /** @type {C} */ (next()) } if (version !== 0 && version !== 1) { @@ -343,7 +421,7 @@ export class CID { } const prefixSize = offset - const multihashCode = next() // multihash code + const multihashCode = /** @type {A} */ (next()) // multihash code const digestSize = next() // multihash length const size = offset + digestSize const multihashSize = size - prefixSize @@ -358,16 +436,21 @@ export class CID { * a default decoder). * * @template {string} Prefix - * @param {string} source - * @param {MultibaseDecoder} [base] + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @template {API.Version} Ver + * @param {API.ToString, Prefix>} source + * @param {API.MultibaseDecoder} [base] + * @returns {CID} */ static parse (source, base) { const [prefix, bytes] = parseCIDtoBytes(source, base) const cid = CID.decode(bytes) + // Cache string representation to avoid computing it on `this.toString()` - // @ts-ignore - Can't access private - cid._baseCache.set(prefix, source) + baseCache(cid).set(prefix, source) return cid } @@ -375,30 +458,39 @@ export class CID { /** * @template {string} Prefix - * @param {string} source - * @param {MultibaseDecoder} [base] - * @returns {[string, Uint8Array]} + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @template {API.Version} Ver + * @param {API.ToString, Prefix>} source + * @param {API.MultibaseDecoder} [base] + * @returns {[Prefix, API.ByteView>]} */ const parseCIDtoBytes = (source, base) => { switch (source[0]) { // CIDv0 is parsed differently case 'Q': { const decoder = base || base58btc - return [base58btc.prefix, decoder.decode(`${base58btc.prefix}${source}`)] + return [ + /** @type {Prefix} */ (base58btc.prefix), + decoder.decode(`${base58btc.prefix}${source}`) + ] } case base58btc.prefix: { const decoder = base || base58btc - return [base58btc.prefix, decoder.decode(source)] + return [/** @type {Prefix} */(base58btc.prefix), decoder.decode(source)] } case base32.prefix: { const decoder = base || base32 - return [base32.prefix, decoder.decode(source)] + return [/** @type {Prefix} */(base32.prefix), decoder.decode(source)] } default: { if (base == null) { - throw Error('To parse non base32 or base58btc encoded CID multibase decoder must be provided') + throw Error( + 'To parse non base32 or base58btc encoded CID multibase decoder must be provided' + ) } - return [source[0], base.decode(source)] + return [/** @type {Prefix} */(source[0]), base.decode(source)] } } } @@ -407,7 +499,7 @@ const parseCIDtoBytes = (source, base) => { * * @param {Uint8Array} bytes * @param {Map} cache - * @param {MultibaseEncoder<'z'>} base + * @param {API.MultibaseEncoder<'z'>} base */ const toStringV0 = (bytes, cache, base) => { const { prefix } = base @@ -429,7 +521,7 @@ const toStringV0 = (bytes, cache, base) => { * @template {string} Prefix * @param {Uint8Array} bytes * @param {Map} cache - * @param {MultibaseEncoder} base + * @param {API.MultibaseEncoder} base */ const toStringV1 = (bytes, cache, base) => { const { prefix } = base @@ -447,7 +539,7 @@ const DAG_PB_CODE = 0x70 const SHA_256_CODE = 0x12 /** - * @param {CIDVersion} version + * @param {API.Version} version * @param {number} code * @param {Uint8Array} multihash * @returns {Uint8Array} @@ -463,40 +555,3 @@ const encodeCID = (version, code, multihash) => { } const cidSymbol = Symbol.for('@ipld/js-cid/CID') -const readonly = { writable: false, configurable: false, enumerable: true } -const hidden = { writable: false, enumerable: false, configurable: false } - -// ESM does not support importing package.json where this version info -// should come from. To workaround it version is copied here. -const version = '0.0.0-dev' -// Start throwing exceptions on major version bump -/** - * - * @param {RegExp} range - * @param {string} message - */ -const deprecate = (range, message) => { - if (range.test(version)) { - console.warn(message) - /* c8 ignore next 3 */ - } else { - throw new Error(message) - } -} - -const IS_CID_DEPRECATION = -`CID.isCID(v) is deprecated and will be removed in the next major release. -Following code pattern: - -if (CID.isCID(value)) { - doSomethingWithCID(value) -} - -Is replaced with: - -const cid = CID.asCID(value) -if (cid) { - // Make sure to use cid instead of value - doSomethingWithCID(cid) -} -` diff --git a/src/codecs/interface.js b/src/codecs/interface.js new file mode 100644 index 00000000..d9b3f4f7 --- /dev/null +++ b/src/codecs/interface.js @@ -0,0 +1 @@ +// this is dummy module overlayed by interface.ts diff --git a/src/codecs/interface.ts b/src/codecs/interface.ts index dc8527c1..f62003a1 100644 --- a/src/codecs/interface.ts +++ b/src/codecs/interface.ts @@ -1,10 +1,12 @@ +import type { ByteView } from '../block/interface.js' + /** * IPLD encoder part of the codec. */ export interface BlockEncoder { name: string code: Code - encode(data: T): ByteView + encode: (data: T) => ByteView } /** @@ -12,7 +14,7 @@ export interface BlockEncoder { */ export interface BlockDecoder { code: Code - decode(bytes: ByteView): T + decode: (bytes: ByteView) => T } /** @@ -20,9 +22,4 @@ export interface BlockDecoder { */ export interface BlockCodec extends BlockEncoder, BlockDecoder {} -// This just a hack to retain type information about the data that -// is encoded `T` Because it's a union `data` field is never going -// to be usable anyway. -export type ByteView = - | Uint8Array - | Uint8Array & { data: T } +export type { ByteView } diff --git a/src/codecs/json.js b/src/codecs/json.js index ac1cee27..c3028430 100644 --- a/src/codecs/json.js +++ b/src/codecs/json.js @@ -2,7 +2,7 @@ /** * @template T - * @typedef {import('./interface').ByteView} ByteView + * @typedef {import('./interface.js').ByteView} ByteView */ const textEncoder = new TextEncoder() diff --git a/src/codecs/raw.js b/src/codecs/raw.js index 3affb144..7a9c7815 100644 --- a/src/codecs/raw.js +++ b/src/codecs/raw.js @@ -4,7 +4,7 @@ import { coerce } from '../bytes.js' /** * @template T - * @typedef {import('./interface').ByteView} ByteView + * @typedef {import('./interface.js').ByteView} ByteView */ export const name = 'raw' diff --git a/src/hashes/digest.js b/src/hashes/digest.js index 206e891b..fa5e5bac 100644 --- a/src/hashes/digest.js +++ b/src/hashes/digest.js @@ -3,6 +3,7 @@ import * as varint from '../varint.js' /** * Creates a multihash digest. + * * @template {number} Code * @param {Code} code * @param {Uint8Array} digest @@ -22,6 +23,7 @@ export const create = (code, digest) => { /** * Turns bytes representation of multihash digest into an instance. + * * @param {Uint8Array} multihash * @returns {MultihashDigest} */ @@ -40,24 +42,32 @@ export const decode = (multihash) => { /** * @param {MultihashDigest} a - * @param {MultihashDigest} b - * @returns {boolean} + * @param {unknown} b + * @returns {b is MultihashDigest} */ export const equals = (a, b) => { if (a === b) { return true } else { - return a.code === b.code && a.size === b.size && equalBytes(a.bytes, b.bytes) + const data = /** @type {{code?:unknown, size?:unknown, bytes?:unknown}} */(b) + + return ( + a.code === data.code && + a.size === data.size && + data.bytes instanceof Uint8Array && + equalBytes(a.bytes, data.bytes) + ) } } /** - * @typedef {import('./interface').MultihashDigest} MultihashDigest + * @typedef {import('./interface.js').MultihashDigest} MultihashDigest */ /** * Represents a multihash digest which carries information about the * hashing alogrithm and an actual hash digest. + * * @template {number} Code * @template {number} Size * @class @@ -66,6 +76,7 @@ export const equals = (a, b) => { export class Digest { /** * Creates a multihash digest. + * * @param {Code} code * @param {Size} size * @param {Uint8Array} digest diff --git a/src/hashes/hasher.js b/src/hashes/hasher.js index 703e416b..ac095857 100644 --- a/src/hashes/hasher.js +++ b/src/hashes/hasher.js @@ -3,7 +3,7 @@ import * as Digest from './digest.js' /** * @template {string} Name * @template {number} Code - * @param {Object} options + * @param {object} options * @param {Name} options.name * @param {Code} options.code * @param {(input: Uint8Array) => Await} options.encode @@ -52,7 +52,7 @@ export class Hasher { /** * @template {number} Alg - * @typedef {import('./interface').MultihashHasher} MultihashHasher + * @typedef {import('./interface.js').MultihashHasher} MultihashHasher */ /** diff --git a/src/hashes/interface.js b/src/hashes/interface.js new file mode 100644 index 00000000..d9b3f4f7 --- /dev/null +++ b/src/hashes/interface.js @@ -0,0 +1 @@ +// this is dummy module overlayed by interface.ts diff --git a/src/hashes/interface.ts b/src/hashes/interface.ts index 09afdfe0..b7e9edf6 100644 --- a/src/hashes/interface.ts +++ b/src/hashes/interface.ts @@ -44,7 +44,7 @@ export interface MultihashHasher { * * @param {Uint8Array} input */ - digest(input: Uint8Array): Promise | MultihashDigest + digest: (input: Uint8Array) => Promise> | MultihashDigest /** * Name of the multihash @@ -68,5 +68,5 @@ export interface MultihashHasher { * impractical e.g. implementation of Hash Array Mapped Trie (HAMT). */ export interface SyncMultihashHasher extends MultihashHasher { - digest(input: Uint8Array): MultihashDigest + digest: (input: Uint8Array) => MultihashDigest } diff --git a/src/index.js b/src/index.js index aed6be18..377a9771 100644 --- a/src/index.js +++ b/src/index.js @@ -3,5 +3,7 @@ import * as varint from './varint.js' import * as bytes from './bytes.js' import * as hasher from './hashes/hasher.js' import * as digest from './hashes/digest.js' +// This way TS will also expose all the types from module +export * from './interface.js' export { CID, hasher, digest, varint, bytes } diff --git a/src/interface.js b/src/interface.js new file mode 100644 index 00000000..d9b3f4f7 --- /dev/null +++ b/src/interface.js @@ -0,0 +1 @@ +// this is dummy module overlayed by interface.ts diff --git a/src/interface.ts b/src/interface.ts new file mode 100644 index 00000000..e2a56c67 --- /dev/null +++ b/src/interface.ts @@ -0,0 +1,5 @@ +export * from './bases/interface.js' +export * from './hashes/interface.js' +export * from './codecs/interface.js' +export * from './link/interface.js' +export * from './block/interface.js' diff --git a/src/link.js b/src/link.js new file mode 100644 index 00000000..71793099 --- /dev/null +++ b/src/link.js @@ -0,0 +1,75 @@ +// Linter can see that API is used in types. +// eslint-disable-next-line +import * as API from "./link/interface.js" +import { CID, format } from './cid.js' +// This way TS will also expose all the types from module +export * from './link/interface.js' + +const DAG_PB_CODE = 0x70 +// eslint-disable-next-line +const SHA_256_CODE = 0x12 + +/** + * Simplified version of `create` for CIDv0. + * + * @param {API.MultihashDigest} digest - Multihash. + * @returns {API.LegacyLink} + */ +export const createLegacy = digest => CID.create(0, DAG_PB_CODE, digest) + +/** + * Simplified version of `create` for CIDv1. + * + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @param {Code} code - Content encoding format code. + * @param {API.MultihashDigest} digest - Miltihash of the content. + * @returns {API.Link} + */ +export const create = (code, digest) => CID.create(1, code, digest) + +/** + * Type predicate returns true if value is the link. + * + * @template {API.Link} L + * @param {unknown|L} value + * @returns {value is L & CID} + */ +export const isLink = value => + value != null && /** @type {{asCID: unknown}} */ (value).asCID === value + +/** + * Takes cid in a string representation and creates an instance. If `base` + * decoder is not provided will use a default from the configuration. It will + * throw an error if encoding of the CID is not compatible with supplied (or + * a default decoder). + * + * @template {string} Prefix + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @template {API.Version} Ver + * @param {API.ToString, Prefix>} source + * @param {API.MultibaseDecoder} [base] + * @returns {API.Link} + */ +export const parse = (source, base) => CID.parse(source, base) + +export { format } + +/** + * Decoded a CID from its binary representation. The byte array must contain + * only the CID with no additional bytes. + * + * An error will be thrown if the bytes provided do not contain a valid + * binary representation of a CID. + * + * @template {unknown} Data + * @template {number} Code + * @template {number} Alg + * @template {API.Version} Ver + * @param {API.ByteView>} bytes + * @returns {API.Link} + */ +export const decode = bytes => CID.decode(bytes) diff --git a/src/link/interface.js b/src/link/interface.js new file mode 100644 index 00000000..d9b3f4f7 --- /dev/null +++ b/src/link/interface.js @@ -0,0 +1 @@ +// this is dummy module overlayed by interface.ts diff --git a/src/link/interface.ts b/src/link/interface.ts new file mode 100644 index 00000000..af8b7e47 --- /dev/null +++ b/src/link/interface.ts @@ -0,0 +1,53 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */ +/* eslint-disable no-use-before-define */ +import type { MultihashDigest } from '../hashes/interface.js' +import type { MultibaseEncoder, MultibaseDecoder, Multibase } from '../bases/interface.js' +import type { Phantom, ByteView } from '../block/interface.js' + +export type { MultihashDigest, MultibaseEncoder, MultibaseDecoder } +export type Version = 0 | 1 + +export type DAG_PB = 0x70 +export type SHA_256 = 0x12 + +/** + * Represents an IPLD link to a specific data of type `T`. + * + * @template T - Logical type of the data being linked to. + * @template C - multicodec code corresponding to a codec linked data is encoded with + * @template A - multicodec code corresponding to the hashing algorithm of the CID + * @template V - CID version + */ +export interface Link< + Data extends unknown = unknown, + Format extends number = number, + Alg extends number = number, + V extends Version = 1 + > extends Phantom { + readonly version: V + readonly code: Format + readonly multihash: MultihashDigest + + readonly byteOffset: number + readonly byteLength: number + readonly bytes: ByteView> + + equals: (other: unknown) => other is Link + + toString: (base?: MultibaseEncoder) => ToString, Prefix> + toJSON: () => { version: V, code: Format, hash: Uint8Array } + link: () => Link + + toV1: () => Link +} + +export interface LegacyLink extends Link { +} + +export type UnknownLink = + | LegacyLink + | Link + +export type ToString = Multibase & Phantom + +export type { ByteView } diff --git a/src/traversal.js b/src/traversal.js index a13d1d5c..0b46b086 100644 --- a/src/traversal.js +++ b/src/traversal.js @@ -1,19 +1,24 @@ import { base58btc } from './bases/base58.js' /** - * @typedef {import('./cid.js').CID} CID + * @template [C=number] - multicodec code corresponding to codec used to encode the block + * @template [A=number] - multicodec code corresponding to the hashing algorithm used in CID creation. + * @template [V=0|1] - CID version + * @typedef {import('./cid').CID} CID */ /** - * @template T - * @typedef {import('./block.js').Block} Block + * @template [T=unknown] - Logical type of the data encoded in the block + * @template [C=number] - multicodec code corresponding to codec used to encode the block + * @template [A=number] - multicodec code corresponding to the hashing algorithm used in CID creation. + * @template [V=0|1] - CID version + * @typedef {import('./block/interface.js').BlockView} BlockView */ /** - * @template T - * @param {Object} options + * @param {object} options * @param {CID} options.cid - * @param {(cid: CID) => Promise|null>} options.load + * @param {(cid: CID) => Promise} options.load * @param {Set} [options.seen] */ const walk = async ({ cid, load, seen }) => { diff --git a/test/test-block.js b/test/test-block.spec.js similarity index 94% rename from test/test-block.js rename to test/test-block.spec.js index c744e72f..8b1dbe43 100644 --- a/test/test-block.js +++ b/test/test-block.spec.js @@ -1,8 +1,8 @@ /* globals describe, it */ -import * as codec from 'multiformats/codecs/json' -import { sha256 as hasher } from 'multiformats/hashes/sha2' -import * as main from 'multiformats/block' -import { CID, bytes } from 'multiformats' +import * as codec from '../src/codecs/json.js' +import { sha256 as hasher } from '../src/hashes/sha2.js' +import * as main from '../src/block.js' +import { CID, bytes } from '../src/index.js' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' @@ -60,9 +60,9 @@ describe('block', () => { it('get', () => { let ret = block.get('link/test') assert.deepStrictEqual(ret.remaining, 'test') - assert.deepStrictEqual(ret.value.toString(), link.toString()) + assert.deepStrictEqual(String(ret.value), link.toString()) ret = block.get('nope') - // @ts-expect-error - 'string' is not expected + assert.deepStrictEqual(ret, { value: 'skip' }) }) diff --git a/test/test-bytes.js b/test/test-bytes.spec.js similarity index 100% rename from test/test-bytes.js rename to test/test-bytes.spec.js diff --git a/test/test-cid.js b/test/test-cid.spec.js similarity index 63% rename from test/test-cid.js rename to test/test-cid.spec.js index 905d4644..6de0f6c5 100644 --- a/test/test-cid.js +++ b/test/test-cid.spec.js @@ -1,15 +1,18 @@ /* globals describe, it */ -import OLDCID from 'cids' import { fromHex, toHex, equals } from '../src/bytes.js' -import { varint, CID } from 'multiformats' -import { base58btc } from 'multiformats/bases/base58' -import { base32 } from 'multiformats/bases/base32' -import { base64 } from 'multiformats/bases/base64' -import { sha256, sha512 } from 'multiformats/hashes/sha2' +import { varint, CID } from '../src/index.js' +import { base58btc } from '../src/bases/base58.js' +import { base32 } from '../src/bases/base32.js' +import { base64 } from '../src/bases/base64.js' +import { sha256, sha512 } from '../src/hashes/sha2.js' import invalidMultihash from './fixtures/invalid-multihash.js' +import OLDCID from 'cids' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' +// Linter can see that API is used in types. +// eslint-disable-next-line +import * as API from 'multiformats' chai.use(chaiAsPromised) const { assert } = chai @@ -39,6 +42,16 @@ describe('CID', () => { assert.deepStrictEqual(cid.toString(), base58btc.baseEncode(hash.bytes)) }) + it('CID.createV0', async () => { + const hash = await sha256.digest(textEncoder.encode('abc')) + const cid = CID.createV0(hash) + + assert.deepStrictEqual(cid.code, 112) + assert.deepStrictEqual(cid.version, 0) + assert.deepStrictEqual(cid.multihash, hash) + assert.deepStrictEqual(cid.toString(), base58btc.baseEncode(hash.bytes)) + }) + it('create from multihash', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) @@ -47,14 +60,20 @@ describe('CID', () => { assert.deepStrictEqual(cid.code, 112) assert.deepStrictEqual(cid.version, 0) assert.deepStrictEqual(cid.multihash.digest, hash.digest) - assert.deepStrictEqual({ ...cid.multihash, digest: null }, { ...hash, digest: null }) + assert.deepStrictEqual( + { ...cid.multihash, digest: null }, + { ...hash, digest: null } + ) cid.toString() assert.deepStrictEqual(cid.toString(), base58btc.baseEncode(hash.bytes)) }) it('throws on invalid BS58Str multihash ', async () => { const msg = 'Non-base58btc character' - assert.throws(() => CID.parse('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII'), msg) + assert.throws( + () => CID.parse('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII'), + msg + ) }) it('throws on trying to create a CIDv0 with a codec other than dag-pb', async () => { @@ -63,14 +82,6 @@ describe('CID', () => { assert.throws(() => CID.create(0, 113, hash), msg) }) - // This was failing for quite some time, test just missed await so it went - // unnoticed. Not sure we still care about checking fourth argument. - // it('throws on trying to pass specific base encoding [deprecated]', async () => { - // const hash = await sha256.digest(textEncoder.encode('abc')) - // const msg = 'No longer supported, cannot specify base encoding in instantiation' - // assert.throws(() => CID.create(0, 112, hash, 'base32'), msg) - // }) - it('throws on trying to base encode CIDv0 in other base than base58btc', async () => { const mhStr = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n' const cid = CID.parse(mhStr) @@ -85,41 +96,59 @@ describe('CID', () => { const bytes = cid.bytes assert.ok(bytes) const str = toHex(bytes) - assert.deepStrictEqual(str, '1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + assert.deepStrictEqual( + str, + '1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' + ) }) it('should construct from an old CID', () => { const cidStr = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n' const oldCid = CID.parse(cidStr) - const newCid = CID.asCID(oldCid) - assert.deepStrictEqual(/** @type {CID} */(newCid).toString(), cidStr) + const newCid = /** @type {CID} */ (CID.asCID(oldCid)) + assert.deepStrictEqual(newCid.toString(), cidStr) }) it('inspect bytes', () => { - const byts = fromHex('1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + const byts = fromHex( + '1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' + ) const inspected = CID.inspectBytes(byts.subarray(0, 10)) // should only need the first few bytes - assert.deepStrictEqual({ - version: 0, - codec: 0x70, - multihashCode: 0x12, - multihashSize: 34, - digestSize: 32, - size: 34 - }, inspected) + assert.deepStrictEqual( + { + version: 0, + codec: 0x70, + multihashCode: 0x12, + multihashSize: 34, + digestSize: 32, + size: 34 + }, + inspected + ) }) describe('decodeFirst', () => { it('no remainder', () => { - const byts = fromHex('1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + const byts = fromHex( + '1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' + ) const [cid, remainder] = CID.decodeFirst(byts) - assert.deepStrictEqual(cid.toString(), 'QmatYkNGZnELf8cAGdyJpUca2PyY4szai3RHyyWofNY1pY') + assert.deepStrictEqual( + cid.toString(), + 'QmatYkNGZnELf8cAGdyJpUca2PyY4szai3RHyyWofNY1pY' + ) assert.deepStrictEqual(remainder.byteLength, 0) }) it('remainder', () => { - const byts = fromHex('1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad0102030405') + const byts = fromHex( + '1220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad0102030405' + ) const [cid, remainder] = CID.decodeFirst(byts) - assert.deepStrictEqual(cid.toString(), 'QmatYkNGZnELf8cAGdyJpUca2PyY4szai3RHyyWofNY1pY') + assert.deepStrictEqual( + cid.toString(), + 'QmatYkNGZnELf8cAGdyJpUca2PyY4szai3RHyyWofNY1pY' + ) assert.deepStrictEqual(toHex(remainder), '0102030405') }) }) @@ -136,8 +165,11 @@ describe('CID', () => { }) it('handles CID (no multibase)', () => { - const cidStr = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u' - const cidBuf = fromHex('017012207252523e6591fb8fe553d67ff55a86f84044b46a3e4176e10c58fa529a4aabd5') + const cidStr = + 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u' + const cidBuf = fromHex( + '017012207252523e6591fb8fe553d67ff55a86f84044b46a3e4176e10c58fa529a4aabd5' + ) const cid = CID.decode(cidBuf) assert.deepStrictEqual(cid.code, 112) assert.deepStrictEqual(cid.version, 1) @@ -152,6 +184,15 @@ describe('CID', () => { equalDigest(cid.multihash, hash) }) + it('CID.createV1', async () => { + const hash = await sha256.digest(textEncoder.encode('abc')) + const cid = CID.createV1(0x71, hash) + + assert.deepStrictEqual(cid.code, 0x71) + assert.deepStrictEqual(cid.version, 1) + equalDigest(cid.multihash, hash) + }) + it('can roundtrip through cid.toString()', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) const cid1 = CID.create(1, 0x71, hash) @@ -162,25 +203,11 @@ describe('CID', () => { assert.deepStrictEqual(cid1.multihash.digest, cid2.multihash.digest) assert.deepStrictEqual(cid1.multihash.bytes, cid2.multihash.bytes) const clear = { digest: null, bytes: null } - assert.deepStrictEqual({ ...cid1.multihash, ...clear }, { ...cid2.multihash, ...clear }) - }) - - /* TODO: after i have a keccak hash for the new interface - it('handles multibyte varint encoded codec codes', () => { - const ethBlockHash = textEncoder.encode('8a8e84c797605fbe75d5b5af107d4220a2db0ad35fd66d9be3d38d87c472b26d', 'hex') - const hash = keccak256.digest(ethBlockHash) - const cid1 = CID.create(1, 0x90, hash) - const cid2 = CID.parse(cid1.toString()) - - assert.deepStrictEqual(cid1.code, 0x90) - assert.deepStrictEqual(cid1.version, 1) - assert.deepStrictEqual(cid1.multihash, hash) - - assert.deepStrictEqual(cid2.code, 0x90) - assert.deepStrictEqual(cid2.version, 1) - assert.deepStrictEqual(cid2.multihash, hash) + assert.deepStrictEqual( + { ...cid1.multihash, ...clear }, + { ...cid2.multihash, ...clear } + ) }) - */ it('.bytes', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) @@ -189,14 +216,23 @@ describe('CID', () => { const bytes = cid.bytes assert.ok(bytes) const str = toHex(bytes) - assert.deepStrictEqual(str, '01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + assert.deepStrictEqual( + str, + '01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' + ) }) it('should construct from an old CID without a multibaseName', () => { - const cidStr = 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u' + const cidStr = + 'bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u' const oldCid = CID.parse(cidStr) - const newCid = CID.asCID(oldCid) - assert.deepStrictEqual(/** @type {CID} */(newCid).toString(), cidStr) + const newCid = /** @type {CID} */ (CID.asCID(oldCid)) + assert.deepStrictEqual(newCid.toString(), cidStr) + }) + + it('.link() should return this CID', () => { + const cid = CID.parse('bafybeidskjjd4zmr7oh6ku6wp72vvbxyibcli2r6if3ocdcy7jjjusvl2u') + assert.equal(cid, cid.link()) }) }) @@ -208,11 +244,17 @@ describe('CID', () => { it('.equals v0 to v0', () => { const cid1 = CID.parse(h1) assert.deepStrictEqual(cid1.equals(CID.parse(h1)), true) - assert.deepStrictEqual(cid1.equals(CID.create(cid1.version, cid1.code, cid1.multihash)), true) + assert.deepStrictEqual( + cid1.equals(CID.create(cid1.version, cid1.code, cid1.multihash)), + true + ) const cid2 = CID.parse(h2) assert.deepStrictEqual(cid1.equals(CID.parse(h2)), false) - assert.deepStrictEqual(cid1.equals(CID.create(cid2.version, cid2.code, cid2.multihash)), false) + assert.deepStrictEqual( + cid1.equals(CID.create(cid2.version, cid2.code, cid2.multihash)), + false + ) }) it('.equals v0 to v1 and vice versa', () => { @@ -230,25 +272,14 @@ describe('CID', () => { const cid1 = CID.parse(h3) assert.deepStrictEqual(cid1.equals(CID.parse(h3)), true) - assert.deepStrictEqual(cid1.equals(CID.create(cid1.version, cid1.code, cid1.multihash)), true) - }) - - it('.isCid', () => { - assert.ok(CID.isCID(CID.parse(h1))) - - assert.ok(!CID.isCID(false)) - - assert.ok(!CID.isCID(textEncoder.encode('hello world'))) - - assert.ok(CID.isCID(CID.parse(h1).toV0())) - - assert.ok(CID.isCID(CID.parse(h1).toV1())) + assert.deepStrictEqual( + cid1.equals(CID.create(cid1.version, cid1.code, cid1.multihash)), + true + ) }) it('works with deepEquals', () => { const ch1 = CID.parse(h1) - // @ts-expect-error - '_baseCache' is private - ch1._baseCache.set('herp', 'derp') assert.deepStrictEqual(ch1, CID.parse(h1)) assert.notDeepEqual(ch1, CID.parse(h2)) }) @@ -271,19 +302,22 @@ describe('CID', () => { ] for (const i of decode) { - const name = `CID.decode(textEncoder.encode(${JSON.stringify(i.toString())}))` + const name = `CID.decode(textEncoder.encode(${JSON.stringify( + i.toString() + )}))` it(name, async () => assert.throws(() => CID.decode(i))) } const create = [ - ...[...parse, ...decode].map(i => [0, 112, i]), - ...[...parse, ...decode].map(i => [1, 112, i]), + ...[...parse, ...decode].map((i) => [0, 112, i]), + ...[...parse, ...decode].map((i) => [1, 112, i]), [18, 112, 'QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L'] ] for (const [version, code, hash] of create) { const form = JSON.stringify(hash.toString()) - const mh = hash instanceof Uint8Array ? `textEncoder.encode(${form})` : form + const mh = + hash instanceof Uint8Array ? `textEncoder.encode(${form})` : form const name = `CID.create(${version}, ${code}, ${mh})` // @ts-expect-error - version issn't always 0|1 it(name, async () => assert.throws(() => CID.create(version, code, hash))) @@ -310,26 +344,32 @@ describe('CID', () => { describe('conversion v0 <-> v1', () => { it('should convert v0 to v1', async () => { const hash = await sha256.digest(textEncoder.encode(`TEST${Date.now()}`)) - const cid = (CID.create(0, 112, hash)).toV1() + const cid = CID.create(0, 112, hash).toV1() assert.deepStrictEqual(cid.version, 1) }) it('should convert v1 to v0', async () => { const hash = await sha256.digest(textEncoder.encode(`TEST${Date.now()}`)) - const cid = (CID.create(1, 112, hash)).toV0() + const cid = CID.create(1, 112, hash).toV0() assert.deepStrictEqual(cid.version, 0) }) it('should not convert v1 to v0 if not dag-pb codec', async () => { const hash = await sha256.digest(textEncoder.encode(`TEST${Date.now()}`)) const cid = CID.create(1, 0x71, hash) - assert.throws(() => cid.toV0(), 'Cannot convert a non dag-pb CID to CIDv0') + assert.throws( + () => cid.toV0(), + 'Cannot convert a non dag-pb CID to CIDv0' + ) }) it('should not convert v1 to v0 if not sha2-256 multihash', async () => { const hash = await sha512.digest(textEncoder.encode(`TEST${Date.now()}`)) const cid = CID.create(1, 112, hash) - assert.throws(() => cid.toV0(), 'Cannot convert non sha2-256 multihash CID to CIDv0') + assert.throws( + () => cid.toV0(), + 'Cannot convert non sha2-256 multihash CID to CIDv0' + ) }) it('should return assert.deepStrictEqual instance when converting v1 to v1', async () => { @@ -344,6 +384,31 @@ describe('CID', () => { const cid = CID.create(0, 112, hash) assert.deepStrictEqual(cid.toV0() === cid, true) }) + + it('should fail to convert unknown version', async () => { + const hash = await sha256.digest(textEncoder.encode(`TEST${Date.now()}`)) + const cid = CID.create(0, 112, hash) + const cid1 = Object.assign(Object.create(Object.getPrototypeOf(cid)), { + ...cid + }) + const cid2 = Object.assign(Object.create(Object.getPrototypeOf(cid)), { + ...cid, + version: 3 + }) + + assert.deepStrictEqual(cid1.toV0().version, 0) + assert.deepStrictEqual(cid1.toV1().version, 1) + assert.equal(cid2.version, 3) + + assert.throws( + () => cid2.toV1(), + /Can not convert CID version 3 to version 1/ + ) + assert.throws( + () => cid2.toV0(), + /Can not convert CID version 3 to version 0/ + ) + }) }) describe('caching', () => { @@ -358,30 +423,63 @@ describe('CID', () => { const hash = await sha256.digest(textEncoder.encode('abc')) const cid = CID.create(1, 112, hash) - // @ts-expect-error - _baseCache is private - assert.deepStrictEqual(cid._baseCache.size, 0) - - assert.deepStrictEqual(cid.toString(base64), 'mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt') - - // @ts-expect-error - _baseCache is private - assert.deepStrictEqual(cid._baseCache.get(base64.prefix), 'mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt') - - // @ts-expect-error - _baseCache is private - assert.deepStrictEqual(cid._baseCache.has(base32.prefix), false) + const b32 = { + ...base32, + callCount: 0, + /** + * @param {Uint8Array} bytes + */ + encode (bytes) { + this.callCount += 1 + return base32.encode(bytes) + '!' + } + } - const base32String = 'bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' - assert.deepStrictEqual(cid.toString(), base32String) + const b64 = { + ...base64, + callCount: 0, + /** + * @param {Uint8Array} bytes + */ + encode (bytes) { + this.callCount += 1 + return base64.encode(bytes) + } + } - // @ts-expect-error - _baseCache is private - assert.deepStrictEqual(cid._baseCache.get(base32.prefix), base32String) - assert.deepStrictEqual(cid.toString(base64), 'mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt') + const base32String = + 'bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' + assert.deepEqual(cid.toString(b32), `${base32String}!`) + assert.deepEqual(b32.callCount, 1) + assert.deepEqual(cid.toString(), `${base32String}!`) + assert.deepEqual(b32.callCount, 1) + + assert.deepStrictEqual( + cid.toString(b64), + 'mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt' + ) + assert.equal(b64.callCount, 1) + assert.deepStrictEqual( + cid.toString(b64), + 'mAXASILp4Fr+PAc/qQUFA3l2uIiOwA2Gjlhd6nLQQ/2HyABWt' + ) + assert.equal(b64.callCount, 1) }) it('should cache string representation when constructed with one', () => { - const base32String = 'bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' + const base32String = + 'bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' const cid = CID.parse(base32String) - // @ts-expect-error - _baseCache is private - assert.deepStrictEqual(cid._baseCache.get(base32.prefix), base32String) + + assert.deepStrictEqual( + cid.toString({ + ...base32, + encode () { + throw Error('Should not call decode') + } + }), + base32String + ) }) }) @@ -390,16 +488,13 @@ describe('CID', () => { const cid = CID.create(1, 112, hash) const json = cid.toJSON() - assert.deepStrictEqual({ ...json, hash: null }, { code: 112, version: 1, hash: null }) + assert.deepStrictEqual( + { ...json, hash: null }, + { code: 112, version: 1, hash: null } + ) assert.ok(equals(json.hash, hash.bytes)) }) - it('isCID', async () => { - const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = CID.create(1, 112, hash) - assert.strictEqual(OLDCID.isCID(cid), false) - }) - it('asCID', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) class IncompatibleCID { @@ -424,12 +519,11 @@ describe('CID', () => { const code = 112 const incompatibleCID = new IncompatibleCID(version, code, hash) - assert.ok(CID.isCID(incompatibleCID)) assert.strictEqual(incompatibleCID.toString(), '[object Object]') // @ts-expect-error - no such method assert.strictEqual(typeof incompatibleCID.toV0, 'undefined') - const cid1 = /** @type {CID} */(CID.asCID(incompatibleCID)) + const cid1 = /** @type {CID} */ (CID.asCID(incompatibleCID)) assert.ok(cid1 instanceof CID) assert.strictEqual(cid1.code, code) assert.strictEqual(cid1.version, version) @@ -450,7 +544,9 @@ describe('CID', () => { const cid4 = CID.asCID(cid3) assert.strictEqual(cid3, cid4) - const cid5 = /** @type {CID} */(CID.asCID(new OLDCID(1, 'raw', Uint8Array.from(hash.bytes)))) + const cid5 = /** @type {CID} */ ( + CID.asCID(new OLDCID(1, 'raw', Uint8Array.from(hash.bytes))) + ) assert.ok(cid5 instanceof CID) assert.strictEqual(cid5.version, 1) assert.ok(equals(cid5.multihash.bytes, hash.bytes)) @@ -458,8 +554,8 @@ describe('CID', () => { }) /** - * @param {CID} x - * @param {CID} y + * @param {API.CID} x + * @param {API.CID} y */ const digestsame = (x, y) => { // @ts-ignore - not sure what this supposed to be @@ -496,7 +592,7 @@ describe('CID', () => { const hash = await sha256.digest(textEncoder.encode('abc')) const cid = CID.create(1, 112, hash) - const parsed = /** @type {CID} */(CID.parse(cid.toString(base58btc))) + const parsed = CID.parse(cid.toString(base58btc)) digestsame(cid, parsed) }) @@ -511,7 +607,8 @@ describe('CID', () => { it('fails to parse base64 encoded CIDv1', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) const cid = CID.create(1, 112, hash) - const msg = 'To parse non base32 or base58btc encoded CID multibase decoder must be provided' + const msg = + 'To parse non base32 or base58btc encoded CID multibase decoder must be provided' assert.throws(() => CID.parse(cid.toString(base64)), msg) }) @@ -526,29 +623,44 @@ describe('CID', () => { }) it('inspect bytes', () => { - const byts = fromHex('01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + const byts = fromHex( + '01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' + ) const inspected = CID.inspectBytes(byts.subarray(0, 10)) // should only need the first few bytes - assert.deepStrictEqual({ - version: 1, - codec: 0x71, - multihashCode: 0x12, - multihashSize: 34, - digestSize: 32, - size: 36 - }, inspected) + assert.deepStrictEqual( + { + version: 1, + codec: 0x71, + multihashCode: 0x12, + multihashSize: 34, + digestSize: 32, + size: 36 + }, + inspected + ) describe('decodeFirst', () => { it('no remainder', () => { - const byts = fromHex('01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + const byts = fromHex( + '01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' + ) const [cid, remainder] = CID.decodeFirst(byts) - assert.deepStrictEqual(cid.toString(), 'bafyreif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu') + assert.deepStrictEqual( + cid.toString(), + 'bafyreif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' + ) assert.deepStrictEqual(remainder.byteLength, 0) }) it('remainder', () => { - const byts = fromHex('01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad0102030405') + const byts = fromHex( + '01711220ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad0102030405' + ) const [cid, remainder] = CID.decodeFirst(byts) - assert.deepStrictEqual(cid.toString(), 'bafyreif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu') + assert.deepStrictEqual( + cid.toString(), + 'bafyreif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu' + ) assert.deepStrictEqual(toHex(remainder), '0102030405') }) }) @@ -556,7 +668,9 @@ describe('CID', () => { it('new CID from old CID', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = /** @type {CID} */ (CID.asCID(new OLDCID(1, 'raw', Uint8Array.from(hash.bytes)))) + const cid = /** @type {CID} */ ( + CID.asCID(new OLDCID(1, 'raw', Uint8Array.from(hash.bytes))) + ) assert.deepStrictEqual(cid.version, 1) equalDigest(cid.multihash, hash) @@ -566,39 +680,16 @@ describe('CID', () => { it('util.inspect', async () => { const hash = await sha256.digest(textEncoder.encode('abc')) const cid = CID.create(1, 112, hash) - // @ts-expect-error - no such method is known - assert.deepStrictEqual(typeof cid[Symbol.for('nodejs.util.inspect.custom')], 'function') - // @ts-expect-error - no such method is known - assert.deepStrictEqual(cid[Symbol.for('nodejs.util.inspect.custom')](), 'CID(bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu)') - }) - - describe('deprecations', async () => { - it('codec', async () => { - const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = CID.create(1, 112, hash) - assert.throws(() => cid.codec, '"codec" property is deprecated, use integer "code" property instead') - // @ts-expect-error - 'string' is not assignable to parameter of type 'number' - assert.throws(() => CID.create(1, 'dag-pb', hash), 'String codecs are no longer supported') - }) - - it('multibaseName', async () => { - const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = CID.create(1, 112, hash) - assert.throws(() => cid.multibaseName, '"multibaseName" property is deprecated') - }) - - it('prefix', async () => { - const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = CID.create(1, 112, hash) - assert.throws(() => cid.prefix, '"prefix" property is deprecated') - }) - - it('toBaseEncodedString()', async () => { - const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = CID.create(1, 112, hash) - // @ts-expect-error - deprecated - assert.throws(() => cid.toBaseEncodedString(), 'Deprecated, use .toString()') - }) + assert.deepStrictEqual( + // @ts-expect-error - no such method is known + typeof cid[Symbol.for('nodejs.util.inspect.custom')], + 'function' + ) + assert.deepStrictEqual( + // @ts-expect-error - no such method is known + cid[Symbol.for('nodejs.util.inspect.custom')](), + 'CID(bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu)' + ) }) it('invalid CID version', async () => { @@ -606,9 +697,15 @@ describe('CID', () => { assert.throws(() => CID.decode(encoded), 'Invalid CID version 2') }) - it('buffer', async () => { - const hash = await sha256.digest(textEncoder.encode('abc')) - const cid = CID.create(1, 112, hash) - assert.throws(() => cid.buffer, 'Deprecated .buffer property, use .bytes to get Uint8Array instead') + it('CID can be moved across JS realms', async () => { + const cid = CID.parse('bafybeif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu') + const { port1: sender, port2: receiver } = new MessageChannel() + sender.postMessage(cid) + const cid2 = await new Promise((resolve) => { + receiver.onmessage = (event) => { resolve(event.data) } + }) + assert.strictEqual(cid2.asCID, cid2) + sender.close() + receiver.close() }) }) diff --git a/test/test-link.spec.js b/test/test-link.spec.js new file mode 100644 index 00000000..67f320fa --- /dev/null +++ b/test/test-link.spec.js @@ -0,0 +1,116 @@ +/* globals describe, it */ + +import * as Link from '../src/link.js' +import chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import { sha256 } from '../src/hashes/sha2.js' + +chai.use(chaiAsPromised) +const { assert } = chai +const utf8 = new TextEncoder() + +const h1 = 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n' +const h4 = 'bafyreidykglsfhoixmivffc5uwhcgshx4j465xwqntbmu43nb2dzqwfvae' +const CBOR = 0x71 +// eslint-disable-next-line +const SHA256 = sha256.code + +const sh1 = /** @type {Link.MultihashDigest} */ ( + Link.parse(h4).multihash +) + +describe('Link', () => { + it('isLink', () => { + assert.equal(Link.isLink(0), false) + assert.equal(Link.isLink(false), false) + }) + + describe('create', () => { + it('create v1', async () => { + const hash = await sha256.digest(utf8.encode('abc')) + const link = Link.create(0x71, hash) + /** @type {0x71} */ + const code = link.code + assert.deepStrictEqual(code, 0x71) + + /** @type {1} */ + const version = link.version + assert.deepEqual(version, 1) + + /** @type {Link.MultihashDigest}> */ + const multihash = link.multihash + assert.deepStrictEqual(multihash, hash) + }) + + it('create v0', async () => { + const hash = await sha256.digest(utf8.encode('abc')) + const link = Link.createLegacy(hash) + + /** @type {0x70} */ + const code = link.code + assert.deepStrictEqual(code, 0x70) + + /** @type {0} */ + const version = link.version + assert.deepEqual(version, 0) + + /** @type {Link.MultihashDigest}> */ + const multihash = link.multihash + assert.deepStrictEqual(multihash, hash) + }) + }) + + describe('parse', () => { + it('can parse any string', () => { + const link = Link.parse(h1) + + /** @type {Link.Link} */ + // @ts-expect-error - types can not be inferred + const t1 = link + assert.ok(t1) + + // it is possible to manually cast + const t2 = /** @type {Link.LegacyLink} */ (link) + assert.ok(t2) + }) + + it('parse retains type info', () => { + const original = Link.create(CBOR, sh1) + const source = Link.format(original) + const link = Link.parse(source) + assert.equal(original.equals(link), true, 'format -> parse roundtrips') + + // ensure that type info is retained + /** @type {Link.Link} */ + const t1 = link + assert.ok(t1) + + // ensurate that you can't cast incorrectly + const t2 = + // @ts-expect-error - version is 1 not 0 + /** @type {Link.Link} */ (link) + assert.ok(t2) + }) + }) +}) + +describe('decode', () => { + it('decode', async () => { + const hash = await sha256.digest(utf8.encode('abc')) + const { bytes } = Link.create(0x71, hash) + + const link = Link.decode(bytes) + + /** @type {0x71} */ + const code = link.code + assert.deepStrictEqual(code, 0x71) + + /** @type {1} */ + const version = link.version + assert.deepEqual(version, 1) + + /** @type {Link.MultihashDigest}> */ + const multihash = link.multihash + assert.deepStrictEqual(multihash, hash) + }) +}) diff --git a/test/test-multibase-spec.js b/test/test-multibase-spec.spec.js similarity index 99% rename from test/test-multibase-spec.js rename to test/test-multibase-spec.spec.js index d49040a6..aa060f76 100644 --- a/test/test-multibase-spec.js +++ b/test/test-multibase-spec.spec.js @@ -1,7 +1,6 @@ /* eslint-env mocha */ -'use strict' -import { bases } from 'multiformats/basics' +import { bases } from '../src/basics.js' import { fromString } from '../src/bytes.js' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' diff --git a/test/test-multibase.js b/test/test-multibase.spec.js similarity index 93% rename from test/test-multibase.js rename to test/test-multibase.spec.js index db4d37b0..fa5a685a 100644 --- a/test/test-multibase.js +++ b/test/test-multibase.spec.js @@ -1,13 +1,13 @@ /* globals describe, it */ import * as bytes from '../src/bytes.js' -import * as b2 from 'multiformats/bases/base2' -import * as b8 from 'multiformats/bases/base8' -import * as b10 from 'multiformats/bases/base10' -import * as b16 from 'multiformats/bases/base16' -import * as b32 from 'multiformats/bases/base32' -import * as b36 from 'multiformats/bases/base36' -import * as b58 from 'multiformats/bases/base58' -import * as b64 from 'multiformats/bases/base64' +import * as b2 from '../src/bases/base2.js' +import * as b8 from '../src/bases/base8.js' +import * as b10 from '../src/bases/base10.js' +import * as b16 from '../src/bases/base16.js' +import * as b32 from '../src/bases/base32.js' +import * as b36 from '../src/bases/base36.js' +import * as b58 from '../src/bases/base58.js' +import * as b64 from '../src/bases/base64.js' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' diff --git a/test/test-multicodec.js b/test/test-multicodec.spec.js similarity index 90% rename from test/test-multicodec.js rename to test/test-multicodec.spec.js index b12d5d17..831c72f7 100644 --- a/test/test-multicodec.js +++ b/test/test-multicodec.spec.js @@ -1,7 +1,7 @@ /* globals describe, it */ import * as bytes from '../src/bytes.js' -import * as raw from 'multiformats/codecs/raw' -import * as json from 'multiformats/codecs/json' +import * as raw from '../src/codecs/raw.js' +import * as json from '../src/codecs/json.js' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' diff --git a/test/test-multihash.js b/test/test-multihash.spec.js similarity index 97% rename from test/test-multihash.js rename to test/test-multihash.spec.js index d65f392d..857fbe10 100644 --- a/test/test-multihash.js +++ b/test/test-multihash.spec.js @@ -1,12 +1,12 @@ /* globals describe, it */ import { fromHex, fromString } from '../src/bytes.js' -import { hash as slSha256 } from '@stablelib/sha256' -import { hash as slSha512 } from '@stablelib/sha512' +import { sha256, sha512 } from '../src/hashes/sha2.js' +import { identity } from '../src/hashes/identity.js' +import { decode as decodeDigest, create as createDigest } from '../src/hashes/digest.js' import valid from './fixtures/valid-multihash.js' import invalid from './fixtures/invalid-multihash.js' -import { sha256, sha512 } from 'multiformats/hashes/sha2' -import { identity } from 'multiformats/hashes/identity' -import { decode as decodeDigest, create as createDigest } from 'multiformats/hashes/digest' +import { hash as slSha256 } from '@stablelib/sha256' +import { hash as slSha512 } from '@stablelib/sha512' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' diff --git a/test/test-traversal.js b/test/test-traversal.spec.js similarity index 86% rename from test/test-traversal.js rename to test/test-traversal.spec.js index b7b191f0..62f9c2bd 100644 --- a/test/test-traversal.js +++ b/test/test-traversal.spec.js @@ -1,13 +1,32 @@ /* globals describe, it */ -import * as codec from 'multiformats/codecs/json' -import * as dagPB from '@ipld/dag-pb' -import { sha256 as hasher } from 'multiformats/hashes/sha2' -import * as main from 'multiformats/block' -import { walk } from 'multiformats/traversal' -import { assert } from 'chai' +import * as codec from '../src/codecs/json.js' +import { sha256 as hasher } from '../src/hashes/sha2.js' +import * as main from '../src/block.js' +import { walk } from '../src/traversal.js' import { fromString } from '../src/bytes.js' +import { assert } from 'chai' -const { createLink, createNode } = dagPB +/** @typedef {import('../src/cid.js').CID} CID */ + +// from dag-pb, simplified +/** + * @param {Uint8Array} data + * @param {{Hash:CID, Name:string, Tsize:number}[]} links + * @returns {{Data:Uint8Array, Links:{Hash:CID, Name:string, Tsize:number}[]}} + */ +function createNode (data, links) { + return { Data: data, Links: links } +} + +/** + * @param {string} name + * @param {number} size + * @param {CID} cid + * @returns {{Hash:CID, Name:string, Tsize:number}} + */ +function createLink (name, size, cid) { + return { Hash: cid, Name: name, Tsize: size } +} describe('traversal', () => { describe('walk', async () => { @@ -43,7 +62,7 @@ describe('traversal', () => { const cidA = blockA.cid /** - * @param {import('multiformats').CID} cid + * @param {CID} cid */ const load = async (cid) => { if (cid.equals(cidE)) { @@ -70,7 +89,7 @@ describe('traversal', () => { */ const loadWrapper = (load, arr = []) => /** - * @param {import('multiformats').CID} cid + * @param {CID} cid */ (cid) => { arr.push(cid.toString()) diff --git a/test/test-varint.js b/test/test-varint.js index e875e95b..bdb0a5d2 100644 --- a/test/test-varint.js +++ b/test/test-varint.js @@ -1,6 +1,6 @@ /* globals describe, it */ -import { varint } from 'multiformats' +import { varint } from '../src/index.js' import chai from 'chai' import chaiAsPromised from 'chai-as-promised' diff --git a/test/ts-use/package.json b/test/ts-use/package.json index c4ef82b8..64a68bf1 100644 --- a/test/ts-use/package.json +++ b/test/ts-use/package.json @@ -1,10 +1,20 @@ { "name": "ts-use", "private": true, + "type": "module", "dependencies": { - "multiformats": "file:../../dist/" + "multiformats": "file:../../" }, "scripts": { - "test": "npm install && npm_config_yes=true npx -p typescript tsc --noEmit" + "test": "npm install && ../../node_modules/.bin/tsc --noEmit" + }, + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "sourceType": "module", + "project": [ + "./test/ts-use/tsconfig.json" + ] + } } -} +} \ No newline at end of file diff --git a/test/ts-use/src/main.ts b/test/ts-use/src/main.ts index d57c5688..ff7de729 100644 --- a/test/ts-use/src/main.ts +++ b/test/ts-use/src/main.ts @@ -9,6 +9,7 @@ const main = async () => { codec: json }) + /* eslint-disable no-console */ console.log(block) } diff --git a/test/ts-use/tsconfig.json b/test/ts-use/tsconfig.json index ff147598..2588ec61 100644 --- a/test/ts-use/tsconfig.json +++ b/test/ts-use/tsconfig.json @@ -1,9 +1,8 @@ { "compilerOptions": { "strict": true, - "moduleResolution": "node", + "module": "NodeNext", "noImplicitAny": true, - "skipLibCheck": true, "incremental": true } } diff --git a/test/tsconfig.json b/test/tsconfig.json deleted file mode 100644 index 8068304b..00000000 --- a/test/tsconfig.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "references": [ - { - "path": "../" - } - ], - "compilerOptions": { - "composite": true, - "allowJs": true, - "checkJs": true, - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": false, - "noImplicitAny": true, - "noImplicitThis": true, - "noFallthroughCasesInSwitch": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "strictFunctionTypes": false, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "strictBindCallApply": true, - "strict": true, - "alwaysStrict": true, - "esModuleInterop": true, - "target": "ES2020", - "moduleResolution": "node", - "declaration": true, - "declarationMap": true, - "skipLibCheck": true, - "stripInternal": true, - "resolveJsonModule": true, - "noEmit": true, - "paths": { - "multiformats": [ - "../types/src/index" - ], - "multiformats/*": [ - "../types/src/*" - ] - } - }, - "include": [ - "." - ], - "exclude": [ - "ts-use/", - "node_modules" - ] -} diff --git a/tsconfig.json b/tsconfig.json index 43a6feb3..d8dbe630 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,39 +1,26 @@ { + "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "allowJs": true, - "checkJs": true, - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": false, - "noImplicitAny": true, - "noImplicitThis": true, - "noFallthroughCasesInSwitch": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "strictFunctionTypes": false, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "strictBindCallApply": true, - "strict": true, - "alwaysStrict": true, - "esModuleInterop": true, - "target": "ES2020", - "moduleResolution": "node", - "declaration": true, - "declarationMap": true, - "outDir": "types", - "skipLibCheck": true, - "stripInternal": true, - "resolveJsonModule": true, + "outDir": "dist/types", "emitDeclarationOnly": true, - "baseUrl": ".", - "composite": true + "importsNotUsedAsValues": "preserve", + "paths": { + "multiformats": [ + "./src/index.js" + ], + "multiformats/interface": [ + "./src/interface" + ], + "multiformats/hashes/interface": [ + "./src/hashes/interface" + ], + "multiformats/*": [ + "./src/*.js" + ] + } }, "include": [ - "src" - ], - "exclude": [ - "vendor", - "node_modules" - ], - "compileOnSave": false -} + "src", + "test" + ] +} \ No newline at end of file diff --git a/vendor/base-x.d.ts b/vendor/base-x.d.ts index 789d1e8f..a8665dc9 100644 --- a/vendor/base-x.d.ts +++ b/vendor/base-x.d.ts @@ -1,9 +1,8 @@ -declare function base(ALPHABET: string, name: string): base.BaseConverter; -export = base; -declare namespace base { - interface BaseConverter { - encode(buffer: Uint8Array | number[]): string; - decodeUnsafe(string: string): Uint8Array | undefined; - decode(string: string): Uint8Array; - } + +export interface BaseConverter { + encode(buffer: Uint8Array | number[]): string; + decodeUnsafe(string: string): Uint8Array | undefined; + decode(string: string): Uint8Array; } + +export default function base(ALPHABET: string, name: string): BaseConverter diff --git a/vendor/varint.d.ts b/vendor/varint.d.ts index 11e2e2d7..f5a8a021 100644 --- a/vendor/varint.d.ts +++ b/vendor/varint.d.ts @@ -3,44 +3,47 @@ // Definitions by: David Brockman Smoliansky // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -/// +interface Varint { + encode: { + /** + * Encodes `num` into `buffer` starting at `offset`. returns `buffer`, with the encoded varint written into it. + * `varint.encode.bytes` will now be set to the number of bytes modified. + */ + (num: number, buffer: Uint8Array, offset?: number): Buffer; -export const encode: { - /** - * Encodes `num` into `buffer` starting at `offset`. returns `buffer`, with the encoded varint written into it. - * `varint.encode.bytes` will now be set to the number of bytes modified. - */ - (num: number, buffer: Uint8Array, offset?: number): Buffer; + /** + * Encodes `num` into `array` starting at `offset`. returns `array`, with the encoded varint written into it. + * If `array` is not provided, it will default to a new array. + * `varint.encode.bytes` will now be set to the number of bytes modified. + */ + (num: number, array?: number[], offset?: number): number[] - /** - * Encodes `num` into `array` starting at `offset`. returns `array`, with the encoded varint written into it. - * If `array` is not provided, it will default to a new array. - * `varint.encode.bytes` will now be set to the number of bytes modified. - */ - (num: number, array?: number[], offset?: number): number[] + /** + * Similar to `decode.bytes` when encoding a number it can be useful to know how many bytes where written (especially if you pass an output array). + * You can access this via `varint.encode.bytes` which holds the number of bytes written in the last encode. + */ + bytes: number + }, - /** - * Similar to `decode.bytes` when encoding a number it can be useful to know how many bytes where written (especially if you pass an output array). - * You can access this via `varint.encode.bytes` which holds the number of bytes written in the last encode. - */ - bytes: number -} + decode: { + /** + * Decodes `data`, which can be either a buffer or array of integers, from position `offset` or default 0 and returns the decoded original integer. + * Throws a `RangeError` when `data` does not represent a valid encoding. + */ + (buf: Uint8Array | number[], offset?: number): number -export const decode: { - /** - * Decodes `data`, which can be either a buffer or array of integers, from position `offset` or default 0 and returns the decoded original integer. - * Throws a `RangeError` when `data` does not represent a valid encoding. - */ - (buf: Uint8Array | number[], offset?: number): number + /** + * If you also require the length (number of bytes) that were required to decode the integer you can access it via `varint.decode.bytes`. + * This is an integer property that will tell you the number of bytes that the last .decode() call had to use to decode. + */ + bytes: number + }, /** - * If you also require the length (number of bytes) that were required to decode the integer you can access it via `varint.decode.bytes`. - * This is an integer property that will tell you the number of bytes that the last .decode() call had to use to decode. + * returns the number of bytes this number will be encoded as, up to a maximum of 8. */ - bytes: number + encodingLength(num: number): number } -/** - * returns the number of bytes this number will be encoded as, up to a maximum of 8. - */ -export function encodingLength(num: number): number +declare const varint:Varint +export default varint