+237
@@ -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<Base extends string, Prefix extends string> implements MultibaseEncoder<Prefix>, 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<Prefix> {
|
||||
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<Base extends string, Prefix extends string> implements MultibaseDecoder<Prefix>, UnibaseDecoder<Prefix>, 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<OtherPrefix extends string> (decoder: UnibaseDecoder<OtherPrefix> | ComposedDecoder<OtherPrefix>): ComposedDecoder<Prefix | OtherPrefix> {
|
||||
return or(this, decoder)
|
||||
}
|
||||
}
|
||||
|
||||
type Decoders<Prefix extends string> = Record<Prefix, UnibaseDecoder<Prefix>>
|
||||
|
||||
class ComposedDecoder<Prefix extends string> implements MultibaseDecoder<Prefix>, CombobaseDecoder<Prefix> {
|
||||
readonly decoders: Decoders<Prefix>
|
||||
|
||||
constructor (decoders: Decoders<Prefix>) {
|
||||
this.decoders = decoders
|
||||
}
|
||||
|
||||
or <OtherPrefix extends string> (decoder: UnibaseDecoder<OtherPrefix> | ComposedDecoder<OtherPrefix>): ComposedDecoder<Prefix | OtherPrefix> {
|
||||
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 <L extends string, R extends string> (left: UnibaseDecoder<L> | CombobaseDecoder<L>, right: UnibaseDecoder<R> | CombobaseDecoder<R>): ComposedDecoder<L | R> {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return new ComposedDecoder({
|
||||
...(left.decoders ?? { [(left as UnibaseDecoder<L>).prefix]: left }),
|
||||
...(right.decoders ?? { [(right as UnibaseDecoder<R>).prefix]: right })
|
||||
} as Decoders<L | R>)
|
||||
}
|
||||
|
||||
export class Codec<Base extends string, Prefix extends string> implements MultibaseCodec<Prefix>, MultibaseEncoder<Prefix>, MultibaseDecoder<Prefix>, BaseCodec, BaseEncoder, BaseDecoder {
|
||||
readonly name: Base
|
||||
readonly prefix: Prefix
|
||||
readonly baseEncode: EncodeFn
|
||||
readonly baseDecode: DecodeFn
|
||||
readonly encoder: Encoder<Base, Prefix>
|
||||
readonly decoder: Decoder<Base, Prefix>
|
||||
|
||||
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 <Base extends string, Prefix extends string> ({ name, prefix, encode, decode }: { name: Base, prefix: Prefix, encode: EncodeFn, decode: DecodeFn }): Codec<Base, Prefix> {
|
||||
return new Codec(name, prefix, encode, decode)
|
||||
}
|
||||
|
||||
export function baseX <Base extends string, Prefix extends string> ({ name, prefix, alphabet }: { name: Base, prefix: Prefix, alphabet: string }): Codec<Base, Prefix> {
|
||||
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<string, number> = {}
|
||||
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 <Base extends string, Prefix extends string> ({ name, prefix, bitsPerChar, alphabet }: { name: Base, prefix: Prefix, bitsPerChar: number, alphabet: string }): Codec<Base, Prefix> {
|
||||
return from({
|
||||
prefix,
|
||||
name,
|
||||
encode (input: Uint8Array): string {
|
||||
return encode(input, alphabet, bitsPerChar)
|
||||
},
|
||||
decode (input: string): Uint8Array {
|
||||
return decode(input, alphabet, bitsPerChar, name)
|
||||
}
|
||||
})
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { baseX } from './base.js'
|
||||
|
||||
export const base10 = baseX({
|
||||
prefix: '9',
|
||||
name: 'base10',
|
||||
alphabet: '0123456789'
|
||||
})
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { rfc4648 } from './base.js'
|
||||
|
||||
export const base16 = rfc4648({
|
||||
prefix: 'f',
|
||||
name: 'base16',
|
||||
alphabet: '0123456789abcdef',
|
||||
bitsPerChar: 4
|
||||
})
|
||||
|
||||
export const base16upper = rfc4648({
|
||||
prefix: 'F',
|
||||
name: 'base16upper',
|
||||
alphabet: '0123456789ABCDEF',
|
||||
bitsPerChar: 4
|
||||
})
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { rfc4648 } from './base.js'
|
||||
|
||||
export const base2 = rfc4648({
|
||||
prefix: '0',
|
||||
name: 'base2',
|
||||
alphabet: '01',
|
||||
bitsPerChar: 1
|
||||
})
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { from } from './base.js'
|
||||
|
||||
const alphabet = Array.from('🚀🪐☄🛰🌌🌑🌒🌓🌔🌕🌖🌗🌘🌍🌏🌎🐉☀💻🖥💾💿😂❤😍🤣😊🙏💕😭😘👍😅👏😁🔥🥰💔💖💙😢🤔😆🙄💪😉☺👌🤗💜😔😎😇🌹🤦🎉💞✌✨🤷😱😌🌸🙌😋💗💚😏💛🙂💓🤩😄😀🖤😃💯🙈👇🎶😒🤭❣😜💋👀😪😑💥🙋😞😩😡🤪👊🥳😥🤤👉💃😳✋😚😝😴🌟😬🙃🍀🌷😻😓⭐✅🥺🌈😈🤘💦✔😣🏃💐☹🎊💘😠☝😕🌺🎂🌻😐🖕💝🙊😹🗣💫💀👑🎵🤞😛🔴😤🌼😫⚽🤙☕🏆🤫👈😮🙆🍻🍃🐶💁😲🌿🧡🎁⚡🌞🎈❌✊👋😰🤨😶🤝🚶💰🍓💢🤟🙁🚨💨🤬✈🎀🍺🤓😙💟🌱😖👶🥴▶➡❓💎💸⬇😨🌚🦋😷🕺⚠🙅😟😵👎🤲🤠🤧📌🔵💅🧐🐾🍒😗🤑🌊🤯🐷☎💧😯💆👆🎤🙇🍑❄🌴💣🐸💌📍🥀🤢👅💡💩👐📸👻🤐🤮🎼🥵🚩🍎🍊👼💍📣🥂')
|
||||
const alphabetBytesToChars: string[] = (alphabet.reduce<string[]>((p, c, i) => { p[i] = c; return p }, ([])))
|
||||
const alphabetCharsToBytes: number[] = (alphabet.reduce<number[]>((p, c, i) => { p[c.codePointAt(0) as number] = i; return p }, ([])))
|
||||
|
||||
function encode (data: Uint8Array): string {
|
||||
return data.reduce((p, c) => {
|
||||
p += alphabetBytesToChars[c]
|
||||
return p
|
||||
}, '')
|
||||
}
|
||||
|
||||
function decode (str: string): Uint8Array {
|
||||
const byts = []
|
||||
for (const char of str) {
|
||||
const byt = alphabetCharsToBytes[char.codePointAt(0) as number]
|
||||
if (byt === undefined) {
|
||||
throw new Error(`Non-base256emoji character: ${char}`)
|
||||
}
|
||||
byts.push(byt)
|
||||
}
|
||||
return new Uint8Array(byts)
|
||||
}
|
||||
|
||||
export const base256emoji = from({
|
||||
prefix: '🚀',
|
||||
name: 'base256emoji',
|
||||
encode,
|
||||
decode
|
||||
})
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { rfc4648 } from './base.js'
|
||||
|
||||
export const base32 = rfc4648({
|
||||
prefix: 'b',
|
||||
name: 'base32',
|
||||
alphabet: 'abcdefghijklmnopqrstuvwxyz234567',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32upper = rfc4648({
|
||||
prefix: 'B',
|
||||
name: 'base32upper',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32pad = rfc4648({
|
||||
prefix: 'c',
|
||||
name: 'base32pad',
|
||||
alphabet: 'abcdefghijklmnopqrstuvwxyz234567=',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32padupper = rfc4648({
|
||||
prefix: 'C',
|
||||
name: 'base32padupper',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32hex = rfc4648({
|
||||
prefix: 'v',
|
||||
name: 'base32hex',
|
||||
alphabet: '0123456789abcdefghijklmnopqrstuv',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32hexupper = rfc4648({
|
||||
prefix: 'V',
|
||||
name: 'base32hexupper',
|
||||
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32hexpad = rfc4648({
|
||||
prefix: 't',
|
||||
name: 'base32hexpad',
|
||||
alphabet: '0123456789abcdefghijklmnopqrstuv=',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32hexpadupper = rfc4648({
|
||||
prefix: 'T',
|
||||
name: 'base32hexpadupper',
|
||||
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUV=',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
|
||||
export const base32z = rfc4648({
|
||||
prefix: 'h',
|
||||
name: 'base32z',
|
||||
alphabet: 'ybndrfg8ejkmcpqxot1uwisza345h769',
|
||||
bitsPerChar: 5
|
||||
})
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { baseX } from './base.js'
|
||||
|
||||
export const base36 = baseX({
|
||||
prefix: 'k',
|
||||
name: 'base36',
|
||||
alphabet: '0123456789abcdefghijklmnopqrstuvwxyz'
|
||||
})
|
||||
|
||||
export const base36upper = baseX({
|
||||
prefix: 'K',
|
||||
name: 'base36upper',
|
||||
alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
})
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { baseX } from './base.js'
|
||||
|
||||
export const base58btc = baseX({
|
||||
name: 'base58btc',
|
||||
prefix: 'z',
|
||||
alphabet: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
})
|
||||
|
||||
export const base58flickr = baseX({
|
||||
name: 'base58flickr',
|
||||
prefix: 'Z',
|
||||
alphabet: '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
|
||||
})
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { rfc4648 } from './base.js'
|
||||
|
||||
export const base64 = rfc4648({
|
||||
prefix: 'm',
|
||||
name: 'base64',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
||||
bitsPerChar: 6
|
||||
})
|
||||
|
||||
export const base64pad = rfc4648({
|
||||
prefix: 'M',
|
||||
name: 'base64pad',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
|
||||
bitsPerChar: 6
|
||||
})
|
||||
|
||||
export const base64url = rfc4648({
|
||||
prefix: 'u',
|
||||
name: 'base64url',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
|
||||
bitsPerChar: 6
|
||||
})
|
||||
|
||||
export const base64urlpad = rfc4648({
|
||||
prefix: 'U',
|
||||
name: 'base64urlpad',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=',
|
||||
bitsPerChar: 6
|
||||
})
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { rfc4648 } from './base.js'
|
||||
|
||||
export const base8 = rfc4648({
|
||||
prefix: '7',
|
||||
name: 'base8',
|
||||
alphabet: '01234567',
|
||||
bitsPerChar: 3
|
||||
})
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { fromString, toString } from '../bytes.js'
|
||||
import { from } from './base.js'
|
||||
|
||||
export const identity = from({
|
||||
prefix: '\x00',
|
||||
name: 'identity',
|
||||
encode: (buf) => toString(buf),
|
||||
decode: (str) => fromString(str)
|
||||
})
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// Base encoders / decoders just base encode / decode between binary and
|
||||
// textual representation. They are unaware of multibase.
|
||||
|
||||
/**
|
||||
* Base encoder just encodes bytes into base encoded string.
|
||||
*/
|
||||
export interface BaseEncoder {
|
||||
/**
|
||||
* Base encodes to a **plain** (and not a multibase) string. Unlike
|
||||
* `encode` no multibase prefix is added.
|
||||
*/
|
||||
baseEncode(bytes: Uint8Array): string
|
||||
}
|
||||
|
||||
/**
|
||||
* Base decoder decodes encoded with matching base encoding into bytes.
|
||||
*/
|
||||
export interface BaseDecoder {
|
||||
/**
|
||||
* Decodes **plain** (and not a multibase) string. Unlike
|
||||
* decode
|
||||
*/
|
||||
baseDecode(text: string): Uint8Array
|
||||
}
|
||||
|
||||
/**
|
||||
* Base codec is just dual of encoder and decoder.
|
||||
*/
|
||||
export interface BaseCodec {
|
||||
encoder: BaseEncoder
|
||||
decoder: BaseDecoder
|
||||
}
|
||||
|
||||
/**
|
||||
* Multibase represents base encoded strings with a prefix first character
|
||||
* describing it's encoding.
|
||||
*/
|
||||
export type Multibase<Prefix extends string> =
|
||||
| string
|
||||
| string & { [0]: Prefix }
|
||||
|
||||
/**
|
||||
* Multibase encoder for the specific base encoding encodes bytes into
|
||||
* multibase of that encoding.
|
||||
*/
|
||||
export interface MultibaseEncoder<Prefix extends string> {
|
||||
/**
|
||||
* Name of the encoding.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* Prefix character for that base encoding.
|
||||
*/
|
||||
prefix: Prefix
|
||||
/**
|
||||
* Encodes binary data into **multibase** string (which will have a
|
||||
* prefix added).
|
||||
*/
|
||||
encode(bytes: Uint8Array): Multibase<Prefix>
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface implemented by multibase decoder, that takes multibase strings
|
||||
* to bytes. It may support single encoding like base32 or multiple encodings
|
||||
* like base32, base58btc, base64. If passed multibase is incompatible it will
|
||||
* throw an exception.
|
||||
*/
|
||||
export interface MultibaseDecoder<Prefix extends string> {
|
||||
/**
|
||||
* Decodes **multibase** string (which must have a multibase prefix added).
|
||||
* If prefix does not match
|
||||
*/
|
||||
decode(multibase: Multibase<Prefix>): Uint8Array
|
||||
}
|
||||
|
||||
/**
|
||||
* Dual of multibase encoder and decoder.
|
||||
*/
|
||||
export interface MultibaseCodec<Prefix extends string> {
|
||||
name: string
|
||||
prefix: Prefix
|
||||
encoder: MultibaseEncoder<Prefix>
|
||||
decoder: MultibaseDecoder<Prefix>
|
||||
}
|
||||
|
||||
export interface UnibaseDecoder<Prefix extends string> extends MultibaseDecoder<Prefix> {
|
||||
// Reserve this property so it can be used to derive type.
|
||||
readonly decoders?: null
|
||||
|
||||
readonly prefix: Prefix
|
||||
}
|
||||
|
||||
export interface CombobaseDecoder<Prefix extends string> extends MultibaseDecoder<Prefix> {
|
||||
readonly decoders: Record<Prefix, UnibaseDecoder<Prefix>>
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import * as base10 from './bases/base10.js'
|
||||
import * as base16 from './bases/base16.js'
|
||||
import * as base2 from './bases/base2.js'
|
||||
import * as base256emoji from './bases/base256emoji.js'
|
||||
import * as base32 from './bases/base32.js'
|
||||
import * as base36 from './bases/base36.js'
|
||||
import * as base58 from './bases/base58.js'
|
||||
import * as base64 from './bases/base64.js'
|
||||
import * as base8 from './bases/base8.js'
|
||||
import * as identityBase from './bases/identity.js'
|
||||
import * as json from './codecs/json.js'
|
||||
import * as raw from './codecs/raw.js'
|
||||
import * as identity from './hashes/identity.js'
|
||||
import * as sha2 from './hashes/sha2.js'
|
||||
import { CID, hasher, digest, varint, bytes } from './index.js'
|
||||
|
||||
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 }
|
||||
+239
@@ -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 <T> (source: T, base: Array<string | number>): 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<string> {
|
||||
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 <T> (source: T, base: Array<string | number>): Iterable<string> {
|
||||
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 <T> (source: T, path: string[]): API.BlockCursorView<unknown> {
|
||||
let node = source as Record<string, any>
|
||||
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<T, C extends number, A extends number, V extends API.Version> implements API.BlockView<T, C, A, V> {
|
||||
readonly cid: CID<T, C, A, V>
|
||||
readonly bytes: API.ByteView<T>
|
||||
readonly value: T
|
||||
readonly asBlock: this
|
||||
|
||||
constructor ({ cid, bytes, value }: { cid: CID<T, C, A, V>, bytes: API.ByteView<T>, 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<unknown, number, number, API.Version>]> {
|
||||
return links(this.value, [])
|
||||
}
|
||||
|
||||
tree (): Iterable<string> {
|
||||
return tree(this.value, [])
|
||||
}
|
||||
|
||||
get (path = '/'): API.BlockCursorView<unknown> {
|
||||
return get(this.value, path.split('/').filter(Boolean))
|
||||
}
|
||||
}
|
||||
|
||||
interface EncodeInput <T, Code extends number, Alg extends number> {
|
||||
value: T
|
||||
codec: API.BlockEncoder<Code, T>
|
||||
hasher: API.MultihashHasher<Alg>
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 <T, Code extends number, Alg extends number> ({ value, codec, hasher }: EncodeInput<T, Code, Alg>): Promise<API.BlockView<T, Code, Alg>> {
|
||||
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<T, Code, Alg, 1>
|
||||
|
||||
return new Block({ value, bytes, cid })
|
||||
}
|
||||
|
||||
interface DecodeInput <T, Code extends number, Alg extends number> {
|
||||
bytes: API.ByteView<T>
|
||||
codec: API.BlockDecoder<Code, T>
|
||||
hasher: API.MultihashHasher<Alg>
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 <T, Code extends number, Alg extends number> ({ bytes, codec, hasher }: DecodeInput<T, Code, Alg>): Promise<API.BlockView<T, Code, Alg>> {
|
||||
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<T, Code, Alg, 1>
|
||||
|
||||
return new Block({ value, bytes, cid })
|
||||
}
|
||||
|
||||
type CreateUnsafeInput <T, Code extends number, Alg extends number, V extends API.Version> = {
|
||||
cid: API.Link<T, Code, Alg, V>
|
||||
value: T
|
||||
codec?: API.BlockDecoder<Code, T>
|
||||
bytes: API.ByteView<T>
|
||||
} | {
|
||||
cid: API.Link<T, Code, Alg, V>
|
||||
value?: undefined
|
||||
codec: API.BlockDecoder<Code, T>
|
||||
bytes: API.ByteView<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 <T, Code extends number, Alg extends number, V extends API.Version> ({ bytes, cid, value: maybeValue, codec }: CreateUnsafeInput<T, Code, Alg, V>): API.BlockView<T, Code, Alg, V> {
|
||||
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<T, Code, Alg, V>,
|
||||
bytes,
|
||||
value
|
||||
})
|
||||
}
|
||||
|
||||
interface CreateInput <T, Code extends number, Alg extends number, V extends API.Version> {
|
||||
bytes: API.ByteView<T>
|
||||
cid: API.Link<T, Code, Alg, V>
|
||||
hasher: API.MultihashHasher<Alg>
|
||||
codec: API.BlockDecoder<Code, T>
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 <T, Code extends number, Alg extends number, V extends API.Version> ({ bytes, cid, hasher, codec }: CreateInput<T, Code, Alg, V>): Promise<API.BlockView<T, Code, Alg, V>> {
|
||||
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
|
||||
})
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import type { CID } from '../cid.js'
|
||||
import type { Link, Version } from '../link/interface.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<Data> extends Uint8Array, Phantom<Data> {}
|
||||
|
||||
/**
|
||||
* Similar to ByteView but extends ArrayBuffer.
|
||||
*/
|
||||
export interface ArrayBufferView<Data> extends ArrayBuffer, Phantom<Data> {}
|
||||
|
||||
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 requiring 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<T> {
|
||||
// This field can not be represented because field name is non-existent
|
||||
// unique symbol. But given that field is optional any object will valid
|
||||
// type constraint.
|
||||
[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<T>
|
||||
cid: Link<T, C, A, V>
|
||||
}
|
||||
|
||||
export type BlockCursorView<T = unknown> =
|
||||
| { 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<T, C, A, V> {
|
||||
cid: CID<T, C, A, V>
|
||||
value: T
|
||||
|
||||
links(): Iterable<[string, CID]>
|
||||
tree(): Iterable<string>
|
||||
get(path: string): BlockCursorView<unknown>
|
||||
}
|
||||
+46
@@ -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)
|
||||
}
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
import { base32 } from './bases/base32.js'
|
||||
import { base58btc } from './bases/base58.js'
|
||||
import { coerce } from './bytes.js'
|
||||
import * as Digest from './hashes/digest.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'
|
||||
|
||||
export function format <T extends API.Link<unknown, number, number, API.Version>, Prefix extends string> (link: T, base?: API.MultibaseEncoder<Prefix>): API.ToString<T, Prefix> {
|
||||
const { bytes, version } = link
|
||||
switch (version) {
|
||||
case 0:
|
||||
return toStringV0(
|
||||
bytes,
|
||||
baseCache(link),
|
||||
base as API.MultibaseEncoder<'z'> ?? base58btc.encoder
|
||||
)
|
||||
default:
|
||||
return toStringV1(
|
||||
bytes,
|
||||
baseCache(link),
|
||||
(base ?? base32.encoder) as API.MultibaseEncoder<Prefix>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function toJSON <Link extends API.UnknownLink> (link: Link): API.LinkJSON<Link> {
|
||||
return {
|
||||
'/': format(link)
|
||||
}
|
||||
}
|
||||
|
||||
export function fromJSON <Link extends API.UnknownLink> (json: API.LinkJSON<Link>): CID<unknown, number, number, API.Version> {
|
||||
return CID.parse(json['/'])
|
||||
}
|
||||
|
||||
const cache = new WeakMap<API.UnknownLink, Map<string, string>>()
|
||||
|
||||
function baseCache (cid: API.UnknownLink): Map<string, string> {
|
||||
const baseCache = cache.get(cid)
|
||||
if (baseCache == null) {
|
||||
const baseCache = new Map()
|
||||
cache.set(cid, baseCache)
|
||||
return baseCache
|
||||
}
|
||||
return baseCache
|
||||
}
|
||||
|
||||
export class CID<Data = unknown, Format extends number = number, Alg extends number = number, Version extends API.Version = API.Version> implements API.Link<Data, Format, Alg, Version> {
|
||||
readonly code: Format
|
||||
readonly version: Version
|
||||
readonly multihash: API.MultihashDigest<Alg>
|
||||
readonly bytes: Uint8Array
|
||||
readonly '/': Uint8Array
|
||||
|
||||
/**
|
||||
* @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: Version, code: Format, multihash: API.MultihashDigest<Alg>, bytes: Uint8Array) {
|
||||
this.code = code
|
||||
this.version = version
|
||||
this.multihash = multihash
|
||||
this.bytes = bytes
|
||||
|
||||
// flag to serializers that this is a CID and
|
||||
// should be treated specially
|
||||
this['/'] = bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Signalling `cid.asCID === cid` has been replaced with `cid['/'] === cid.bytes`
|
||||
* please either use `CID.asCID(cid)` or switch to new signalling mechanism
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get asCID (): this {
|
||||
return this
|
||||
}
|
||||
|
||||
// ArrayBufferView
|
||||
get byteOffset (): number {
|
||||
return this.bytes.byteOffset
|
||||
}
|
||||
|
||||
// ArrayBufferView
|
||||
get byteLength (): number {
|
||||
return this.bytes.byteLength
|
||||
}
|
||||
|
||||
toV0 (): CID<Data, API.DAG_PB, API.SHA_256, 0> {
|
||||
switch (this.version) {
|
||||
case 0: {
|
||||
return this as CID<Data, API.DAG_PB, API.SHA_256, 0>
|
||||
}
|
||||
case 1: {
|
||||
const { code, multihash } = this
|
||||
|
||||
if (code !== DAG_PB_CODE) {
|
||||
throw new Error('Cannot convert a non dag-pb CID to CIDv0')
|
||||
}
|
||||
|
||||
// sha2-256
|
||||
if (multihash.code !== SHA_256_CODE) {
|
||||
throw new Error('Cannot convert non sha2-256 multihash CID to CIDv0')
|
||||
}
|
||||
|
||||
return (
|
||||
CID.createV0(
|
||||
multihash as API.MultihashDigest<API.SHA_256>
|
||||
)
|
||||
)
|
||||
}
|
||||
default: {
|
||||
throw Error(
|
||||
`Can not convert CID version ${this.version} to version 0. This is a bug please report`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toV1 (): CID<Data, Format, Alg, 1> {
|
||||
switch (this.version) {
|
||||
case 0: {
|
||||
const { code, digest } = this.multihash
|
||||
const multihash = Digest.create(code, digest)
|
||||
return (
|
||||
CID.createV1(this.code, multihash)
|
||||
)
|
||||
}
|
||||
case 1: {
|
||||
return this as CID<Data, Format, Alg, 1>
|
||||
}
|
||||
default: {
|
||||
throw Error(
|
||||
`Can not convert CID version ${this.version} to version 1. This is a bug please report`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
equals (other: unknown): other is CID<Data, Format, Alg, Version> {
|
||||
return CID.equals(this, other)
|
||||
}
|
||||
|
||||
static equals <Data, Format extends number, Alg extends number, Version extends API.Version>(self: API.Link<Data, Format, Alg, Version>, other: unknown): other is CID {
|
||||
const unknown = other as { code?: unknown, version?: unknown, multihash?: unknown }
|
||||
return (
|
||||
unknown != null &&
|
||||
self.code === unknown.code &&
|
||||
self.version === unknown.version &&
|
||||
Digest.equals(self.multihash, unknown.multihash)
|
||||
)
|
||||
}
|
||||
|
||||
toString (base?: API.MultibaseEncoder<string>): string {
|
||||
return format(this, base)
|
||||
}
|
||||
|
||||
toJSON (): API.LinkJSON<this> {
|
||||
return { '/': format(this) }
|
||||
}
|
||||
|
||||
link (): this {
|
||||
return this
|
||||
}
|
||||
|
||||
readonly [Symbol.toStringTag] = 'CID';
|
||||
|
||||
// Legacy
|
||||
|
||||
[Symbol.for('nodejs.util.inspect.custom')] (): string {
|
||||
return `CID(${this.toString()})`
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes any input `value` and returns a `CID` instance if it was
|
||||
* a `CID` otherwise returns `null`. If `value` is instanceof `CID`
|
||||
* it will return value back. If `value` is not instance of this CID
|
||||
* class, but is compatible CID it will return new instance of this
|
||||
* `CID` class. Otherwise returns null.
|
||||
*
|
||||
* This allows two different incompatible versions of CID library to
|
||||
* co-exist and interop as long as binary interface is compatible.
|
||||
*/
|
||||
static asCID <Data, Format extends number, Alg extends number, Version extends API.Version, U>(input: API.Link<Data, Format, Alg, Version> | U): CID<Data, Format, Alg, Version> | null {
|
||||
if (input == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
const value = input as any
|
||||
if (value instanceof CID) {
|
||||
// If value is instance of CID then we're all set.
|
||||
return value
|
||||
} else if ((value['/'] != null && value['/'] === value.bytes) || value.asCID === value) {
|
||||
// If value isn't instance of this CID class but `this.asCID === this` or
|
||||
// `value['/'] === value.bytes` 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 as API.MultihashDigest<Alg>,
|
||||
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 = Digest.decode(multihash) as API.MultihashDigest<Alg>
|
||||
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`.
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 <Data, Format extends number, Alg extends number, Version extends API.Version>(version: Version, code: Format, digest: API.MultihashDigest<Alg>): CID<Data, Format, Alg, Version> {
|
||||
if (typeof code !== 'number') {
|
||||
throw new Error('String codecs are no longer supported')
|
||||
}
|
||||
|
||||
if (!(digest.bytes instanceof Uint8Array)) {
|
||||
throw new Error('Invalid digest')
|
||||
}
|
||||
|
||||
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`
|
||||
)
|
||||
} else {
|
||||
return new CID(version, code, digest, digest.bytes)
|
||||
}
|
||||
}
|
||||
case 1: {
|
||||
const bytes = encodeCID(version, code, digest.bytes)
|
||||
return new CID(version, code, digest, bytes)
|
||||
}
|
||||
default: {
|
||||
throw new Error('Invalid version')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of `create` for CIDv0.
|
||||
*/
|
||||
static createV0 <T = unknown>(digest: API.MultihashDigest<typeof SHA_256_CODE>): CID<T, typeof DAG_PB_CODE, typeof SHA_256_CODE, 0> {
|
||||
return CID.create(0, DAG_PB_CODE, digest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of `create` for CIDv1.
|
||||
*
|
||||
* @param code - Content encoding format code.
|
||||
* @param digest - Multihash of the content.
|
||||
*/
|
||||
static createV1 <Data, Code extends number, Alg extends number>(code: Code, digest: API.MultihashDigest<Alg>): CID<Data, Code, Alg, 1> {
|
||||
return CID.create(1, code, digest)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static decode <Data, Code extends number, Alg extends number, Version extends API.Version>(bytes: API.ByteView<API.Link<Data, Code, Alg, Version>>): CID<Data, Code, Alg, Version> {
|
||||
const [cid, remainder] = CID.decodeFirst(bytes)
|
||||
if (remainder.length !== 0) {
|
||||
throw new Error('Incorrect length')
|
||||
}
|
||||
return cid
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoded a CID from its binary representation at the beginning of a byte
|
||||
* array.
|
||||
*
|
||||
* Returns an array with the first element containing the CID and the second
|
||||
* 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.
|
||||
*/
|
||||
static decodeFirst <T, C extends number, A extends number, V extends API.Version>(bytes: API.ByteView<API.Link<T, C, A, V>>): [CID<T, C, A, V>, Uint8Array] {
|
||||
const specs = CID.inspectBytes(bytes)
|
||||
const prefixSize = specs.size - 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 as API.MultihashDigest<API.SHA_256>)
|
||||
: CID.createV1(specs.codec, digest)
|
||||
return [cid as CID<T, C, A, V>, bytes.subarray(specs.size)]
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect the initial bytes of a CID to determine its properties.
|
||||
*
|
||||
* Involves decoding up to 4 varints. Typically this will require only 4 to 6
|
||||
* bytes but for larger multicodec code values and larger multihash digest
|
||||
* 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.
|
||||
*/
|
||||
static inspectBytes <T, C extends number, A extends number, V extends API.Version>(initialBytes: API.ByteView<API.Link<T, C, A, V>>): { version: V, codec: C, multihashCode: A, digestSize: number, multihashSize: number, size: number } {
|
||||
let offset = 0
|
||||
const next = (): number => {
|
||||
const [i, length] = varint.decode(initialBytes.subarray(offset))
|
||||
offset += length
|
||||
return i
|
||||
}
|
||||
|
||||
let version = next() as V
|
||||
let codec = DAG_PB_CODE as C
|
||||
if (version as number === 18) {
|
||||
// CIDv0
|
||||
version = 0 as V
|
||||
offset = 0
|
||||
} else {
|
||||
codec = next() as C
|
||||
}
|
||||
|
||||
if (version !== 0 && version !== 1) {
|
||||
throw new RangeError(`Invalid CID version ${version}`)
|
||||
}
|
||||
|
||||
const prefixSize = offset
|
||||
const multihashCode = next() as A // multihash code
|
||||
const digestSize = next() // multihash length
|
||||
const size = offset + digestSize
|
||||
const multihashSize = size - prefixSize
|
||||
|
||||
return { version, codec, multihashCode, digestSize, multihashSize, size }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
static parse <Prefix extends string, Data, Code extends number, Alg extends number, Version extends API.Version>(source: API.ToString<API.Link<Data, Code, Alg, Version>, Prefix>, base?: API.MultibaseDecoder<Prefix>): CID<Data, Code, Alg, Version> {
|
||||
const [prefix, bytes] = parseCIDtoBytes(source, base)
|
||||
|
||||
const cid = CID.decode(bytes)
|
||||
|
||||
if (cid.version === 0 && source[0] !== 'Q') {
|
||||
throw Error('Version 0 CID string must not include multibase prefix')
|
||||
}
|
||||
|
||||
// Cache string representation to avoid computing it on `this.toString()`
|
||||
baseCache(cid).set(prefix, source)
|
||||
|
||||
return cid
|
||||
}
|
||||
}
|
||||
|
||||
function parseCIDtoBytes <Prefix extends string, Data, Code extends number, Alg extends number, Version extends API.Version> (source: API.ToString<API.Link<Data, Code, Alg, Version>, Prefix>, base?: API.MultibaseDecoder<Prefix>): [Prefix, API.ByteView<API.Link<Data, Code, Alg, Version>>] {
|
||||
switch (source[0]) {
|
||||
// CIDv0 is parsed differently
|
||||
case 'Q': {
|
||||
const decoder = base ?? base58btc
|
||||
return [
|
||||
base58btc.prefix as Prefix,
|
||||
decoder.decode(`${base58btc.prefix}${source}`)
|
||||
]
|
||||
}
|
||||
case base58btc.prefix: {
|
||||
const decoder = base ?? base58btc
|
||||
return [base58btc.prefix as Prefix, decoder.decode(source)]
|
||||
}
|
||||
case base32.prefix: {
|
||||
const decoder = base ?? base32
|
||||
return [base32.prefix as Prefix, decoder.decode(source)]
|
||||
}
|
||||
default: {
|
||||
if (base == null) {
|
||||
throw Error(
|
||||
'To parse non base32 or base58btc encoded CID multibase decoder must be provided'
|
||||
)
|
||||
}
|
||||
return [source[0] as Prefix, base.decode(source)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toStringV0 (bytes: Uint8Array, cache: Map<string, string>, base: API.MultibaseEncoder<'z'>): string {
|
||||
const { prefix } = base
|
||||
if (prefix !== base58btc.prefix) {
|
||||
throw Error(`Cannot string encode V0 in ${base.name} encoding`)
|
||||
}
|
||||
|
||||
const cid = cache.get(prefix)
|
||||
if (cid == null) {
|
||||
const cid = base.encode(bytes).slice(1)
|
||||
cache.set(prefix, cid)
|
||||
return cid
|
||||
} else {
|
||||
return cid
|
||||
}
|
||||
}
|
||||
|
||||
function toStringV1 <Prefix extends string> (bytes: Uint8Array, cache: Map<string, string>, base: API.MultibaseEncoder<Prefix>): string {
|
||||
const { prefix } = base
|
||||
const cid = cache.get(prefix)
|
||||
if (cid == null) {
|
||||
const cid = base.encode(bytes)
|
||||
cache.set(prefix, cid)
|
||||
return cid
|
||||
} else {
|
||||
return cid
|
||||
}
|
||||
}
|
||||
|
||||
const DAG_PB_CODE = 0x70
|
||||
const SHA_256_CODE = 0x12
|
||||
|
||||
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)
|
||||
varint.encodeTo(version, bytes, 0)
|
||||
varint.encodeTo(code, bytes, codeOffset)
|
||||
bytes.set(multihash, hashOffset)
|
||||
return bytes
|
||||
}
|
||||
|
||||
const cidSymbol = Symbol.for('@ipld/js-cid/CID')
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import type { ArrayBufferView, ByteView } from '../block/interface.js'
|
||||
|
||||
/**
|
||||
* IPLD encoder part of the codec.
|
||||
*/
|
||||
export interface BlockEncoder<Code extends number, T> {
|
||||
name: string
|
||||
code: Code
|
||||
encode(data: T): ByteView<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* IPLD decoder part of the codec.
|
||||
*/
|
||||
export interface BlockDecoder<Code extends number, T> {
|
||||
code: Code
|
||||
decode(bytes: ByteView<T> | ArrayBufferView<T>): T
|
||||
}
|
||||
|
||||
/**
|
||||
* An IPLD codec is a combination of both encoder and decoder.
|
||||
*/
|
||||
export interface BlockCodec<Code extends number, T> extends BlockEncoder<Code, T>, BlockDecoder<Code, T> {}
|
||||
|
||||
export type { ArrayBufferView, ByteView }
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import type { ArrayBufferView, ByteView } from './interface.js'
|
||||
|
||||
const textEncoder = new TextEncoder()
|
||||
const textDecoder = new TextDecoder()
|
||||
|
||||
export const name = 'json'
|
||||
export const code = 0x0200
|
||||
|
||||
export function encode <T> (node: T): ByteView<T> {
|
||||
return textEncoder.encode(JSON.stringify(node))
|
||||
}
|
||||
|
||||
export function decode <T> (data: ByteView<T> | ArrayBufferView<T>): T {
|
||||
return JSON.parse(textDecoder.decode(data))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { coerce } from '../bytes.js'
|
||||
import type { ArrayBufferView, ByteView } from './interface.js'
|
||||
|
||||
export const name = 'raw'
|
||||
export const code = 0x55
|
||||
|
||||
export function encode (node: Uint8Array): ByteView<Uint8Array> {
|
||||
return coerce(node)
|
||||
}
|
||||
|
||||
export function decode (data: ByteView<Uint8Array> | ArrayBufferView<Uint8Array>): Uint8Array {
|
||||
return coerce(data)
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { coerce, equals as equalBytes } from '../bytes.js'
|
||||
import * as varint from '../varint.js'
|
||||
import type { MultihashDigest } from './interface.js'
|
||||
|
||||
/**
|
||||
* Creates a multihash digest.
|
||||
*/
|
||||
export function create <Code extends number> (code: Code, digest: Uint8Array): Digest<Code, number> {
|
||||
const size = digest.byteLength
|
||||
const sizeOffset = varint.encodingLength(code)
|
||||
const digestOffset = sizeOffset + varint.encodingLength(size)
|
||||
|
||||
const bytes = new Uint8Array(digestOffset + size)
|
||||
varint.encodeTo(code, bytes, 0)
|
||||
varint.encodeTo(size, bytes, sizeOffset)
|
||||
bytes.set(digest, digestOffset)
|
||||
|
||||
return new Digest(code, size, digest, bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns bytes representation of multihash digest into an instance.
|
||||
*/
|
||||
export function decode (multihash: Uint8Array): MultihashDigest {
|
||||
const bytes = coerce(multihash)
|
||||
const [code, sizeOffset] = varint.decode(bytes)
|
||||
const [size, digestOffset] = varint.decode(bytes.subarray(sizeOffset))
|
||||
const digest = bytes.subarray(sizeOffset + digestOffset)
|
||||
|
||||
if (digest.byteLength !== size) {
|
||||
throw new Error('Incorrect length')
|
||||
}
|
||||
|
||||
return new Digest(code, size, digest, bytes)
|
||||
}
|
||||
|
||||
export function equals (a: MultihashDigest, b: unknown): b is MultihashDigest {
|
||||
if (a === b) {
|
||||
return true
|
||||
} else {
|
||||
const data = b as { code?: unknown, size?: unknown, bytes?: unknown }
|
||||
|
||||
return (
|
||||
a.code === data.code &&
|
||||
a.size === data.size &&
|
||||
data.bytes instanceof Uint8Array &&
|
||||
equalBytes(a.bytes, data.bytes)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a multihash digest which carries information about the
|
||||
* hashing algorithm and an actual hash digest.
|
||||
*/
|
||||
export class Digest<Code extends number, Size extends number> implements MultihashDigest {
|
||||
readonly code: Code
|
||||
readonly size: Size
|
||||
readonly digest: Uint8Array
|
||||
readonly bytes: Uint8Array
|
||||
|
||||
/**
|
||||
* Creates a multihash digest.
|
||||
*/
|
||||
constructor (code: Code, size: Size, digest: Uint8Array, bytes: Uint8Array) {
|
||||
this.code = code
|
||||
this.size = size
|
||||
this.digest = digest
|
||||
this.bytes = bytes
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import * as Digest from './digest.js'
|
||||
import type { MultihashHasher } from './interface.js'
|
||||
|
||||
type Await<T> = Promise<T> | T
|
||||
|
||||
export function from <Name extends string, Code extends number> ({ name, code, encode }: { name: Name, code: Code, encode(input: Uint8Array): Await<Uint8Array> }): Hasher<Name, Code> {
|
||||
return new Hasher(name, code, encode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hasher represents a hashing algorithm implementation that produces as
|
||||
* `MultihashDigest`.
|
||||
*/
|
||||
export class Hasher<Name extends string, Code extends number> implements MultihashHasher<Code> {
|
||||
readonly name: Name
|
||||
readonly code: Code
|
||||
readonly encode: (input: Uint8Array) => Await<Uint8Array>
|
||||
|
||||
constructor (name: Name, code: Code, encode: (input: Uint8Array) => Await<Uint8Array>) {
|
||||
this.name = name
|
||||
this.code = code
|
||||
this.encode = encode
|
||||
}
|
||||
|
||||
digest (input: Uint8Array): Await<Digest.Digest<Code, number>> {
|
||||
if (input instanceof Uint8Array) {
|
||||
const result = this.encode(input)
|
||||
return result instanceof Uint8Array
|
||||
? Digest.create(this.code, result)
|
||||
/* c8 ignore next 1 */
|
||||
: result.then(digest => Digest.create(this.code, digest))
|
||||
} else {
|
||||
throw Error('Unknown type, must be binary type')
|
||||
/* c8 ignore next 1 */
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { coerce } from '../bytes.js'
|
||||
import * as Digest from './digest.js'
|
||||
|
||||
const code = 0x0
|
||||
const name = 'identity'
|
||||
|
||||
const encode: (input: Uint8Array) => Uint8Array = coerce
|
||||
|
||||
function digest (input: Uint8Array): Digest.Digest<typeof code, number> {
|
||||
return Digest.create(code, encode(input))
|
||||
}
|
||||
|
||||
export const identity = { code, name, encode, digest }
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// # Multihash
|
||||
|
||||
/**
|
||||
* Represents a multihash digest which carries information about the
|
||||
* hashing algorithm and an actual hash digest.
|
||||
*/
|
||||
// Note: In the current version there is no first class multihash
|
||||
// representation (plain Uint8Array is used instead) instead there seems to be
|
||||
// a bunch of places that parse it to extract (code, digest, size). By creating
|
||||
// this first class representation we avoid reparsing and things generally fit
|
||||
// really nicely.
|
||||
export interface MultihashDigest<Code extends number = number> {
|
||||
/**
|
||||
* Code of the multihash
|
||||
*/
|
||||
code: Code
|
||||
|
||||
/**
|
||||
* Raw digest (without a hashing algorithm info)
|
||||
*/
|
||||
digest: Uint8Array
|
||||
|
||||
/**
|
||||
* byte length of the `this.digest`
|
||||
*/
|
||||
size: number
|
||||
|
||||
/**
|
||||
* Binary representation of this multihash digest.
|
||||
*/
|
||||
bytes: Uint8Array
|
||||
}
|
||||
|
||||
/**
|
||||
* Hasher represents a hashing algorithm implementation that produces as
|
||||
* `MultihashDigest`.
|
||||
*/
|
||||
export interface MultihashHasher<Code extends number = number> {
|
||||
/**
|
||||
* Takes binary `input` and returns it (multi) hash digest. Return value is
|
||||
* either promise of a digest or a digest. This way general use can `await`
|
||||
* while performance critical code may asses return value to decide whether
|
||||
* await is needed.
|
||||
*/
|
||||
digest(input: Uint8Array): Promise<MultihashDigest<Code>> | MultihashDigest<Code>
|
||||
|
||||
/**
|
||||
* Name of the multihash
|
||||
*/
|
||||
name: string
|
||||
|
||||
/**
|
||||
* Code of the multihash
|
||||
*/
|
||||
code: Code
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync variant of `MultihashHasher` that refines return type of the `digest`
|
||||
* to `MultihashDigest`. It is subtype of `MultihashHasher` so implementations
|
||||
* of this interface can be passed anywhere `MultihashHasher` is expected,
|
||||
* allowing consumer to either `await` or check the return type to decide
|
||||
* whether to await or proceed with return value.
|
||||
*
|
||||
* `SyncMultihashHasher` is useful in certain APIs where async hashing would be
|
||||
* impractical e.g. implementation of Hash Array Mapped Trie (HAMT).
|
||||
*/
|
||||
export interface SyncMultihashHasher<Code extends number = number> extends MultihashHasher<Code> {
|
||||
digest(input: Uint8Array): MultihashDigest<Code>
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/* global crypto */
|
||||
|
||||
import { from } from './hasher.js'
|
||||
|
||||
const sha = (name: AlgorithmIdentifier) =>
|
||||
async (data: Uint8Array) => new Uint8Array(await crypto.subtle.digest(name, data))
|
||||
|
||||
export const sha1 = from({
|
||||
name: 'sha-1',
|
||||
code: 0x11,
|
||||
encode: sha('SHA-1')
|
||||
})
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import crypto from 'crypto'
|
||||
import { coerce } from '../bytes.js'
|
||||
import { from } from './hasher.js'
|
||||
|
||||
export const sha1 = from({
|
||||
name: 'sha-1',
|
||||
code: 0x11,
|
||||
encode: (input) => coerce(crypto.createHash('sha1').update(input).digest())
|
||||
})
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/* global crypto */
|
||||
|
||||
import { from } from './hasher.js'
|
||||
|
||||
function sha (name: AlgorithmIdentifier): (data: Uint8Array) => Promise<Uint8Array> {
|
||||
return async data => new Uint8Array(await crypto.subtle.digest(name, data))
|
||||
}
|
||||
|
||||
export const sha256 = from({
|
||||
name: 'sha2-256',
|
||||
code: 0x12,
|
||||
encode: sha('SHA-256')
|
||||
})
|
||||
|
||||
export const sha512 = from({
|
||||
name: 'sha2-512',
|
||||
code: 0x13,
|
||||
encode: sha('SHA-512')
|
||||
})
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import crypto from 'crypto'
|
||||
import { coerce } from '../bytes.js'
|
||||
import { from } from './hasher.js'
|
||||
|
||||
export const sha256 = from({
|
||||
name: 'sha2-256',
|
||||
code: 0x12,
|
||||
encode: (input) => coerce(crypto.createHash('sha256').update(input).digest())
|
||||
})
|
||||
|
||||
export const sha512 = from({
|
||||
name: 'sha2-512',
|
||||
code: 0x13,
|
||||
encode: input => coerce(crypto.createHash('sha512').update(input).digest())
|
||||
})
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* @packageDocumentation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ```TypeScript
|
||||
* import { CID } from 'multiformats/cid'
|
||||
* import * as json from 'multiformats/codecs/json'
|
||||
* import { sha256 } from 'multiformats/hashes/sha2'
|
||||
*
|
||||
* const bytes = json.encode({ hello: 'world' })
|
||||
*
|
||||
* const hash = await sha256.digest(bytes)
|
||||
* const cid = CID.create(1, json.code, hash)
|
||||
* //> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
|
||||
* ```
|
||||
*
|
||||
* ## Creating Blocks
|
||||
*
|
||||
* ```TypeScript
|
||||
* import * as Block from 'multiformats/block'
|
||||
* import * as codec from '@ipld/dag-cbor'
|
||||
* import { sha256 as hasher } from 'multiformats/hashes/sha2'
|
||||
*
|
||||
* const value = { hello: 'world' }
|
||||
*
|
||||
* // encode a block
|
||||
* let block = await Block.encode({ value, codec, hasher })
|
||||
*
|
||||
* block.value // { hello: 'world' }
|
||||
* block.bytes // Uint8Array
|
||||
* block.cid // CID() w/ sha2-256 hash address and dag-cbor codec
|
||||
*
|
||||
* // you can also decode blocks from their binary state
|
||||
* block = await Block.decode({ bytes: block.bytes, codec, hasher })
|
||||
*
|
||||
* // if you have the cid you can also verify the hash on decode
|
||||
* block = await Block.create({ bytes: block.bytes, cid: block.cid, codec, hasher })
|
||||
* ```
|
||||
*
|
||||
* ## 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:
|
||||
*
|
||||
* ```TypeScript
|
||||
* import { base64 } from "multiformats/bases/base64"
|
||||
* cid.toString(base64.encoder)
|
||||
* //> 'mAYAEEiCTojlxqRTl6svwqNJRVM2jCcPBxy+7mRTUfGDzy2gViA'
|
||||
* ```
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* ```TypeScript
|
||||
* CID.parse('mAYAEEiCTojlxqRTl6svwqNJRVM2jCcPBxy+7mRTUfGDzy2gViA', base64.decoder)
|
||||
* //> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
|
||||
* ```
|
||||
*
|
||||
* Dual of multibase encoder & decoder is defined as multibase codec and it exposes
|
||||
* 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:
|
||||
*
|
||||
* ```TypeScript
|
||||
* cid.toString(base64)
|
||||
* CID.parse(cid.toString(base64), base64)
|
||||
* ```
|
||||
*
|
||||
* **Note:** CID implementation comes bundled with `base32` and `base58btc`
|
||||
* multibase codecs so that CIDs can be base serialized to (version specific)
|
||||
* default base encoding and parsed without having to supply base encoders/decoders:
|
||||
*
|
||||
* ```TypeScript
|
||||
* const v1 = CID.parse('bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea')
|
||||
* v1.toString()
|
||||
* //> 'bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea'
|
||||
*
|
||||
* const v0 = CID.parse('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n')
|
||||
* v0.toString()
|
||||
* //> 'QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'
|
||||
* v0.toV1().toString()
|
||||
* //> 'bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku'
|
||||
* ```
|
||||
*
|
||||
* ## 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`.
|
||||
*
|
||||
* ```TypeScript
|
||||
* export const { name, code, encode, decode } = {
|
||||
* name: 'json',
|
||||
* code: 0x0200,
|
||||
* encode: json => new TextEncoder().encode(JSON.stringify(json)),
|
||||
* decode: bytes => JSON.parse(new TextDecoder().decode(bytes))
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ## 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:
|
||||
*
|
||||
* ```TypeScript
|
||||
* import * as hasher from 'multiformats/hashes/hasher'
|
||||
*
|
||||
* const sha256 = hasher.from({
|
||||
* // As per multiformats table
|
||||
* // https://github.com/multiformats/multicodec/blob/master/table.csv#L9
|
||||
* name: 'sha2-256',
|
||||
* code: 0x12,
|
||||
*
|
||||
* encode: (input) => new Uint8Array(crypto.createHash('sha256').update(input).digest())
|
||||
* })
|
||||
*
|
||||
* const hash = await sha256.digest(json.encode({ hello: 'world' }))
|
||||
* CID.create(1, json.code, hash)
|
||||
*
|
||||
* //> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
|
||||
* ```
|
||||
*
|
||||
* ## 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()`.
|
||||
*
|
||||
* ```TypeScript
|
||||
* import { walk } from 'multiformats/traversal'
|
||||
* import * as Block from 'multiformats/block'
|
||||
* import * as codec from 'multiformats/codecs/json'
|
||||
* import { sha256 as hasher } from 'multiformats/hashes/sha2'
|
||||
*
|
||||
* // build a DAG (a single block for this simple example)
|
||||
* const value = { hello: 'world' }
|
||||
* const block = await Block.encode({ value, codec, hasher })
|
||||
* const { cid } = block
|
||||
* console.log(cid)
|
||||
* //> CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)
|
||||
*
|
||||
* // create a loader function that also collects CIDs of blocks in
|
||||
* // their traversal order
|
||||
* const load = (cid, blocks) => async (cid) => {
|
||||
* // fetch a block using its cid
|
||||
* // e.g.: const block = await fetchBlockByCID(cid)
|
||||
* blocks.push(cid)
|
||||
* return block
|
||||
* }
|
||||
*
|
||||
* // collect blocks in this DAG starting from the root `cid`
|
||||
* const blocks = []
|
||||
* await walk({ cid, load: load(cid, blocks) })
|
||||
*
|
||||
* console.log(blocks)
|
||||
* //> [CID(bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea)]
|
||||
* ```
|
||||
*
|
||||
* ## Legacy interface
|
||||
*
|
||||
* [`blockcodec-to-ipld-format`](https://github.com/ipld/js-blockcodec-to-ipld-format) converts a multiformats [`BlockCodec`](https://github.com/multiformats/js-multiformats/blob/master/src/codecs/interface.ts#L21) into an
|
||||
* [`interface-ipld-format`](https://github.com/ipld/interface-ipld-format) for use with the [`ipld`](https://github.com/ipld/ipld) package. This can help bridge IPLD codecs implemented using the structure and interfaces defined here to existing code that assumes, or requires `interface-ipld-format`. This bridge also includes the relevant TypeScript definitions.
|
||||
*
|
||||
* ## Implementations
|
||||
*
|
||||
* By default, no base encodings (other than base32 & base58btc), hash functions,
|
||||
* or codec implementations are exposed by `multiformats`, you need to
|
||||
* 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) |
|
||||
*
|
||||
* 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) |
|
||||
*
|
||||
* ### 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) |
|
||||
*/
|
||||
|
||||
import * as bytes from './bytes.js'
|
||||
import { CID } from './cid.js'
|
||||
import * as digest from './hashes/digest.js'
|
||||
import * as hasher from './hashes/hasher.js'
|
||||
import * as varint from './varint.js'
|
||||
|
||||
// This way TS will also expose all the types from module
|
||||
export * from './interface.js'
|
||||
|
||||
export { CID, hasher, digest, varint, bytes }
|
||||
+5
@@ -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'
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { CID, format, toJSON, fromJSON } from './cid.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'
|
||||
|
||||
const DAG_PB_CODE = 0x70
|
||||
// eslint-disable-next-line
|
||||
const SHA_256_CODE = 0x12
|
||||
|
||||
/**
|
||||
* Simplified version of `create` for CIDv0.
|
||||
*/
|
||||
export function createLegacy (digest: API.MultihashDigest<typeof SHA_256_CODE>): API.LegacyLink {
|
||||
return CID.create(0, DAG_PB_CODE, digest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of `create` for CIDv1.
|
||||
*
|
||||
* @param code - Content encoding format code.
|
||||
* @param digest - Miltihash of the content.
|
||||
*/
|
||||
export function create <Data, Code extends number, Alg extends number> (code: Code, digest: API.MultihashDigest<Alg>): API.Link<Data, Code, Alg> {
|
||||
return CID.create(1, code, digest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Type predicate returns true if value is the link.
|
||||
*/
|
||||
export function isLink <L extends API.Link<unknown, number, number, 0 | 1>> (value: unknown | L): value is L & CID {
|
||||
if (value == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
const withSlash = value as { '/'?: Uint8Array, bytes: Uint8Array }
|
||||
|
||||
if (withSlash['/'] != null && withSlash['/'] === withSlash.bytes) {
|
||||
return true
|
||||
}
|
||||
|
||||
const withAsCID = value as { 'asCID'?: unknown }
|
||||
|
||||
if (withAsCID.asCID === value) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
export function parse <Prefix extends string, Data, Code extends number, Alg extends number, Ver extends API.Version> (source: API.ToString<API.Link<Data, Code, Alg, Ver>, Prefix>, base?: API.MultibaseDecoder<Prefix>): API.Link<Data, Code, Alg, Ver> {
|
||||
return CID.parse(source, base)
|
||||
}
|
||||
|
||||
export { format, toJSON, fromJSON }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function decode <Data, Code extends number, Alg extends number, Ver extends API.Version> (bytes: API.ByteView<API.Link<Data, Code, Alg, Ver>>): API.Link<Data, Code, Alg, Ver> {
|
||||
return CID.decode(bytes)
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
import type { MultibaseEncoder, MultibaseDecoder, Multibase } from '../bases/interface.js'
|
||||
import type { Phantom, ByteView } from '../block/interface.js'
|
||||
import type { MultihashDigest } from '../hashes/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<Data> {
|
||||
readonly version: V
|
||||
readonly code: Format
|
||||
readonly multihash: MultihashDigest<Alg>
|
||||
|
||||
readonly byteOffset: number
|
||||
readonly byteLength: number
|
||||
readonly bytes: ByteView<Link<Data, Format, Alg, V>>
|
||||
|
||||
equals(other: unknown): other is Link<Data, Format, Alg, Version>
|
||||
|
||||
toString<Prefix extends string>(base?: MultibaseEncoder<Prefix>): ToString<Link<Data, Format, Alg, Version>, Prefix>
|
||||
link(): Link<Data, Format, Alg, V>
|
||||
|
||||
toV1(): Link<Data, Format, Alg, 1>
|
||||
}
|
||||
|
||||
export interface LinkJSON<T extends UnknownLink = UnknownLink> {
|
||||
'/': ToString<T>
|
||||
}
|
||||
|
||||
export interface LegacyLink<T extends unknown = unknown> extends Link<T, DAG_PB, SHA_256, 0> {
|
||||
}
|
||||
|
||||
export type UnknownLink =
|
||||
| LegacyLink<unknown>
|
||||
| Link<unknown, number, number, Version>
|
||||
|
||||
export type ToString<T, Prefix extends string = string> = Multibase<Prefix> & Phantom<T>
|
||||
|
||||
export type { ByteView }
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { base58btc } from './bases/base58.js'
|
||||
import type { BlockView as _BlockView } from './block/interface.js'
|
||||
import type { CID, Version } from './cid.js'
|
||||
|
||||
type BlockView<T=unknown, C extends number = number, A extends number = number, V extends Version = Version> = _BlockView<T, C, A, V>
|
||||
|
||||
export async function walk ({ cid, load, seen }: { cid: CID, load(cid: CID): Promise<BlockView | null>, seen?: Set<string> }): Promise<void> {
|
||||
seen = seen ?? new Set()
|
||||
const b58Cid = cid.toString(base58btc)
|
||||
if (seen.has(b58Cid)) {
|
||||
return
|
||||
}
|
||||
|
||||
const block = await load(cid)
|
||||
seen.add(b58Cid)
|
||||
|
||||
if (block === null) { // the loader signals with `null` that we should skip this block
|
||||
return
|
||||
}
|
||||
|
||||
for (const [, cid] of block.links()) {
|
||||
await walk({ cid, load, seen })
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import varint from './vendor/varint.js'
|
||||
|
||||
export function decode (data: Uint8Array, offset = 0): [number, number] {
|
||||
const code = varint.decode(data, offset)
|
||||
return [code, varint.decode.bytes]
|
||||
}
|
||||
|
||||
export function encodeTo (int: number, target: Uint8Array, offset = 0): Uint8Array {
|
||||
varint.encode(int, target, offset)
|
||||
return target
|
||||
}
|
||||
|
||||
export function encodingLength (int: number): number {
|
||||
return varint.encodingLength(int)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/* eslint-disable */
|
||||
|
||||
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
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/* eslint-disable */
|
||||
// base-x encoding / decoding
|
||||
// Copyright (c) 2018 base-x contributors
|
||||
// Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp)
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
/**
|
||||
* @param {string} ALPHABET
|
||||
* @param {any} name
|
||||
*/
|
||||
function base (ALPHABET, name) {
|
||||
if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') }
|
||||
var BASE_MAP = new Uint8Array(256);
|
||||
for (var j = 0; j < BASE_MAP.length; j++) {
|
||||
BASE_MAP[j] = 255;
|
||||
}
|
||||
for (var i = 0; i < ALPHABET.length; i++) {
|
||||
var x = ALPHABET.charAt(i);
|
||||
var xc = x.charCodeAt(0);
|
||||
if (BASE_MAP[xc] !== 255) { throw new TypeError(x + ' is ambiguous') }
|
||||
BASE_MAP[xc] = i;
|
||||
}
|
||||
var BASE = ALPHABET.length;
|
||||
var LEADER = ALPHABET.charAt(0);
|
||||
var FACTOR = Math.log(BASE) / Math.log(256); // log(BASE) / log(256), rounded up
|
||||
var iFACTOR = Math.log(256) / Math.log(BASE); // log(256) / log(BASE), rounded up
|
||||
/**
|
||||
* @param {any[] | Iterable<number>} source
|
||||
*/
|
||||
function encode (source) {
|
||||
// @ts-ignore
|
||||
if (source instanceof Uint8Array) ; else if (ArrayBuffer.isView(source)) {
|
||||
source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
||||
} else if (Array.isArray(source)) {
|
||||
source = Uint8Array.from(source);
|
||||
}
|
||||
if (!(source instanceof Uint8Array)) { throw new TypeError('Expected Uint8Array') }
|
||||
if (source.length === 0) { return '' }
|
||||
// Skip & count leading zeroes.
|
||||
var zeroes = 0;
|
||||
var length = 0;
|
||||
var pbegin = 0;
|
||||
var pend = source.length;
|
||||
while (pbegin !== pend && source[pbegin] === 0) {
|
||||
pbegin++;
|
||||
zeroes++;
|
||||
}
|
||||
// Allocate enough space in big-endian base58 representation.
|
||||
var size = ((pend - pbegin) * iFACTOR + 1) >>> 0;
|
||||
var b58 = new Uint8Array(size);
|
||||
// Process the bytes.
|
||||
while (pbegin !== pend) {
|
||||
var carry = source[pbegin];
|
||||
// Apply "b58 = b58 * 256 + ch".
|
||||
var i = 0;
|
||||
for (var it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) {
|
||||
carry += (256 * b58[it1]) >>> 0;
|
||||
b58[it1] = (carry % BASE) >>> 0;
|
||||
carry = (carry / BASE) >>> 0;
|
||||
}
|
||||
if (carry !== 0) { throw new Error('Non-zero carry') }
|
||||
length = i;
|
||||
pbegin++;
|
||||
}
|
||||
// Skip leading zeroes in base58 result.
|
||||
var it2 = size - length;
|
||||
while (it2 !== size && b58[it2] === 0) {
|
||||
it2++;
|
||||
}
|
||||
// Translate the result into a string.
|
||||
var str = LEADER.repeat(zeroes);
|
||||
for (; it2 < size; ++it2) { str += ALPHABET.charAt(b58[it2]); }
|
||||
return str
|
||||
}
|
||||
/**
|
||||
* @param {string | string[]} source
|
||||
*/
|
||||
function decodeUnsafe (source) {
|
||||
if (typeof source !== 'string') { throw new TypeError('Expected String') }
|
||||
if (source.length === 0) { return new Uint8Array() }
|
||||
var psz = 0;
|
||||
// Skip leading spaces.
|
||||
if (source[psz] === ' ') { return }
|
||||
// Skip and count leading '1's.
|
||||
var zeroes = 0;
|
||||
var length = 0;
|
||||
while (source[psz] === LEADER) {
|
||||
zeroes++;
|
||||
psz++;
|
||||
}
|
||||
// Allocate enough space in big-endian base256 representation.
|
||||
var size = (((source.length - psz) * FACTOR) + 1) >>> 0; // log(58) / log(256), rounded up.
|
||||
var b256 = new Uint8Array(size);
|
||||
// Process the characters.
|
||||
while (source[psz]) {
|
||||
// Decode character
|
||||
var carry = BASE_MAP[source.charCodeAt(psz)];
|
||||
// Invalid character
|
||||
if (carry === 255) { return }
|
||||
var i = 0;
|
||||
for (var it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) {
|
||||
carry += (BASE * b256[it3]) >>> 0;
|
||||
b256[it3] = (carry % 256) >>> 0;
|
||||
carry = (carry / 256) >>> 0;
|
||||
}
|
||||
if (carry !== 0) { throw new Error('Non-zero carry') }
|
||||
length = i;
|
||||
psz++;
|
||||
}
|
||||
// Skip trailing spaces.
|
||||
if (source[psz] === ' ') { return }
|
||||
// Skip leading zeroes in b256.
|
||||
var it4 = size - length;
|
||||
while (it4 !== size && b256[it4] === 0) {
|
||||
it4++;
|
||||
}
|
||||
var vch = new Uint8Array(zeroes + (size - it4));
|
||||
var j = zeroes;
|
||||
while (it4 !== size) {
|
||||
vch[j++] = b256[it4++];
|
||||
}
|
||||
return vch
|
||||
}
|
||||
/**
|
||||
* @param {string | string[]} string
|
||||
*/
|
||||
function decode (string) {
|
||||
var buffer = decodeUnsafe(string);
|
||||
if (buffer) { return buffer }
|
||||
throw new Error(`Non-${name} character`)
|
||||
}
|
||||
return {
|
||||
encode: encode,
|
||||
decodeUnsafe: decodeUnsafe,
|
||||
decode: decode
|
||||
}
|
||||
}
|
||||
var src = base;
|
||||
|
||||
var _brrp__multiformats_scope_baseX = src;
|
||||
|
||||
export default _brrp__multiformats_scope_baseX;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/* eslint-disable */
|
||||
// Type definitions for varint 5.0
|
||||
// Project: https://github.com/chrisdickinson/varint#readme
|
||||
// Definitions by: David Brockman Smoliansky <https://github.com/dbrockman>
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
},
|
||||
|
||||
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
|
||||
},
|
||||
|
||||
/**
|
||||
* returns the number of bytes this number will be encoded as, up to a maximum of 8.
|
||||
*/
|
||||
encodingLength(num: number): number
|
||||
}
|
||||
|
||||
declare const varint:Varint
|
||||
export default varint
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/* eslint-disable */
|
||||
var encode_1 = encode;
|
||||
|
||||
var MSB = 0x80
|
||||
, REST = 0x7F
|
||||
, MSBALL = ~REST
|
||||
, INT = Math.pow(2, 31);
|
||||
|
||||
/**
|
||||
* @param {number} num
|
||||
* @param {number[]} out
|
||||
* @param {number} offset
|
||||
*/
|
||||
function encode(num, out, offset) {
|
||||
out = out || [];
|
||||
offset = offset || 0;
|
||||
var oldOffset = offset;
|
||||
|
||||
while(num >= INT) {
|
||||
out[offset++] = (num & 0xFF) | MSB;
|
||||
num /= 128;
|
||||
}
|
||||
while(num & MSBALL) {
|
||||
out[offset++] = (num & 0xFF) | MSB;
|
||||
num >>>= 7;
|
||||
}
|
||||
out[offset] = num | 0;
|
||||
|
||||
// @ts-ignore
|
||||
encode.bytes = offset - oldOffset + 1;
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
var decode = read;
|
||||
|
||||
var MSB$1 = 0x80
|
||||
, REST$1 = 0x7F;
|
||||
|
||||
/**
|
||||
* @param {string | any[]} buf
|
||||
* @param {number} offset
|
||||
*/
|
||||
function read(buf, offset) {
|
||||
var res = 0
|
||||
, offset = offset || 0
|
||||
, shift = 0
|
||||
, counter = offset
|
||||
, b
|
||||
, l = buf.length;
|
||||
|
||||
do {
|
||||
if (counter >= l) {
|
||||
// @ts-ignore
|
||||
read.bytes = 0;
|
||||
throw new RangeError('Could not decode varint')
|
||||
}
|
||||
b = buf[counter++];
|
||||
res += shift < 28
|
||||
? (b & REST$1) << shift
|
||||
: (b & REST$1) * Math.pow(2, shift);
|
||||
shift += 7;
|
||||
} while (b >= MSB$1)
|
||||
|
||||
// @ts-ignore
|
||||
read.bytes = counter - offset;
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
var N1 = Math.pow(2, 7);
|
||||
var N2 = Math.pow(2, 14);
|
||||
var N3 = Math.pow(2, 21);
|
||||
var N4 = Math.pow(2, 28);
|
||||
var N5 = Math.pow(2, 35);
|
||||
var N6 = Math.pow(2, 42);
|
||||
var N7 = Math.pow(2, 49);
|
||||
var N8 = Math.pow(2, 56);
|
||||
var N9 = Math.pow(2, 63);
|
||||
|
||||
var length = function (/** @type {number} */ value) {
|
||||
return (
|
||||
value < N1 ? 1
|
||||
: value < N2 ? 2
|
||||
: value < N3 ? 3
|
||||
: value < N4 ? 4
|
||||
: value < N5 ? 5
|
||||
: value < N6 ? 6
|
||||
: value < N7 ? 7
|
||||
: value < N8 ? 8
|
||||
: value < N9 ? 9
|
||||
: 10
|
||||
)
|
||||
};
|
||||
|
||||
var varint = {
|
||||
encode: encode_1
|
||||
, decode: decode
|
||||
, encodingLength: length
|
||||
};
|
||||
|
||||
var _brrp_varint = varint;
|
||||
|
||||
export default _brrp_varint;
|
||||
Reference in New Issue
Block a user