Skip to content

Commit

Permalink
feat!: CID as an interface
Browse files Browse the repository at this point in the history
This is an attempt at implementing #203, converting the CID class
into a minimal interface and having factory functions to return
objects that conform to the CID interface.

I've preserved all the generics the `Link` interface introduced and
there are functions in `link.js` that add the extra methods to turn
something that conforms to `CID` into something that can be used as
a `Link` so existing code using the `Link` API should not have to
change.

Notably there was no need to update any of the `Link` tests.

The static methods on CID have been exported as individual functions
- the names remain the same (`decode`, `parse`, etc) in an attempt
to be less disruptive.

Code using these methods should mostly just need to change:

```js
import { CID } from 'multiformats/cid'
```

to:

```js
import * as CID from 'multiformats/cid
```

Types can be imported as:

```ts
import type { CID } from 'multiformats/interface'
```

or as before:

```ts
import type { CID } from 'multiformats/cid'
```

BREAKING CHANGE: the CID class is now an interface
  • Loading branch information
achingbrain committed Oct 15, 2022
1 parent 0ec751a commit 8152e34
Show file tree
Hide file tree
Showing 14 changed files with 600 additions and 470 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ This library defines common interfaces and low level building blocks for various
This library provides implementations for most basics and many others can be found in linked repositories.

```js
import { CID } from 'multiformats/cid'
import * as CID from 'multiformats/cid'
import * as json from 'multiformats/codecs/json'
import { sha256 } from 'multiformats/hashes/sha2'

Expand Down
26 changes: 16 additions & 10 deletions src/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ 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'
// Linter can see that API is used in types.
// eslint-disable-next-line
import * as CIDAPI from './cid/interface.js'
// Linter can see that API is used in types.
// eslint-disable-next-line
import * as LinkAPI from './link/interface.js'
import { asLink } from './link.js'

function readonly ({ enumerable = true, configurable = false } = {}) {
return { enumerable, configurable, writable: false }
Expand All @@ -10,7 +17,7 @@ function readonly ({ enumerable = true, configurable = false } = {}) {
/**
* @param {[string|number, string]} path
* @param {any} value
* @returns {Iterable<[string, CID]>}
* @returns {Iterable<[string, CIDAPI.CID]>}
*/
function * linksWithin (path, value) {
if (value != null && typeof value === 'object') {
Expand Down Expand Up @@ -39,7 +46,7 @@ function * linksWithin (path, value) {
* @template T
* @param {T} source
* @param {Array<string|number>} base
* @returns {Iterable<[string, CID]>}
* @returns {Iterable<[string, CIDAPI.CID]>}
*/
function * links (source, base) {
if (source == null || source instanceof Uint8Array) {
Expand Down Expand Up @@ -121,7 +128,7 @@ function get (source, path) {
class Block {
/**
* @param {object} options
* @param {CID<T, C, A, V>} options.cid
* @param {LinkAPI.Link<T, C, A, V>} options.cid
* @param {API.ByteView<T>} options.bytes
* @param {T} options.value
*/
Expand Down Expand Up @@ -176,14 +183,14 @@ async function encode ({ value, codec, hasher }) {

const bytes = codec.encode(value)
const hash = await hasher.digest(bytes)
/** @type {CID<T, Code, Alg, 1>} */
/** @type {CIDAPI.CID<T, Code, Alg, 1>} */
const cid = CID.create(
1,
codec.code,
hash
)

return new Block({ value, bytes, cid })
return new Block({ value, bytes, cid: asLink(cid) })
}

/**
Expand All @@ -194,18 +201,18 @@ async function encode ({ value, codec, hasher }) {
* @param {API.ByteView<T>} options.bytes
* @param {API.BlockDecoder<Code, T>} options.codec
* @param {API.MultihashHasher<Alg>} options.hasher
* @returns {Promise<API.BlockView<T, Code, Alg>>}
* @returns {Promise<API.BlockView<T, Code, Alg, 1>>}
*/
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<T, Code, Alg, 1>} */
/** @type {CIDAPI.CID<T, Code, Alg, 1>} */
const cid = CID.create(1, codec.code, hash)

return new Block({ value, bytes, cid })
return new Block({ value, bytes, cid: asLink(cid) })
}

/**
Expand All @@ -229,8 +236,7 @@ function createUnsafe ({ bytes, cid, value: maybeValue, codec }) {
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<T, Code, Alg, V>} */ (cid),
cid,
bytes,
value
})
Expand Down
4 changes: 2 additions & 2 deletions src/block/interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* 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'
import { CID } from '../cid/interface.js'

/**
* A byte-encoded representation of some type of `Data`.
Expand Down Expand Up @@ -64,7 +64,7 @@ export interface BlockView<
A extends number = number,
V extends Version = 1
> extends Block<T, C, A, V> {
cid: CID<T, C, A, V>
cid: Link<T, C, A, V>
value: T

links: () => Iterable<[string, CID]>
Expand Down
Loading

0 comments on commit 8152e34

Please sign in to comment.