Ready NFT
CI / Foundry project (push) Waiting to run

This commit is contained in:
2024-08-18 17:10:03 +08:00
parent e7ed382823
commit 11e41de86a
209 changed files with 6325 additions and 120 deletions
+237
View File
@@ -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
View File
@@ -0,0 +1,7 @@
import { baseX } from './base.js'
export const base10 = baseX({
prefix: '9',
name: 'base10',
alphabet: '0123456789'
})
+15
View File
@@ -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
View File
@@ -0,0 +1,8 @@
import { rfc4648 } from './base.js'
export const base2 = rfc4648({
prefix: '0',
name: 'base2',
alphabet: '01',
bitsPerChar: 1
})
+31
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,8 @@
import { rfc4648 } from './base.js'
export const base8 = rfc4648({
prefix: '7',
name: 'base8',
alphabet: '01234567',
bitsPerChar: 3
})
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;