diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml
index 2c7a14bb..359eb975 100644
--- a/.github/workflows/js-test-and-release.yml
+++ b/.github/workflows/js-test-and-release.yml
@@ -9,7 +9,9 @@ on:
permissions:
contents: write
+ id-token: write
packages: write
+ pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml
new file mode 100644
index 00000000..bd00f090
--- /dev/null
+++ b/.github/workflows/semantic-pull-request.yml
@@ -0,0 +1,12 @@
+name: Semantic PR
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - edited
+ - synchronize
+
+jobs:
+ main:
+ uses: pl-strflt/.github/.github/workflows/reusable-semantic-pull-request.yml@v0.3
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 00000000..16d65d72
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,13 @@
+name: Close and mark stale issue
+
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+permissions:
+ issues: write
+ pull-requests: write
+
+jobs:
+ stale:
+ uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3
diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml
deleted file mode 100644
index ae9947ea..00000000
--- a/.github/workflows/typecheck.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-on:
- push:
- branches:
- - master
- - main
- - default
- pull_request:
- branches:
- - "**"
-
-name: Typecheck
-jobs:
- check:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [12.x]
- steps:
- - uses: actions/checkout@v4
- - name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v4.0.0
- with:
- node-version: ${{ matrix.node-version }}
- - name: Install dependencies
- run: npm install
- - name: Typecheck
- uses: gozala/typescript-error-reporter-action@v1.0.9
- with:
- project: tsconfig.json
diff --git a/.gitignore b/.gitignore
index dec11b7f..7ad9e674 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,9 @@
-.coverage
-package-lock.json
node_modules
-.DS_Store
-yarn.lock
-types
-test/ts-use/tsconfig.tsbuildinfo
-types/tsconfig.tsbuildinfo
-*.log
+build
dist
.docs
+.coverage
+node_modules
+package-lock.json
+yarn.lock
+.vscode
diff --git a/README.md b/README.md
index 1ed02dda..260df917 100644
--- a/README.md
+++ b/README.md
@@ -1,51 +1,16 @@
-# 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/actions/workflow/status/multiformats/js-multiformats/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/multiformats/js-multiformats/actions/workflows/js-test-and-release.yml?query=branch%3Amaster)
> Interface for multihash, multicodec, multibase and CID
-## Table of contents
-
-- [Install](#install)
- - [Browser `
-```
-
-## Interfaces
+# About
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.
-```js
+```TypeScript
import { CID } from 'multiformats/cid'
import * as json from 'multiformats/codecs/json'
import { sha256 } from 'multiformats/hashes/sha2'
@@ -57,9 +22,9 @@ const cid = CID.create(1, json.code, hash)
//> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
```
-### Creating Blocks
+## Creating Blocks
-```js
+```TypeScript
import * as Block from 'multiformats/block'
import * as codec from '@ipld/dag-cbor'
import { sha256 as hasher } from 'multiformats/hashes/sha2'
@@ -80,11 +45,11 @@ block = await Block.decode({ bytes: block.bytes, codec, hasher })
block = await Block.create({ bytes: block.bytes, cid: block.cid, codec, hasher })
```
-### Multibase Encoders / Decoders / Codecs
+## Multibase Encoders / Decoders / Codecs
CIDs can be serialized to string representation using multibase encoders that implement [`MultibaseEncoder`](https://github.com/multiformats/js-multiformats/blob/master/src/bases/interface.ts) interface. This library provides quite a few implementations that can be imported:
-```js
+```TypeScript
import { base64 } from "multiformats/bases/base64"
cid.toString(base64.encoder)
//> 'mAYAEEiCTojlxqRTl6svwqNJRVM2jCcPBxy+7mRTUfGDzy2gViA'
@@ -92,7 +57,7 @@ cid.toString(base64.encoder)
Parsing CID string serialized CIDs requires multibase decoder that implements [`MultibaseDecoder`](https://github.com/multiformats/js-multiformats/blob/master/src/bases/interface.ts) interface. This library provides a decoder for every encoder it provides:
-```js
+```TypeScript
CID.parse('mAYAEEiCTojlxqRTl6svwqNJRVM2jCcPBxy+7mRTUfGDzy2gViA', base64.decoder)
//> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
```
@@ -102,7 +67,7 @@ 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
+```TypeScript
cid.toString(base64)
CID.parse(cid.toString(base64), base64)
```
@@ -111,7 +76,7 @@ CID.parse(cid.toString(base64), base64)
multibase codecs so that CIDs can be base serialized to (version specific)
default base encoding and parsed without having to supply base encoders/decoders:
-```js
+```TypeScript
const v1 = CID.parse('bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea')
v1.toString()
//> 'bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea'
@@ -123,17 +88,13 @@ v0.toV1().toString()
//> 'bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku'
```
-### Multicodec Encoders / Decoders / Codecs
+## Multicodec Encoders / Decoders / Codecs
This library defines [`BlockEncoder`, `BlockDecoder` and `BlockCodec` interfaces](https://github.com/multiformats/js-multiformats/blob/master/src/codecs/interface.ts).
Codec implementations should conform to the `BlockCodec` interface which implements both `BlockEncoder` and `BlockDecoder`.
Here is an example implementation of JSON `BlockCodec`.
-```js
-/**
- * @template T
- * @type {BlockCodec<0x0200, T>}
- */
+```TypeScript
export const { name, code, encode, decode } = {
name: 'json',
code: 0x0200,
@@ -142,11 +103,11 @@ export const { name, code, encode, decode } = {
}
```
-### Multihash Hashers
+## Multihash Hashers
This library defines [`MultihashHasher` and `MultihashDigest` interfaces](https://github.com/multiformats/js-multiformats/blob/master/src/hashes/interface.ts) and convinient function for implementing them:
-```js
+```TypeScript
import * as hasher from 'multiformats/hashes/hasher'
const sha256 = hasher.from({
@@ -164,13 +125,13 @@ CID.create(1, json.code, hash)
//> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
```
-### Traversal
+## Traversal
This library contains higher-order functions for traversing graphs of data easily.
`walk()` walks through the links in each block of a DAG calling a user-supplied loader function for each one, in depth-first order with no duplicate block visits. The loader should return a `Block` object and can be used to inspect and collect block ordering for a full DAG walk. The loader should `throw` on error, and return `null` if a block should be skipped by `walk()`.
-```js
+```TypeScript
import { walk } from 'multiformats/traversal'
import * as Block from 'multiformats/block'
import * as codec from 'multiformats/codecs/json'
@@ -243,17 +204,31 @@ Other (less useful) bases implemented in [multiformats/js-multiformats](https://
| `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) |
-## API Docs
+# Install
+
+```console
+$ npm i multiformats
+```
+
+## Browser `
+```
+
+# API Docs
-
-## License
+# License
Licensed under either of
- 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 3843b15f..0edc8014 100644
--- a/package.json
+++ b/package.json
@@ -17,173 +17,158 @@
"ipld",
"multiformats"
],
- "engines": {
- "node": ">=16.0.0",
- "npm": ">=7.0.0"
- },
"type": "module",
- "types": "./dist/types/src/index.d.ts",
+ "types": "./dist/src/index.d.ts",
"typesVersions": {
"*": {
"*": [
"*",
- "dist/types/*",
- "dist/types/src/*",
- "dist/types/src/*/index"
+ "dist/*",
+ "dist/src/*",
+ "dist/src/*/index"
],
"src/*": [
"*",
- "dist/types/*",
- "dist/types/src/*",
- "dist/types/src/*/index"
+ "dist/*",
+ "dist/src/*",
+ "dist/src/*/index"
]
}
},
"files": [
- "CHANGELOG.md",
- "examples",
- "LICENSE*",
"src",
- "test",
- "tsconfig.json",
"dist",
- "vendor",
- "!**/*.tsbuildinfo",
- "!test/ts-use/node_modules"
+ "!dist/test",
+ "!**/*.tsbuildinfo"
],
"exports": {
".": {
- "types": "./dist/types/src/index.d.ts",
- "import": "./src/index.js"
+ "types": "./dist/src/index.d.ts",
+ "import": "./dist/src/index.js"
},
"./bases/base10": {
- "types": "./dist/types/src/bases/base10.d.ts",
- "import": "./src/bases/base10.js"
+ "types": "./dist/src/bases/base10.d.ts",
+ "import": "./dist/src/bases/base10.js"
},
"./bases/base16": {
- "types": "./dist/types/src/bases/base16.d.ts",
- "import": "./src/bases/base16.js"
+ "types": "./dist/src/bases/base16.d.ts",
+ "import": "./dist/src/bases/base16.js"
},
"./bases/base2": {
- "types": "./dist/types/src/bases/base2.d.ts",
- "import": "./src/bases/base2.js"
+ "types": "./dist/src/bases/base2.d.ts",
+ "import": "./dist/src/bases/base2.js"
},
"./bases/base256emoji": {
- "types": "./dist/types/src/bases/base256emoji.d.ts",
- "import": "./src/bases/base256emoji.js"
+ "types": "./dist/src/bases/base256emoji.d.ts",
+ "import": "./dist/src/bases/base256emoji.js"
},
"./bases/base32": {
- "types": "./dist/types/src/bases/base32.d.ts",
- "import": "./src/bases/base32.js"
+ "types": "./dist/src/bases/base32.d.ts",
+ "import": "./dist/src/bases/base32.js"
},
"./bases/base36": {
- "types": "./dist/types/src/bases/base36.d.ts",
- "import": "./src/bases/base36.js"
+ "types": "./dist/src/bases/base36.d.ts",
+ "import": "./dist/src/bases/base36.js"
},
"./bases/base58": {
- "types": "./dist/types/src/bases/base58.d.ts",
- "import": "./src/bases/base58.js"
+ "types": "./dist/src/bases/base58.d.ts",
+ "import": "./dist/src/bases/base58.js"
},
"./bases/base64": {
- "types": "./dist/types/src/bases/base64.d.ts",
- "import": "./src/bases/base64.js"
+ "types": "./dist/src/bases/base64.d.ts",
+ "import": "./dist/src/bases/base64.js"
},
"./bases/base8": {
- "types": "./dist/types/src/bases/base8.d.ts",
- "import": "./src/bases/base8.js"
+ "types": "./dist/src/bases/base8.d.ts",
+ "import": "./dist/src/bases/base8.js"
},
"./bases/identity": {
- "types": "./dist/types/src/bases/identity.d.ts",
- "import": "./src/bases/identity.js"
+ "types": "./dist/src/bases/identity.d.ts",
+ "import": "./dist/src/bases/identity.js"
},
"./bases/interface": {
- "types": "./dist/types/src/bases/interface.d.ts",
- "import": "./src/bases/interface.js"
+ "types": "./dist/src/bases/interface.d.ts",
+ "import": "./dist/src/bases/interface.js"
},
"./basics": {
- "types": "./dist/types/src/basics.d.ts",
- "import": "./src/basics.js"
+ "types": "./dist/src/basics.d.ts",
+ "import": "./dist/src/basics.js"
},
"./block": {
- "types": "./dist/types/src/block.d.ts",
- "import": "./src/block.js"
+ "types": "./dist/src/block.d.ts",
+ "import": "./dist/src/block.js"
},
"./block/interface": {
- "types": "./dist/types/src/block/interface.d.ts",
- "import": "./src/block/interface.js"
+ "types": "./dist/src/block/interface.d.ts",
+ "import": "./dist/src/block/interface.js"
},
"./bytes": {
- "types": "./dist/types/src/bytes.d.ts",
- "import": "./src/bytes.js"
+ "types": "./dist/src/bytes.d.ts",
+ "import": "./dist/src/bytes.js"
},
"./cid": {
- "types": "./dist/types/src/cid.d.ts",
- "import": "./src/cid.js"
+ "types": "./dist/src/cid.d.ts",
+ "import": "./dist/src/cid.js"
},
"./codecs/interface": {
- "types": "./dist/types/src/codecs/interface.d.ts",
- "import": "./src/codecs/interface.js"
+ "types": "./dist/src/codecs/interface.d.ts",
+ "import": "./dist/src/codecs/interface.js"
},
"./codecs/json": {
- "types": "./dist/types/src/codecs/json.d.ts",
- "import": "./src/codecs/json.js"
+ "types": "./dist/src/codecs/json.d.ts",
+ "import": "./dist/src/codecs/json.js"
},
"./codecs/raw": {
- "types": "./dist/types/src/codecs/raw.d.ts",
- "import": "./src/codecs/raw.js"
+ "types": "./dist/src/codecs/raw.d.ts",
+ "import": "./dist/src/codecs/raw.js"
},
"./hashes/digest": {
- "types": "./dist/types/src/hashes/digest.d.ts",
- "import": "./src/hashes/digest.js"
+ "types": "./dist/src/hashes/digest.d.ts",
+ "import": "./dist/src/hashes/digest.js"
},
"./hashes/hasher": {
- "types": "./dist/types/src/hashes/hasher.d.ts",
- "import": "./src/hashes/hasher.js"
+ "types": "./dist/src/hashes/hasher.d.ts",
+ "import": "./dist/src/hashes/hasher.js"
},
"./hashes/identity": {
- "types": "./dist/types/src/hashes/identity.d.ts",
- "import": "./src/hashes/identity.js"
+ "types": "./dist/src/hashes/identity.d.ts",
+ "import": "./dist/src/hashes/identity.js"
},
"./hashes/interface": {
- "types": "./dist/types/src/hashes/interface.d.ts",
- "import": "./src/hashes/interface.js"
+ "types": "./dist/src/hashes/interface.d.ts",
+ "import": "./dist/src/hashes/interface.js"
},
"./hashes/sha1": {
"types": "./dist/types/src/hashes/sha1.d.ts",
- "browser": "./src/hashes/sha1-browser.js",
- "import": "./src/hashes/sha1.js"
+ "browser": "./dist/src/hashes/sha1-browser.js",
+ "import": "./dist/src/hashes/sha1.js"
},
"./hashes/sha2": {
- "types": "./dist/types/src/hashes/sha2.d.ts",
- "browser": "./src/hashes/sha2-browser.js",
- "import": "./src/hashes/sha2.js"
+ "types": "./dist/src/hashes/sha2.d.ts",
+ "browser": "./dist/src/hashes/sha2-browser.js",
+ "import": "./dist/src/hashes/sha2.js"
},
"./interface": {
- "types": "./dist/types/src/interface.d.ts",
- "import": "./src/interface.js"
+ "types": "./dist/src/interface.d.ts",
+ "import": "./dist/src/interface.js"
},
"./link": {
- "types": "./dist/types/src/link.d.ts",
- "import": "./src/link.js"
+ "types": "./dist/src/link.d.ts",
+ "import": "./dist/src/link.js"
},
"./link/interface": {
- "types": "./dist/types/src/link/interface.d.ts",
- "import": "./src/link/interface.js"
+ "types": "./dist/src/link/interface.d.ts",
+ "import": "./dist/src/link/interface.js"
},
"./traversal": {
- "types": "./dist/types/src/traversal.d.ts",
- "import": "./src/traversal.js"
+ "types": "./dist/src/traversal.d.ts",
+ "import": "./dist/src/traversal.js"
}
},
- "browser": {
- "./hashes/sha1": "./src/hashes/sha1-browser.js",
- "./src/hashes/sha1.js": "./src/hashes/sha1-browser.js",
- "./hashes/sha2": "./src/hashes/sha2-browser.js",
- "./src/hashes/sha2.js": "./src/hashes/sha2-browser.js"
- },
"eslintConfig": {
"extends": "ipfs",
"parserOptions": {
+ "project": true,
"sourceType": "module"
}
},
@@ -278,8 +263,7 @@
"build": "aegir build",
"release": "aegir release",
"docs": "aegir docs",
- "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": "npm run lint && npm run test:node && npm run test:chrome",
"test:node": "aegir test -t node --cov",
"test:chrome": "aegir test -t browser --cov",
"test:chrome-webworker": "aegir test -t webworker",
@@ -303,5 +287,11 @@
"browser"
]
}
+ },
+ "browser": {
+ "./hashes/sha1": "./dist/src/hashes/sha1-browser.js",
+ "./dist/src/hashes/sha1.js": "./dist/src/hashes/sha1-browser.js",
+ "./hashes/sha2": "./dist/src/hashes/sha2-browser.js",
+ "./dist/src/hashes/sha2.js": "./dist/src/hashes/sha2-browser.js"
}
}
diff --git a/src/bases/base.js b/src/bases/base.js
deleted file mode 100644
index 4ace5ff4..00000000
--- a/src/bases/base.js
+++ /dev/null
@@ -1,347 +0,0 @@
-import basex from '../../vendor/base-x.js'
-import { coerce } from '../bytes.js'
-// 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 {API.MultibaseEncoder}
- * @implements {API.BaseEncoder}
- */
-class Encoder {
- /**
- * @param {Base} name
- * @param {Prefix} prefix
- * @param {(bytes:Uint8Array) => string} baseEncode
- */
- constructor (name, prefix, baseEncode) {
- this.name = name
- this.prefix = prefix
- this.baseEncode = baseEncode
- }
-
- /**
- * @param {Uint8Array} bytes
- * @returns {API.Multibase}
- */
- encode (bytes) {
- if (bytes instanceof Uint8Array) {
- return `${this.prefix}${this.baseEncode(bytes)}`
- } else {
- throw Error('Unknown type, must be binary type')
- }
- }
-}
-
-/**
- * @template {string} Prefix
- */
-/**
- * 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 {API.MultibaseDecoder}
- * @implements {API.UnibaseDecoder}
- * @implements {API.BaseDecoder}
- */
-class Decoder {
- /**
- * @param {Base} name
- * @param {Prefix} prefix
- * @param {(text:string) => Uint8Array} baseDecode
- */
- constructor (name, prefix, baseDecode) {
- this.name = name
- this.prefix = prefix
- /* c8 ignore next 3 */
- if (prefix.codePointAt(0) === undefined) {
- throw new Error('Invalid prefix character')
- }
- /** @private */
- this.prefixCodePoint = /** @type {number} */ (prefix.codePointAt(0))
- this.baseDecode = baseDecode
- }
-
- /**
- * @param {string} text
- */
- decode (text) {
- if (typeof text === 'string') {
- if (text.codePointAt(0) !== this.prefixCodePoint) {
- throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`)
- }
- return this.baseDecode(text.slice(this.prefix.length))
- } else {
- throw Error('Can only multibase decode strings')
- }
- }
-
- /**
- * @template {string} OtherPrefix
- * @param {API.UnibaseDecoder|ComposedDecoder} decoder
- * @returns {ComposedDecoder}
- */
- or (decoder) {
- return or(this, decoder)
- }
-}
-
-/**
- * @template {string} Prefix
- * @typedef {Record>} Decoders
- */
-
-/**
- * @template {string} Prefix
- * @implements {API.MultibaseDecoder}
- * @implements {API.CombobaseDecoder}
- */
-class ComposedDecoder {
- /**
- * @param {Decoders} decoders
- */
- constructor (decoders) {
- this.decoders = decoders
- }
-
- /**
- * @template {string} OtherPrefix
- * @param {API.UnibaseDecoder|ComposedDecoder} decoder
- * @returns {ComposedDecoder}
- */
- or (decoder) {
- return or(this, decoder)
- }
-
- /**
- * @param {string} input
- * @returns {Uint8Array}
- */
- decode (input) {
- const prefix = /** @type {Prefix} */ (input[0])
- const decoder = this.decoders[prefix]
- if (decoder) {
- return decoder.decode(input)
- } else {
- throw RangeError(`Unable to decode multibase string ${JSON.stringify(input)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`)
- }
- }
-}
-
-/**
- * @template {string} L
- * @template {string} R
- * @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 API.UnibaseDecoder */(left).prefix]: left }),
- ...(right.decoders || { [/** @type API.UnibaseDecoder */(right).prefix]: right })
-}))
-
-/**
- * @class
- * @template {string} Base
- * @template {string} Prefix
- * @implements {API.MultibaseCodec}
- * @implements {API.MultibaseEncoder}
- * @implements {API.MultibaseDecoder}
- * @implements {API.BaseCodec}
- * @implements {API.BaseEncoder}
- * @implements {API.BaseDecoder}
- */
-export class Codec {
- /**
- * @param {Base} name
- * @param {Prefix} prefix
- * @param {(bytes:Uint8Array) => string} baseEncode
- * @param {(text:string) => Uint8Array} baseDecode
- */
- constructor (name, prefix, baseEncode, baseDecode) {
- this.name = name
- this.prefix = prefix
- this.baseEncode = baseEncode
- this.baseDecode = baseDecode
- this.encoder = new Encoder(name, prefix, baseEncode)
- this.decoder = new Decoder(name, prefix, baseDecode)
- }
-
- /**
- * @param {Uint8Array} input
- */
- encode (input) {
- return this.encoder.encode(input)
- }
-
- /**
- * @param {string} input
- */
- decode (input) {
- return this.decoder.decode(input)
- }
-}
-
-/**
- * @template {string} Base
- * @template {string} Prefix
- * @param {object} options
- * @param {Base} options.name
- * @param {Prefix} options.prefix
- * @param {(bytes:Uint8Array) => string} options.encode
- * @param {(input:string) => Uint8Array} options.decode
- * @returns {Codec}
- */
-export const from = ({ name, prefix, encode, decode }) =>
- new Codec(name, prefix, encode, decode)
-
-/**
- * @template {string} Base
- * @template {string} Prefix
- * @param {object} options
- * @param {Base} options.name
- * @param {Prefix} options.prefix
- * @param {string} options.alphabet
- * @returns {Codec}
- */
-export const baseX = ({ prefix, name, alphabet }) => {
- const { encode, decode } = basex(alphabet, name)
- return from({
- prefix,
- name,
- encode,
- /**
- * @param {string} text
- */
- decode: text => coerce(decode(text))
- })
-}
-
-/**
- * @param {string} string
- * @param {string} alphabet
- * @param {number} bitsPerChar
- * @param {string} name
- * @returns {Uint8Array}
- */
-const decode = (string, alphabet, bitsPerChar, name) => {
- // Build the character lookup table:
- /** @type {Record} */
- const codes = {}
- for (let i = 0; i < alphabet.length; ++i) {
- codes[alphabet[i]] = i
- }
-
- // Count the padding bytes:
- let end = string.length
- while (string[end - 1] === '=') {
- --end
- }
-
- // Allocate the output:
- const out = new Uint8Array((end * bitsPerChar / 8) | 0)
-
- // Parse the data:
- let bits = 0 // Number of bits currently in the buffer
- let buffer = 0 // Bits waiting to be written out, MSB first
- let written = 0 // Next byte to write
- for (let i = 0; i < end; ++i) {
- // Read one character from the string:
- const value = codes[string[i]]
- if (value === undefined) {
- throw new SyntaxError(`Non-${name} character`)
- }
-
- // Append the bits to the buffer:
- buffer = (buffer << bitsPerChar) | value
- bits += bitsPerChar
-
- // Write out some bits if the buffer has a byte's worth:
- if (bits >= 8) {
- bits -= 8
- out[written++] = 0xff & (buffer >> bits)
- }
- }
-
- // Verify that we have received just enough bits:
- if (bits >= bitsPerChar || 0xff & (buffer << (8 - bits))) {
- throw new SyntaxError('Unexpected end of data')
- }
-
- return out
-}
-
-/**
- * @param {Uint8Array} data
- * @param {string} alphabet
- * @param {number} bitsPerChar
- * @returns {string}
- */
-const encode = (data, alphabet, bitsPerChar) => {
- const pad = alphabet[alphabet.length - 1] === '='
- const mask = (1 << bitsPerChar) - 1
- let out = ''
-
- let bits = 0 // Number of bits currently in the buffer
- let buffer = 0 // Bits waiting to be written out, MSB first
- for (let i = 0; i < data.length; ++i) {
- // Slurp data into the buffer:
- buffer = (buffer << 8) | data[i]
- bits += 8
-
- // Write out as much as we can:
- while (bits > bitsPerChar) {
- bits -= bitsPerChar
- out += alphabet[mask & (buffer >> bits)]
- }
- }
-
- // Partial character:
- if (bits) {
- out += alphabet[mask & (buffer << (bitsPerChar - bits))]
- }
-
- // Add padding characters until we hit a byte boundary:
- if (pad) {
- while ((out.length * bitsPerChar) & 7) {
- out += '='
- }
- }
-
- return out
-}
-
-/**
- * RFC4648 Factory
- *
- * @template {string} Base
- * @template {string} Prefix
- * @param {object} options
- * @param {Base} options.name
- * @param {Prefix} options.prefix
- * @param {string} options.alphabet
- * @param {number} options.bitsPerChar
- */
-export const rfc4648 = ({ name, prefix, bitsPerChar, alphabet }) => {
- return from({
- prefix,
- name,
- encode (input) {
- return encode(input, alphabet, bitsPerChar)
- },
- decode (input) {
- return decode(input, alphabet, bitsPerChar, name)
- }
- })
-}
diff --git a/src/bases/base.ts b/src/bases/base.ts
new file mode 100644
index 00000000..03be1c84
--- /dev/null
+++ b/src/bases/base.ts
@@ -0,0 +1,237 @@
+import { coerce } from '../bytes.js'
+import basex from '../vendor/base-x.js'
+import type { BaseCodec, BaseDecoder, BaseEncoder, CombobaseDecoder, Multibase, MultibaseCodec, MultibaseDecoder, MultibaseEncoder, UnibaseDecoder } from './interface.js'
+
+interface EncodeFn { (bytes: Uint8Array): string }
+interface DecodeFn { (text: string): Uint8Array }
+
+/**
+ * Class represents both BaseEncoder and MultibaseEncoder meaning it
+ * can be used to encode to multibase or base encode without multibase
+ * prefix.
+ */
+class Encoder implements MultibaseEncoder, BaseEncoder {
+ readonly name: Base
+ readonly prefix: Prefix
+ readonly baseEncode: EncodeFn
+
+ constructor (name: Base, prefix: Prefix, baseEncode: EncodeFn) {
+ this.name = name
+ this.prefix = prefix
+ this.baseEncode = baseEncode
+ }
+
+ encode (bytes: Uint8Array): Multibase {
+ if (bytes instanceof Uint8Array) {
+ return `${this.prefix}${this.baseEncode(bytes)}`
+ } else {
+ throw Error('Unknown type, must be binary type')
+ }
+ }
+}
+
+/**
+ * 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 Decoder implements MultibaseDecoder, UnibaseDecoder, BaseDecoder {
+ readonly name: Base
+ readonly prefix: Prefix
+ readonly baseDecode: DecodeFn
+ private readonly prefixCodePoint: number
+
+ constructor (name: Base, prefix: Prefix, baseDecode: DecodeFn) {
+ this.name = name
+ this.prefix = prefix
+ /* c8 ignore next 3 */
+ if (prefix.codePointAt(0) === undefined) {
+ throw new Error('Invalid prefix character')
+ }
+ this.prefixCodePoint = prefix.codePointAt(0) as number
+ this.baseDecode = baseDecode
+ }
+
+ decode (text: string): Uint8Array {
+ if (typeof text === 'string') {
+ if (text.codePointAt(0) !== this.prefixCodePoint) {
+ throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`)
+ }
+ return this.baseDecode(text.slice(this.prefix.length))
+ } else {
+ throw Error('Can only multibase decode strings')
+ }
+ }
+
+ or (decoder: UnibaseDecoder | ComposedDecoder): ComposedDecoder {
+ return or(this, decoder)
+ }
+}
+
+type Decoders = Record>
+
+class ComposedDecoder implements MultibaseDecoder, CombobaseDecoder {
+ readonly decoders: Decoders
+
+ constructor (decoders: Decoders) {
+ this.decoders = decoders
+ }
+
+ or (decoder: UnibaseDecoder | ComposedDecoder): ComposedDecoder {
+ return or(this, decoder)
+ }
+
+ decode (input: string): Uint8Array {
+ const prefix = input[0] as Prefix
+ const decoder = this.decoders[prefix]
+ if (decoder != null) {
+ return decoder.decode(input)
+ } else {
+ throw RangeError(`Unable to decode multibase string ${JSON.stringify(input)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`)
+ }
+ }
+}
+
+export function or (left: UnibaseDecoder | CombobaseDecoder, right: UnibaseDecoder | CombobaseDecoder): ComposedDecoder {
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
+ return new ComposedDecoder({
+ ...(left.decoders ?? { [(left as UnibaseDecoder).prefix]: left }),
+ ...(right.decoders ?? { [(right as UnibaseDecoder).prefix]: right })
+ } as Decoders)
+}
+
+export class Codec implements MultibaseCodec, MultibaseEncoder, MultibaseDecoder, BaseCodec, BaseEncoder, BaseDecoder {
+ readonly name: Base
+ readonly prefix: Prefix
+ readonly baseEncode: EncodeFn
+ readonly baseDecode: DecodeFn
+ readonly encoder: Encoder
+ readonly decoder: Decoder
+
+ constructor (name: Base, prefix: Prefix, baseEncode: EncodeFn, baseDecode: DecodeFn) {
+ this.name = name
+ this.prefix = prefix
+ this.baseEncode = baseEncode
+ this.baseDecode = baseDecode
+ this.encoder = new Encoder(name, prefix, baseEncode)
+ this.decoder = new Decoder(name, prefix, baseDecode)
+ }
+
+ encode (input: Uint8Array): string {
+ return this.encoder.encode(input)
+ }
+
+ decode (input: string): Uint8Array {
+ return this.decoder.decode(input)
+ }
+}
+
+export function from ({ name, prefix, encode, decode }: { name: Base, prefix: Prefix, encode: EncodeFn, decode: DecodeFn }): Codec {
+ return new Codec(name, prefix, encode, decode)
+}
+
+export function baseX ({ name, prefix, alphabet }: { name: Base, prefix: Prefix, alphabet: string }): Codec {
+ const { encode, decode } = basex(alphabet, name)
+ return from({
+ prefix,
+ name,
+ encode,
+ decode: (text: string): Uint8Array => coerce(decode(text))
+ })
+}
+
+function decode (string: string, alphabet: string, bitsPerChar: number, name: string): Uint8Array {
+ // Build the character lookup table:
+ const codes: Record = {}
+ for (let i = 0; i < alphabet.length; ++i) {
+ codes[alphabet[i]] = i
+ }
+
+ // Count the padding bytes:
+ let end = string.length
+ while (string[end - 1] === '=') {
+ --end
+ }
+
+ // Allocate the output:
+ const out = new Uint8Array((end * bitsPerChar / 8) | 0)
+
+ // Parse the data:
+ let bits = 0 // Number of bits currently in the buffer
+ let buffer = 0 // Bits waiting to be written out, MSB first
+ let written = 0 // Next byte to write
+ for (let i = 0; i < end; ++i) {
+ // Read one character from the string:
+ const value = codes[string[i]]
+ if (value === undefined) {
+ throw new SyntaxError(`Non-${name} character`)
+ }
+
+ // Append the bits to the buffer:
+ buffer = (buffer << bitsPerChar) | value
+ bits += bitsPerChar
+
+ // Write out some bits if the buffer has a byte's worth:
+ if (bits >= 8) {
+ bits -= 8
+ out[written++] = 0xff & (buffer >> bits)
+ }
+ }
+
+ // Verify that we have received just enough bits:
+ if (bits >= bitsPerChar || (0xff & (buffer << (8 - bits))) !== 0) {
+ throw new SyntaxError('Unexpected end of data')
+ }
+
+ return out
+}
+
+function encode (data: Uint8Array, alphabet: string, bitsPerChar: number): string {
+ const pad = alphabet[alphabet.length - 1] === '='
+ const mask = (1 << bitsPerChar) - 1
+ let out = ''
+
+ let bits = 0 // Number of bits currently in the buffer
+ let buffer = 0 // Bits waiting to be written out, MSB first
+ for (let i = 0; i < data.length; ++i) {
+ // Slurp data into the buffer:
+ buffer = (buffer << 8) | data[i]
+ bits += 8
+
+ // Write out as much as we can:
+ while (bits > bitsPerChar) {
+ bits -= bitsPerChar
+ out += alphabet[mask & (buffer >> bits)]
+ }
+ }
+
+ // Partial character:
+ if (bits !== 0) {
+ out += alphabet[mask & (buffer << (bitsPerChar - bits))]
+ }
+
+ // Add padding characters until we hit a byte boundary:
+ if (pad) {
+ while (((out.length * bitsPerChar) & 7) !== 0) {
+ out += '='
+ }
+ }
+
+ return out
+}
+
+/**
+ * RFC4648 Factory
+ */
+export function rfc4648 ({ name, prefix, bitsPerChar, alphabet }: { name: Base, prefix: Prefix, bitsPerChar: number, alphabet: string }): Codec {
+ return from({
+ prefix,
+ name,
+ encode (input: Uint8Array): string {
+ return encode(input, alphabet, bitsPerChar)
+ },
+ decode (input: string): Uint8Array {
+ return decode(input, alphabet, bitsPerChar, name)
+ }
+ })
+}
diff --git a/src/bases/base10.js b/src/bases/base10.ts
similarity index 100%
rename from src/bases/base10.js
rename to src/bases/base10.ts
diff --git a/src/bases/base16.js b/src/bases/base16.ts
similarity index 95%
rename from src/bases/base16.js
rename to src/bases/base16.ts
index 394a8ca3..8e7b2380 100644
--- a/src/bases/base16.js
+++ b/src/bases/base16.ts
@@ -1,5 +1,3 @@
-// @ts-check
-
import { rfc4648 } from './base.js'
export const base16 = rfc4648({
diff --git a/src/bases/base2.js b/src/bases/base2.ts
similarity index 90%
rename from src/bases/base2.js
rename to src/bases/base2.ts
index e32baa32..2ff4977d 100644
--- a/src/bases/base2.js
+++ b/src/bases/base2.ts
@@ -1,5 +1,3 @@
-// @ts-check
-
import { rfc4648 } from './base.js'
export const base2 = rfc4648({
diff --git a/src/bases/base256emoji.js b/src/bases/base256emoji.ts
similarity index 72%
rename from src/bases/base256emoji.js
rename to src/bases/base256emoji.ts
index 957c81f8..d17a44c2 100644
--- a/src/bases/base256emoji.js
+++ b/src/bases/base256emoji.ts
@@ -1,28 +1,20 @@
import { from } from './base.js'
const alphabet = Array.from('๐๐ชโ๐ฐ๐๐๐๐๐๐๐๐๐๐๐๐๐โ๐ป๐ฅ๐พ๐ฟ๐โค๐๐คฃ๐๐๐๐ญ๐๐๐
๐๐๐ฅ๐ฅฐ๐๐๐๐ข๐ค๐๐๐ช๐โบ๐๐ค๐๐๐๐๐น๐คฆ๐๐โโจ๐คท๐ฑ๐๐ธ๐๐๐๐๐๐๐๐๐คฉ๐๐๐ค๐๐ฏ๐๐๐ถ๐๐คญโฃ๐๐๐๐ช๐๐ฅ๐๐๐ฉ๐ก๐คช๐๐ฅณ๐ฅ๐คค๐๐๐ณโ๐๐๐ด๐๐ฌ๐๐๐ท๐ป๐โญโ
๐ฅบ๐๐๐ค๐ฆโ๐ฃ๐๐โน๐๐๐ โ๐๐บ๐๐ป๐๐๐๐๐น๐ฃ๐ซ๐๐๐ต๐ค๐๐ด๐ค๐ผ๐ซโฝ๐คโ๐๐คซ๐๐ฎ๐๐ป๐๐ถ๐๐ฒ๐ฟ๐งก๐โก๐๐โโ๐๐ฐ๐คจ๐ถ๐ค๐ถ๐ฐ๐๐ข๐ค๐๐จ๐จ๐คฌโ๐๐บ๐ค๐๐๐ฑ๐๐ถ๐ฅดโถโกโ๐๐ธโฌ๐จ๐๐ฆ๐ท๐บโ ๐
๐๐ต๐๐คฒ๐ค ๐คง๐๐ต๐
๐ง๐พ๐๐๐ค๐๐คฏ๐ทโ๐ง๐ฏ๐๐๐ค๐๐โ๐ด๐ฃ๐ธ๐๐๐ฅ๐คข๐
๐ก๐ฉ๐๐ธ๐ป๐ค๐คฎ๐ผ๐ฅต๐ฉ๐๐๐ผ๐๐ฃ๐ฅ')
-const alphabetBytesToChars = /** @type {string[]} */ (alphabet.reduce((p, c, i) => { p[i] = c; return p }, /** @type {string[]} */([])))
-const alphabetCharsToBytes = /** @type {number[]} */ (alphabet.reduce((p, c, i) => { p[/** @type {number} */ (c.codePointAt(0))] = i; return p }, /** @type {number[]} */([])))
+const alphabetBytesToChars: string[] = (alphabet.reduce((p, c, i) => { p[i] = c; return p }, ([])))
+const alphabetCharsToBytes: number[] = (alphabet.reduce((p, c, i) => { p[c.codePointAt(0) as number] = i; return p }, ([])))
-/**
- * @param {Uint8Array} data
- * @returns {string}
- */
-function encode (data) {
+function encode (data: Uint8Array): string {
return data.reduce((p, c) => {
p += alphabetBytesToChars[c]
return p
}, '')
}
-/**
- * @param {string} str
- * @returns {Uint8Array}
- */
-function decode (str) {
+function decode (str: string): Uint8Array {
const byts = []
for (const char of str) {
- const byt = alphabetCharsToBytes[/** @type {number} */ (char.codePointAt(0))]
+ const byt = alphabetCharsToBytes[char.codePointAt(0) as number]
if (byt === undefined) {
throw new Error(`Non-base256emoji character: ${char}`)
}
diff --git a/src/bases/base32.js b/src/bases/base32.ts
similarity index 100%
rename from src/bases/base32.js
rename to src/bases/base32.ts
diff --git a/src/bases/base36.js b/src/bases/base36.ts
similarity index 100%
rename from src/bases/base36.js
rename to src/bases/base36.ts
diff --git a/src/bases/base58.js b/src/bases/base58.ts
similarity index 100%
rename from src/bases/base58.js
rename to src/bases/base58.ts
diff --git a/src/bases/base64.js b/src/bases/base64.ts
similarity index 98%
rename from src/bases/base64.js
rename to src/bases/base64.ts
index 4fe40f9e..6e66d933 100644
--- a/src/bases/base64.js
+++ b/src/bases/base64.ts
@@ -1,5 +1,3 @@
-// @ts-check
-
import { rfc4648 } from './base.js'
export const base64 = rfc4648({
diff --git a/src/bases/base8.js b/src/bases/base8.ts
similarity index 91%
rename from src/bases/base8.js
rename to src/bases/base8.ts
index caa50f7c..7c584a57 100644
--- a/src/bases/base8.js
+++ b/src/bases/base8.ts
@@ -1,5 +1,3 @@
-// @ts-check
-
import { rfc4648 } from './base.js'
export const base8 = rfc4648({
diff --git a/src/bases/identity.js b/src/bases/identity.ts
similarity index 94%
rename from src/bases/identity.js
rename to src/bases/identity.ts
index 5db2a0ca..301ac7a8 100644
--- a/src/bases/identity.js
+++ b/src/bases/identity.ts
@@ -1,5 +1,3 @@
-// @ts-check
-
import { fromString, toString } from '../bytes.js'
import { from } from './base.js'
diff --git a/src/bases/interface.js b/src/bases/interface.js
deleted file mode 100644
index d9b3f4f7..00000000
--- a/src/bases/interface.js
+++ /dev/null
@@ -1 +0,0 @@
-// this is dummy module overlayed by interface.ts
diff --git a/src/bases/interface.ts b/src/bases/interface.ts
index ba3bddbe..f0b63abe 100644
--- a/src/bases/interface.ts
+++ b/src/bases/interface.ts
@@ -8,8 +8,6 @@ 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
}
@@ -21,8 +19,6 @@ export interface BaseDecoder {
/**
* Decodes **plain** (and not a multibase) string. Unlike
* decode
- *
- * @param text
*/
baseDecode(text: string): Uint8Array
}
@@ -73,8 +69,6 @@ export interface MultibaseDecoder {
/**
* Decodes **multibase** string (which must have a multibase prefix added).
* If prefix does not match
- *
- * @param multibase
*/
decode(multibase: Multibase): Uint8Array
}
diff --git a/src/basics.js b/src/basics.ts
similarity index 70%
rename from src/basics.js
rename to src/basics.ts
index fde44152..11b45ed1 100644
--- a/src/basics.js
+++ b/src/basics.ts
@@ -1,5 +1,3 @@
-// @ts-check
-
import * as base10 from './bases/base10.js'
import * as base16 from './bases/base16.js'
import * as base2 from './bases/base2.js'
@@ -16,8 +14,8 @@ import * as identity from './hashes/identity.js'
import * as sha2 from './hashes/sha2.js'
import { CID, hasher, digest, varint, bytes } from './index.js'
-const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64, ...base256emoji }
-const hashes = { ...sha2, ...identity }
-const codecs = { raw, json }
+export const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64, ...base256emoji }
+export const hashes = { ...sha2, ...identity }
+export const codecs = { raw, json }
-export { CID, hasher, digest, varint, bytes, hashes, bases, codecs }
+export { CID, hasher, digest, varint, bytes }
diff --git a/src/block.js b/src/block.js
deleted file mode 100644
index 16adbec5..00000000
--- a/src/block.js
+++ /dev/null
@@ -1,272 +0,0 @@
-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'
-
-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
- * @param {T} source
- * @param {Array} base
- * @returns {Iterable<[string, CID]>}
- */
-function * links (source, base) {
- if (source == null || source instanceof Uint8Array) {
- return
- }
- const cid = CID.asCID(source)
- if (cid) {
- yield [base.join('/'), cid]
- }
- for (const [key, value] of Object.entries(source)) {
- 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)
- }
-}
-
-/**
- * @template T
- * @param {T} source
- * @param {Array} base
- * @returns {Iterable}
- */
-function * tree (source, base) {
- if (source == null || typeof source !== 'object') {
- return
- }
- for (const [key, value] of Object.entries(source)) {
- const path = /** @type {[string|number, string]} */ ([...base, key])
- yield path.join('/')
- if (value != null && !(value instanceof Uint8Array) && typeof value === 'object' && !CID.asCID(value)) {
- yield * treeWithin(path, value)
- }
- }
-}
-
-/**
- *
- * @template T
- * @param {T} source
- * @param {string[]} path
- * @returns {API.BlockCursorView}
- */
-function get (source, path) {
- let node = /** @type {Record} */(source)
- for (const [index, key] of path.entries()) {
- node = node[key]
- if (node == null) {
- throw new Error(`Object has no property at ${path.slice(0, index + 1).map(part => `[${JSON.stringify(part)}]`).join('')}`)
- }
- const cid = CID.asCID(node)
- if (cid) {
- return { value: cid, remaining: path.slice(index + 1).join('/') }
- }
- }
- return { value: node }
-}
-
-/**
- * @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 {API.ByteView} options.bytes
- * @param {T} options.value
- */
- constructor ({ cid, bytes, value }) {
- if (!cid || !bytes || typeof value === 'undefined') { throw new Error('Missing required argument') }
-
- this.cid = cid
- this.bytes = bytes
- this.value = value
- this.asBlock = this
-
- // Mark all the properties immutable
- Object.defineProperties(this, {
- cid: readonly(),
- bytes: readonly(),
- value: readonly(),
- asBlock: readonly()
- })
- }
-
- links () {
- return links(this.value, [])
- }
-
- tree () {
- return tree(this.value, [])
- }
-
- /**
- *
- * @param {string} [path]
- * @returns {API.BlockCursorView}
- */
- get (path = '/') {
- return get(this.value, path.split('/').filter(Boolean))
- }
-}
-
-/**
- * @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 {API.BlockEncoder} options.codec
- * @param {API.MultihashHasher} options.hasher
- * @returns {Promise>}
- */
-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)
- /** @type {CID} */
- const cid = CID.create(
- 1,
- codec.code,
- hash
- )
-
- return new Block({ value, bytes, cid })
-}
-
-/**
- * @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>}
- */
-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
- * @property {CID} options.cid
- */
-
-/**
- * @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}
- */
-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({
- // eslint-disable-next-line object-shorthand
- cid: /** @type {CID} */ (cid),
- bytes,
- value
- })
-}
-
-/**
- * @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>}
- */
-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)
- const hash = await hasher.digest(bytes)
- if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
- throw new Error('CID hash does not match bytes')
- }
-
- return createUnsafe({
- bytes,
- cid,
- value,
- codec
- })
-}
-
-export { encode, decode, create, createUnsafe, Block }
diff --git a/src/block.ts b/src/block.ts
new file mode 100644
index 00000000..ae59ff13
--- /dev/null
+++ b/src/block.ts
@@ -0,0 +1,239 @@
+import { bytes as binary, CID } from './index.js'
+import type * as API from './interface.js'
+
+function readonly ({ enumerable = true, configurable = false } = {}): { enumerable: boolean, configurable: boolean, writable: false } {
+ return { enumerable, configurable, writable: false }
+}
+
+function * linksWithin (path: [string | number, string], value: any): Iterable<[string, CID]> {
+ 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 != null) {
+ yield [elementPath.join('/'), cid]
+ } else if (typeof element === 'object') {
+ yield * links(element, elementPath)
+ }
+ }
+ } else {
+ const cid = CID.asCID(value)
+ if (cid != null) {
+ yield [path.join('/'), cid]
+ } else {
+ yield * links(value, path)
+ }
+ }
+ }
+}
+
+function * links (source: T, base: Array): Iterable<[string, CID]> {
+ if (source == null || source instanceof Uint8Array) {
+ return
+ }
+ const cid = CID.asCID(source)
+ if (cid != null) {
+ yield [base.join('/'), cid]
+ }
+ for (const [key, value] of Object.entries(source)) {
+ const path = [...base, key] as [string | number, string]
+ yield * linksWithin(path, value)
+ }
+}
+
+function * treeWithin (path: [string | number, string], value: any): Iterable {
+ 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) == null)) {
+ yield * tree(element, elementPath)
+ }
+ }
+ } else {
+ yield * tree(value, path)
+ }
+}
+
+function * tree (source: T, base: Array): Iterable {
+ if (source == null || typeof source !== 'object') {
+ return
+ }
+ for (const [key, value] of Object.entries(source)) {
+ const path = [...base, key] as [string | number, string]
+ yield path.join('/')
+ if (value != null && !(value instanceof Uint8Array) && typeof value === 'object' && (CID.asCID(value) == null)) {
+ yield * treeWithin(path, value)
+ }
+ }
+}
+
+function get (source: T, path: string[]): API.BlockCursorView {
+ let node = source as Record
+ for (const [index, key] of path.entries()) {
+ node = node[key]
+ if (node == null) {
+ throw new Error(`Object has no property at ${path.slice(0, index + 1).map(part => `[${JSON.stringify(part)}]`).join('')}`)
+ }
+ const cid = CID.asCID(node)
+ if (cid != null) {
+ return { value: cid, remaining: path.slice(index + 1).join('/') }
+ }
+ }
+ return { value: node }
+}
+
+/**
+ * @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 class Block implements API.BlockView {
+ readonly cid: CID
+ readonly bytes: API.ByteView
+ readonly value: T
+ readonly asBlock: this
+
+ constructor ({ cid, bytes, value }: { cid: CID, bytes: API.ByteView, value: T }) {
+ if (cid == null || bytes == null || typeof value === 'undefined') { throw new Error('Missing required argument') }
+
+ this.cid = cid
+ this.bytes = bytes
+ this.value = value
+ this.asBlock = this
+
+ // Mark all the properties immutable
+ Object.defineProperties(this, {
+ cid: readonly(),
+ bytes: readonly(),
+ value: readonly(),
+ asBlock: readonly()
+ })
+ }
+
+ links (): Iterable<[string, CID]> {
+ return links(this.value, [])
+ }
+
+ tree (): Iterable {
+ return tree(this.value, [])
+ }
+
+ get (path = '/'): API.BlockCursorView {
+ return get(this.value, path.split('/').filter(Boolean))
+ }
+}
+
+interface EncodeInput {
+ value: T
+ codec: API.BlockEncoder
+ hasher: API.MultihashHasher
+}
+
+/**
+ * @template T - Logical type of the data encoded in the block
+ * @template Code - multicodec code corresponding to codec used to encode the block
+ * @template Alg - multicodec code corresponding to the hashing algorithm used in CID creation.
+ */
+export async function encode ({ value, codec, hasher }: EncodeInput): Promise> {
+ if (typeof value === 'undefined') throw new Error('Missing required argument "value"')
+ if (codec == null || hasher == null) throw new Error('Missing required argument: codec or hasher')
+
+ const bytes = codec.encode(value)
+ const hash = await hasher.digest(bytes)
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+ const cid = CID.create(
+ 1,
+ codec.code,
+ hash
+ ) as CID
+
+ return new Block({ value, bytes, cid })
+}
+
+interface DecodeInput {
+ bytes: API.ByteView
+ codec: API.BlockDecoder
+ hasher: API.MultihashHasher
+}
+
+/**
+ * @template T - Logical type of the data encoded in the block
+ * @template Code - multicodec code corresponding to codec used to encode the block
+ * @template Alg - multicodec code corresponding to the hashing algorithm used in CID creation.
+ */
+export async function decode ({ bytes, codec, hasher }: DecodeInput): Promise> {
+ if (bytes == null) throw new Error('Missing required argument "bytes"')
+ if (codec == null || hasher == null) throw new Error('Missing required argument: codec or hasher')
+
+ const value = codec.decode(bytes)
+ const hash = await hasher.digest(bytes)
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+ const cid = CID.create(1, codec.code, hash) as CID
+
+ return new Block({ value, bytes, cid })
+}
+
+type CreateUnsafeInput = {
+ cid: API.Link
+ value: T
+ codec?: API.BlockDecoder
+ bytes: API.ByteView
+} | {
+ cid: API.Link
+ value?: undefined
+ codec: API.BlockDecoder
+ bytes: API.ByteView
+}
+
+/**
+ * @template T - Logical type of the data encoded in the block
+ * @template Code - multicodec code corresponding to codec used to encode the block
+ * @template Alg - multicodec code corresponding to the hashing algorithm used in CID creation.
+ * @template V - CID version
+ */
+export function createUnsafe ({ bytes, cid, value: maybeValue, codec }: CreateUnsafeInput): API.BlockView {
+ const value = maybeValue !== undefined
+ ? maybeValue
+ : (codec?.decode(bytes))
+
+ if (value === undefined) throw new Error('Missing required argument, must either provide "value" or "codec"')
+
+ return new Block({
+ cid: cid as CID,
+ bytes,
+ value
+ })
+}
+
+interface CreateInput {
+ bytes: API.ByteView
+ cid: API.Link
+ hasher: API.MultihashHasher
+ codec: API.BlockDecoder
+}
+
+/**
+ * @template T - Logical type of the data encoded in the block
+ * @template Code - multicodec code corresponding to codec used to encode the block
+ * @template Alg - multicodec code corresponding to the hashing algorithm used in CID creation.
+ * @template V - CID version
+ */
+export async function create ({ bytes, cid, hasher, codec }: CreateInput): Promise> {
+ if (bytes == null) throw new Error('Missing required argument "bytes"')
+ if (hasher == null) throw new Error('Missing required argument "hasher"')
+ const value = codec.decode(bytes)
+ const hash = await hasher.digest(bytes)
+ if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
+ throw new Error('CID hash does not match bytes')
+ }
+
+ return createUnsafe({
+ bytes,
+ cid,
+ value,
+ codec
+ })
+}
diff --git a/src/block/interface.js b/src/block/interface.js
deleted file mode 100644
index d9b3f4f7..00000000
--- a/src/block/interface.js
+++ /dev/null
@@ -1 +0,0 @@
-// this is dummy module overlayed by interface.ts
diff --git a/src/block/interface.ts b/src/block/interface.ts
index 71c55d05..932327df 100644
--- a/src/block/interface.ts
+++ b/src/block/interface.ts
@@ -1,6 +1,3 @@
-/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
-/* eslint-disable no-use-before-define */
-
import type { CID } from '../cid.js'
import type { Link, Version } from '../link/interface.js'
@@ -55,7 +52,7 @@ export interface Block<
cid: Link
}
-export type BlockCursorView =
+export type BlockCursorView =
| { value: T, remaining?: undefined }
| { value: CID, remaining: string }
diff --git a/src/bytes.js b/src/bytes.js
deleted file mode 100644
index a10ce3ec..00000000
--- a/src/bytes.js
+++ /dev/null
@@ -1,67 +0,0 @@
-const empty = new Uint8Array(0)
-
-/**
- * @param {Uint8Array} d
- */
-const toHex = d => d.reduce((hex, byte) => hex + byte.toString(16).padStart(2, '0'), '')
-
-/**
- * @param {string} hex
- */
-const fromHex = hex => {
- const hexes = hex.match(/../g)
- return hexes ? new Uint8Array(hexes.map(b => parseInt(b, 16))) : empty
-}
-
-/**
- * @param {Uint8Array} aa
- * @param {Uint8Array} bb
- */
-const equals = (aa, bb) => {
- if (aa === bb) return true
- if (aa.byteLength !== bb.byteLength) {
- return false
- }
-
- for (let ii = 0; ii < aa.byteLength; ii++) {
- if (aa[ii] !== bb[ii]) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @param {ArrayBufferView|ArrayBuffer|Uint8Array} o
- * @returns {Uint8Array}
- */
-const coerce = o => {
- if (o instanceof Uint8Array && o.constructor.name === 'Uint8Array') return o
- if (o instanceof ArrayBuffer) return new Uint8Array(o)
- if (ArrayBuffer.isView(o)) {
- return new Uint8Array(o.buffer, o.byteOffset, o.byteLength)
- }
- throw new Error('Unknown type, must be binary type')
-}
-
-/**
- * @param {any} o
- * @returns {o is ArrayBuffer|ArrayBufferView}
- */
-const isBinary = o =>
- o instanceof ArrayBuffer || ArrayBuffer.isView(o)
-
-/**
- * @param {string} str
- * @returns {Uint8Array}
- */
-const fromString = str => (new TextEncoder()).encode(str)
-
-/**
- * @param {Uint8Array} b
- * @returns {string}
- */
-const toString = b => (new TextDecoder()).decode(b)
-
-export { equals, coerce, isBinary, fromHex, toHex, fromString, toString, empty }
diff --git a/src/bytes.ts b/src/bytes.ts
new file mode 100644
index 00000000..c323993d
--- /dev/null
+++ b/src/bytes.ts
@@ -0,0 +1,46 @@
+export const empty = new Uint8Array(0)
+
+export function toHex (d: Uint8Array): string {
+ return d.reduce((hex, byte) => hex + byte.toString(16).padStart(2, '0'), '')
+}
+
+export function fromHex (hex: string): Uint8Array {
+ const hexes = hex.match(/../g)
+ return hexes != null ? new Uint8Array(hexes.map(b => parseInt(b, 16))) : empty
+}
+
+export function equals (aa: Uint8Array, bb: Uint8Array): boolean {
+ if (aa === bb) return true
+ if (aa.byteLength !== bb.byteLength) {
+ return false
+ }
+
+ for (let ii = 0; ii < aa.byteLength; ii++) {
+ if (aa[ii] !== bb[ii]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+export function coerce (o: ArrayBufferView | ArrayBuffer | Uint8Array): Uint8Array {
+ if (o instanceof Uint8Array && o.constructor.name === 'Uint8Array') return o
+ if (o instanceof ArrayBuffer) return new Uint8Array(o)
+ if (ArrayBuffer.isView(o)) {
+ return new Uint8Array(o.buffer, o.byteOffset, o.byteLength)
+ }
+ throw new Error('Unknown type, must be binary type')
+}
+
+export function isBinary (o: unknown): o is ArrayBuffer | ArrayBufferView {
+ return o instanceof ArrayBuffer || ArrayBuffer.isView(o)
+}
+
+export function fromString (str: string): Uint8Array {
+ return new TextEncoder().encode(str)
+}
+
+export function toString (b: Uint8Array): string {
+ return new TextDecoder().decode(b)
+}
diff --git a/src/cid.js b/src/cid.ts
similarity index 55%
rename from src/cid.js
rename to src/cid.ts
index 40f7c5fc..6baa4a10 100644
--- a/src/cid.js
+++ b/src/cid.ts
@@ -2,63 +2,43 @@ import { base32 } from './bases/base32.js'
import { base58btc } from './bases/base58.js'
import { coerce } from './bytes.js'
import * as Digest from './hashes/digest.js'
-// Linter can see that API is used in types.
-// eslint-disable-next-line
-import * as API from "./link/interface.js"
import * as varint from './varint.js'
+import type * as API from './link/interface.js'
// This way TS will also expose all the types from module
export * from './link/interface.js'
-/**
- * @template {API.Link} T
- * @template {string} Prefix
- * @param {T} link
- * @param {API.MultibaseEncoder} [base]
- * @returns {API.ToString}
- */
-export const format = (link, base) => {
+export function format , Prefix extends string> (link: T, base?: API.MultibaseEncoder): API.ToString {
const { bytes, version } = link
switch (version) {
case 0:
return toStringV0(
bytes,
baseCache(link),
- /** @type {API.MultibaseEncoder<"z">} */ (base) || base58btc.encoder
+ base as API.MultibaseEncoder<'z'> ?? base58btc.encoder
)
default:
return toStringV1(
bytes,
baseCache(link),
- /** @type {API.MultibaseEncoder} */ (base || base32.encoder)
+ (base ?? base32.encoder) as API.MultibaseEncoder
)
}
}
-/**
- * @template {API.UnknownLink} Link
- * @param {Link} link
- * @returns {API.LinkJSON}
- */
-export const toJSON = (link) => ({
- '/': format(link)
-})
-
-/**
- * @template {API.UnknownLink} Link
- * @param {API.LinkJSON} json
- */
-export const fromJSON = (json) =>
- CID.parse(json['/'])
-
-/** @type {WeakMap>} */
-const cache = new WeakMap()
-
-/**
- * @param {API.UnknownLink} cid
- * @returns {Map}
- */
-const baseCache = cid => {
+export function toJSON (link: Link): API.LinkJSON {
+ return {
+ '/': format(link)
+ }
+}
+
+export function fromJSON (json: API.LinkJSON): CID {
+ return CID.parse(json['/'])
+}
+
+const cache = new WeakMap>()
+
+function baseCache (cid: API.UnknownLink): Map {
const baseCache = cache.get(cid)
if (baseCache == null) {
const baseCache = new Map()
@@ -68,34 +48,26 @@ const baseCache = cid => {
return baseCache
}
-/**
- * @template {unknown} [Data=unknown]
- * @template {number} [Format=number]
- * @template {number} [Alg=number]
- * @template {API.Version} [Version=API.Version]
- * @implements {API.Link}
- */
+export class CID implements API.Link {
+ readonly code: Format
+ readonly version: Version
+ readonly multihash: API.MultihashDigest
+ readonly bytes: Uint8Array
+ readonly '/': Uint8Array
-export class CID {
/**
- * @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
+ * @param version - Version of the CID
+ * @param code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv
+ * @param multihash - (Multi)hash of the of the content.
*/
- constructor (version, code, multihash, bytes) {
- /** @readonly */
+ constructor (version: Version, code: Format, multihash: API.MultihashDigest, bytes: Uint8Array) {
this.code = code
- /** @readonly */
this.version = version
- /** @readonly */
this.multihash = multihash
- /** @readonly */
this.bytes = bytes
// flag to serializers that this is a CID and
// should be treated specially
- /** @readonly */
this['/'] = bytes
}
@@ -105,27 +77,24 @@ export class CID {
*
* @deprecated
*/
- get asCID () {
+ get asCID (): this {
return this
}
// ArrayBufferView
- get byteOffset () {
+ get byteOffset (): number {
return this.bytes.byteOffset
}
// ArrayBufferView
- get byteLength () {
+ get byteLength (): number {
return this.bytes.byteLength
}
- /**
- * @returns {CID}
- */
- toV0 () {
+ toV0 (): CID {
switch (this.version) {
case 0: {
- return /** @type {CID} */ (this)
+ return this as CID
}
case 1: {
const { code, multihash } = this
@@ -139,9 +108,9 @@ export class CID {
throw new Error('Cannot convert non sha2-256 multihash CID to CIDv0')
}
- return /** @type {CID} */ (
+ return (
CID.createV0(
- /** @type {API.MultihashDigest} */ (multihash)
+ multihash as API.MultihashDigest
)
)
}
@@ -153,20 +122,17 @@ export class CID {
}
}
- /**
- * @returns {CID}
- */
- toV1 () {
+ toV1 (): CID {
switch (this.version) {
case 0: {
const { code, digest } = this.multihash
const multihash = Digest.create(code, digest)
- return /** @type {CID} */ (
+ return (
CID.createV1(this.code, multihash)
)
}
case 1: {
- return /** @type {CID} */ (this)
+ return this as CID
}
default: {
throw Error(
@@ -176,62 +142,37 @@ export class CID {
}
}
- /**
- * @param {unknown} other
- * @returns {other is CID}
- */
- equals (other) {
+ equals (other: unknown): other is CID {
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
- )
+ static equals (self: API.Link, other: unknown): other is CID {
+ const unknown = other as { code?: unknown, version?: unknown, multihash?: unknown }
return (
- unknown &&
+ unknown != null &&
self.code === unknown.code &&
self.version === unknown.version &&
Digest.equals(self.multihash, unknown.multihash)
)
}
- /**
- * @param {API.MultibaseEncoder} [base]
- * @returns {string}
- */
- toString (base) {
+ toString (base?: API.MultibaseEncoder): string {
return format(this, base)
}
- /**
- * @returns {API.LinkJSON}
- */
- toJSON () {
+ toJSON (): API.LinkJSON {
return { '/': format(this) }
}
- link () {
+ link (): this {
return this
}
- get [Symbol.toStringTag] () {
- return 'CID'
- }
+ readonly [Symbol.toStringTag] = 'CID';
// Legacy
- [Symbol.for('nodejs.util.inspect.custom')] () {
+ [Symbol.for('nodejs.util.inspect.custom')] (): string {
return `CID(${this.toString()})`
}
@@ -244,21 +185,13 @@ export class CID {
*
* This allows two different incompatible versions of CID library to
* co-exist and interop as long as binary interface is compatible.
- *
- * @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 (input) {
+ static asCID (input: API.Link | U): CID | null {
if (input == null) {
return null
}
- const value = /** @type {any} */ (input)
+ const value = input as any
if (value instanceof CID) {
// If value is instance of CID then we're all set.
return value
@@ -272,17 +205,15 @@ export class CID {
return new CID(
version,
code,
- /** @type {API.MultihashDigest} */ (multihash),
- bytes || encodeCID(version, code, multihash.bytes)
+ multihash as API.MultihashDigest,
+ bytes ?? encodeCID(version, code, multihash.bytes)
)
} else if (value[cidSymbol] === true) {
// 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 =
- /** @type {API.MultihashDigest} */
- (Digest.decode(multihash))
+ const digest = Digest.decode(multihash) as API.MultihashDigest
return CID.create(version, code, digest)
} else {
// Otherwise value is not a CID (or an incompatible version of it) in
@@ -292,17 +223,11 @@ export class 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}
+ * @param version - Version of the CID
+ * @param code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv
+ * @param digest - (Multi)hash of the of the content.
*/
- static create (version, code, digest) {
+ static create (version: Version, code: Format, digest: API.MultihashDigest): CID {
if (typeof code !== 'number') {
throw new Error('String codecs are no longer supported')
}
@@ -333,26 +258,18 @@ export class CID {
/**
* Simplified version of `create` for CIDv0.
- *
- * @template {unknown} [T=unknown]
- * @param {API.MultihashDigest} digest - Multihash.
- * @returns {CID}
*/
- static createV0 (digest) {
+ static createV0 (digest: API.MultihashDigest): CID {
return 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 {CID}
+ * @param code - Content encoding format code.
+ * @param digest - Multihash of the content.
*/
- static createV1 (code, digest) {
+ static createV1 (code: Code, digest: API.MultihashDigest): CID {
return CID.create(1, code, digest)
}
@@ -362,17 +279,10 @@ export class CID {
*
* 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 {CID}
*/
- static decode (bytes) {
+ static decode (bytes: API.ByteView>): CID {
const [cid, remainder] = CID.decodeFirst(bytes)
- if (remainder.length) {
+ if (remainder.length !== 0) {
throw new Error('Incorrect length')
}
return cid
@@ -386,15 +296,8 @@ export class CID {
* element containing the remainder of the original byte array. The remainder
* will be a zero-length byte array if the provided bytes only contained a
* binary CID representation.
- *
- * @template {unknown} T
- * @template {number} C
- * @template {number} A
- * @template {API.Version} V
- * @param {API.ByteView>} bytes
- * @returns {[CID, Uint8Array]}
*/
- static decodeFirst (bytes) {
+ static decodeFirst (bytes: API.ByteView>): [CID, Uint8Array] {
const specs = CID.inspectBytes(bytes)
const prefixSize = specs.size - specs.multihashSize
const multihashBytes = coerce(
@@ -414,9 +317,9 @@ export class CID {
)
const cid =
specs.version === 0
- ? CID.createV0(/** @type {API.MultihashDigest} */ (digest))
+ ? CID.createV0(digest as API.MultihashDigest)
: CID.createV1(specs.codec, digest)
- return [/** @type {CID} */(cid), bytes.subarray(specs.size)]
+ return [cid as CID, bytes.subarray(specs.size)]
}
/**
@@ -427,30 +330,23 @@ export class CID {
* lengths these varints can be quite large. It is recommended that at least
* 10 bytes be made available in the `initialBytes` argument for a complete
* inspection.
- *
- * @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) {
+ static inspectBytes (initialBytes: API.ByteView>): { version: V, codec: C, multihashCode: A, digestSize: number, multihashSize: number, size: number } {
let offset = 0
- const next = () => {
+ const next = (): number => {
const [i, length] = varint.decode(initialBytes.subarray(offset))
offset += length
return i
}
- let version = /** @type {V} */ (next())
- let codec = /** @type {C} */ (DAG_PB_CODE)
- if (/** @type {number} */(version) === 18) {
+ let version = next() as V
+ let codec = DAG_PB_CODE as C
+ if (version as number === 18) {
// CIDv0
- version = /** @type {V} */ (0)
+ version = 0 as V
offset = 0
} else {
- codec = /** @type {C} */ (next())
+ codec = next() as C
}
if (version !== 0 && version !== 1) {
@@ -458,7 +354,7 @@ export class CID {
}
const prefixSize = offset
- const multihashCode = /** @type {A} */ (next()) // multihash code
+ const multihashCode = next() as A // multihash code
const digestSize = next() // multihash length
const size = offset + digestSize
const multihashSize = size - prefixSize
@@ -471,17 +367,8 @@ export class CID {
* 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 {CID}
*/
- static parse (source, base) {
+ static parse (source: API.ToString, Prefix>, base?: API.MultibaseDecoder): CID {
const [prefix, bytes] = parseCIDtoBytes(source, base)
const cid = CID.decode(bytes)
@@ -497,33 +384,23 @@ export class CID {
}
}
-/**
- * @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 {[Prefix, API.ByteView>]}
- */
-const parseCIDtoBytes = (source, base) => {
+function parseCIDtoBytes (source: API.ToString, Prefix>, base?: API.MultibaseDecoder): [Prefix, API.ByteView>] {
switch (source[0]) {
// CIDv0 is parsed differently
case 'Q': {
- const decoder = base || base58btc
+ const decoder = base ?? base58btc
return [
- /** @type {Prefix} */ (base58btc.prefix),
+ base58btc.prefix as Prefix,
decoder.decode(`${base58btc.prefix}${source}`)
]
}
case base58btc.prefix: {
- const decoder = base || base58btc
- return [/** @type {Prefix} */(base58btc.prefix), decoder.decode(source)]
+ const decoder = base ?? base58btc
+ return [base58btc.prefix as Prefix, decoder.decode(source)]
}
case base32.prefix: {
- const decoder = base || base32
- return [/** @type {Prefix} */(base32.prefix), decoder.decode(source)]
+ const decoder = base ?? base32
+ return [base32.prefix as Prefix, decoder.decode(source)]
}
default: {
if (base == null) {
@@ -531,18 +408,12 @@ const parseCIDtoBytes = (source, base) => {
'To parse non base32 or base58btc encoded CID multibase decoder must be provided'
)
}
- return [/** @type {Prefix} */(source[0]), base.decode(source)]
+ return [source[0] as Prefix, base.decode(source)]
}
}
}
-/**
- *
- * @param {Uint8Array} bytes
- * @param {Map} cache
- * @param {API.MultibaseEncoder<'z'>} base
- */
-const toStringV0 = (bytes, cache, base) => {
+function toStringV0 (bytes: Uint8Array, cache: Map, base: API.MultibaseEncoder<'z'>): string {
const { prefix } = base
if (prefix !== base58btc.prefix) {
throw Error(`Cannot string encode V0 in ${base.name} encoding`)
@@ -558,13 +429,7 @@ const toStringV0 = (bytes, cache, base) => {
}
}
-/**
- * @template {string} Prefix
- * @param {Uint8Array} bytes
- * @param {Map} cache
- * @param {API.MultibaseEncoder} base
- */
-const toStringV1 = (bytes, cache, base) => {
+function toStringV1 (bytes: Uint8Array, cache: Map, base: API.MultibaseEncoder): string {
const { prefix } = base
const cid = cache.get(prefix)
if (cid == null) {
@@ -579,13 +444,7 @@ const toStringV1 = (bytes, cache, base) => {
const DAG_PB_CODE = 0x70
const SHA_256_CODE = 0x12
-/**
- * @param {API.Version} version
- * @param {number} code
- * @param {Uint8Array} multihash
- * @returns {Uint8Array}
- */
-const encodeCID = (version, code, multihash) => {
+function encodeCID (version: API.Version, code: number, multihash: Uint8Array): Uint8Array {
const codeOffset = varint.encodingLength(version)
const hashOffset = codeOffset + varint.encodingLength(code)
const bytes = new Uint8Array(hashOffset + multihash.byteLength)
diff --git a/src/codecs/interface.js b/src/codecs/interface.js
deleted file mode 100644
index d9b3f4f7..00000000
--- a/src/codecs/interface.js
+++ /dev/null
@@ -1 +0,0 @@
-// this is dummy module overlayed by interface.ts
diff --git a/src/codecs/json.js b/src/codecs/json.js
deleted file mode 100644
index c3028430..00000000
--- a/src/codecs/json.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// @ts-check
-
-/**
- * @template T
- * @typedef {import('./interface.js').ByteView