Skip to content

Commit 783bce4

Browse files
committed
feat: bundle base58btc and base32 encoders
1 parent 655860e commit 783bce4

21 files changed

+581
-672
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ const bytes = dagcbor.encode({ hello: 'world' })
2323

2424
const hash = await sha256.digest(bytes)
2525
// raw codec is the only codec that is there by default
26-
const cid = CID.create(1, dagcbor.code, hash, {
27-
base: base32,
28-
base58btc
29-
})
26+
const cid = CID.create(1, dagcbor.code, hash)
3027
```
3128

3229
However, if you're doing this much you should probably use multiformats
@@ -35,9 +32,10 @@ with the `Block` API.
3532
```js
3633
// Import basics package with dep-free codecs, hashes, and base encodings
3734
import { block } from 'multiformats/basics'
35+
import { sha256 } from 'multiformats/hashes/sha2'
3836
import dagcbor from '@ipld/dag-cbor'
3937

40-
const encoder = block.encoder(dagcbor)
38+
const encoder = block.encoder(dagcbor, { hasher: sha256 })
4139
const hello = encoder.encode({ hello: 'world' })
4240
const cid = await hello.cid()
4341
```

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,19 @@
4848
"import": "./src/bases/base64-import.js",
4949
"browser": "./src/bases/base64-browser.js"
5050
},
51+
"./hashes/hasher": {
52+
"import": "./src/hashes/hasher.js"
53+
},
54+
"./hashes/digest": {
55+
"import": "./src/hashes/digest.js"
56+
},
5157
"./hashes/sha2": {
5258
"browser": "./src/hashes/sha2-browser.js",
5359
"import": "./src/hashes/sha2.js"
5460
},
61+
"./codecs/codec": {
62+
"import": "./src/codecs/codec.js"
63+
},
5564
"./codecs/json": {
5665
"import": "./src/codecs/json.js"
5766
},

src/bases/base.js

Lines changed: 90 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
*/
88

99
/**
10-
* @template T
10+
* @template {string} T
1111
* @typedef {import('./interface').Multibase<T>} Multibase
1212
*/
1313
/**
14-
* @template T
14+
* @template {string} T
1515
* @typedef {import('./interface').MultibaseEncoder<T>} MultibaseEncoder
1616
*/
1717

@@ -42,16 +42,24 @@ class Encoder {
4242
* @returns {Multibase<Prefix>}
4343
*/
4444
encode (bytes) {
45-
// @ts-ignore
46-
return `${this.prefix}${this.baseEncode(bytes)}`
45+
if (bytes instanceof Uint8Array) {
46+
return `${this.prefix}${this.baseEncode(bytes)}`
47+
} else {
48+
throw Error('Unknown type, must be binary type')
49+
}
4750
}
4851
}
4952

5053
/**
51-
* @template T
54+
* @template {string} T
5255
* @typedef {import('./interface').MultibaseDecoder<T>} MultibaseDecoder
5356
*/
5457

58+
/**
59+
* @template {string} T
60+
* @typedef {import('./interface').UnibaseDecoder<T>} UnibaseDecoder
61+
*/
62+
5563
/**
5664
* Class represents both BaseDecoder and MultibaseDecoder so it could be used
5765
* to decode multibases (with matching prefix) or just base decode strings
@@ -60,6 +68,7 @@ class Encoder {
6068
* @template {string} Base
6169
* @template {string} Prefix
6270
* @implements {MultibaseDecoder<Prefix>}
71+
* @implements {UnibaseDecoder<Prefix>}
6372
* @implements {BaseDecoder}
6473
*/
6574
class Decoder {
@@ -78,13 +87,83 @@ class Decoder {
7887
* @param {string} text
7988
*/
8089
decode (text) {
81-
switch (text[0]) {
82-
case this.prefix: {
83-
return this.baseDecode(text.slice(1))
84-
}
85-
default: {
86-
throw Error(`${this.name} expects input starting with ${this.prefix} and can not decode "${text}"`)
90+
if (typeof text === 'string') {
91+
switch (text[0]) {
92+
case this.prefix: {
93+
return this.baseDecode(text.slice(1))
94+
}
95+
default: {
96+
throw Error(`${this.name} expects input starting with ${this.prefix} and can not decode "${text}"`)
97+
}
8798
}
99+
} else {
100+
throw Error('Can only multibase decode strings')
101+
}
102+
}
103+
104+
/**
105+
* @template {string} OtherPrefix
106+
* @param {UnibaseDecoder<OtherPrefix>|ComposedDecoder<OtherPrefix>} decoder
107+
* @returns {ComposedDecoder<Prefix|OtherPrefix>}
108+
*/
109+
or (decoder) {
110+
if (decoder instanceof ComposedDecoder) {
111+
return new ComposedDecoder({ [this.prefix]: this, ...decoder.decoders })
112+
} else {
113+
return new ComposedDecoder({ [this.prefix]: this, [decoder.prefix]: decoder })
114+
}
115+
}
116+
}
117+
118+
/**
119+
* @template {string} Prefix
120+
* @implements {MultibaseDecoder<Prefix>}
121+
*/
122+
class ComposedDecoder {
123+
/**
124+
* @template {string} T
125+
* @param {UnibaseDecoder<T>} decoder
126+
* @returns {ComposedDecoder<T>}
127+
*/
128+
static from (decoder) {
129+
return new ComposedDecoder({ [decoder.prefix]: decoder })
130+
}
131+
132+
/**
133+
* @param {Object<Prefix, UnibaseDecoder<Prefix>>} decoders
134+
*/
135+
constructor (decoders) {
136+
/** @type {Object<string, UnibaseDecoder<Prefix>>} */
137+
this.decoders = decoders
138+
// so that we can distinguish between unibase and multibase
139+
/** @type {void} */
140+
this.prefix = null
141+
}
142+
143+
/**
144+
* @template {string} OtherPrefix
145+
* @param {UnibaseDecoder<OtherPrefix>|ComposedDecoder<OtherPrefix>} decoder
146+
* @returns {ComposedDecoder<Prefix|OtherPrefix>}
147+
*/
148+
or (decoder) {
149+
if (decoder instanceof ComposedDecoder) {
150+
return new ComposedDecoder({ ...this.decoders, ...decoder.decoders })
151+
} else {
152+
return new ComposedDecoder({ ...this.decoders, [decoder.prefix]: decoder })
153+
}
154+
}
155+
156+
/**
157+
* @param {string} input
158+
* @returns {Uint8Array}
159+
*/
160+
decode (input) {
161+
const prefix = input[0]
162+
const decoder = this.decoders[prefix]
163+
if (decoder) {
164+
return decoder.decode(input)
165+
} else {
166+
throw RangeError(`Unable to decode multibase string ${input}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`)
88167
}
89168
}
90169
}
@@ -191,15 +270,3 @@ export const withSettings = ({ name, prefix, settings, encode, decode }) =>
191270
*/
192271
export const from = ({ name, prefix, encode, decode }) =>
193272
new Codec(name, prefix, encode, decode)
194-
195-
export const notImplemented = ({ name, prefix }) =>
196-
from({
197-
name,
198-
prefix,
199-
encode: _ => {
200-
throw Error(`No ${name} encoder implementation was provided`)
201-
},
202-
decode: _ => {
203-
throw Error(`No ${name} decoder implemnetation was provided`)
204-
}
205-
})

src/bases/interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,8 @@ export interface MultibaseCodec<Prefix extends string> {
8383
encoder: MultibaseEncoder<Prefix>
8484
decoder: MultibaseDecoder<Prefix>
8585
}
86+
87+
88+
export interface UnibaseDecoder<Prefix extends string> extends MultibaseDecoder<Prefix> {
89+
prefix: Prefix
90+
}

src/basics-browser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// @ts-check
22

33
import * as base64 from './bases/base64-browser.js'
4-
import { cid, CID, block, Block, hasher, digest, varint, bytes, hashes, codecs, bases as _bases } from './basics.js'
4+
import { CID, Block, hasher, digest, varint, bytes, hashes, codecs, bases as _bases } from './basics.js'
55

66
const bases = { ..._bases, ...base64 }
77

8-
export { cid, CID, block, Block, hasher, digest, varint, bytes, hashes, codecs, bases }
8+
export { CID, Block, hasher, digest, varint, bytes, hashes, codecs, bases }

src/basics-import.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

2-
import { cid, CID, block, Block, hasher, digest, varint, bytes, hashes, codecs, bases as _bases } from './basics.js'
2+
import { CID, Block, hasher, digest, varint, bytes, hashes, codecs, bases as _bases } from './basics.js'
33
import * as base64 from './bases/base64-import.js'
44

55
const bases = { ..._bases, ...base64 }
6-
export { cid, CID, block, Block, hasher, digest, varint, bytes, hashes, codecs, bases }
6+
export { CID, Block, hasher, digest, varint, bytes, hashes, codecs, bases }

src/basics.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
// @ts-check
22

3-
import { notImplemented } from './bases/base.js'
43
import * as base32 from './bases/base32.js'
4+
import * as base58 from './bases/base58.js'
55
import * as sha2 from './hashes/sha2.js'
66

77
import raw from './codecs/raw.js'
88
import json from './codecs/json.js'
99

10-
import configure from './index.js'
10+
import { CID, Block, hasher, digest, varint, bytes } from './index.js'
1111

12-
const bases = { ...base32 }
12+
const bases = { ...base32, ...base58 }
1313
const hashes = { ...sha2 }
1414
const codecs = { raw, json }
1515

16-
const { cid, CID, block, Block, hasher, digest, varint, bytes } = configure({
17-
base: bases.base32,
18-
base58btc: notImplemented({ name: 'base58btc', prefix: 'z' }),
19-
hasher: hashes.sha256
20-
})
21-
22-
export { cid, CID, block, Block, hasher, digest, varint, bytes, hashes, bases, codecs }
16+
export { CID, Block, hasher, digest, varint, bytes, hashes, bases, codecs }

0 commit comments

Comments
 (0)