Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: BlockEncoder optional promise for encode/decode #305

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export async function encode <T, Code extends number, Alg extends number> ({ val
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 bytes = await Promise.resolve(codec.encode(value))
const hash = await hasher.digest(bytes)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const cid = CID.create(
Expand All @@ -168,7 +168,7 @@ export async function decode <T, Code extends number, Alg extends number> ({ byt
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 value = await Promise.resolve(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>
Expand All @@ -194,10 +194,10 @@ type CreateUnsafeInput <T, Code extends number, Alg extends number, V extends AP
* @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
export async function createUnsafe <T, Code extends number, Alg extends number, V extends API.Version> ({ bytes, cid, value: maybeValue, codec }: CreateUnsafeInput<T, Code, Alg, V>): Promise<API.BlockView<T, Code, Alg, V>> {
const value = await Promise.resolve(maybeValue !== undefined
? maybeValue
: (codec?.decode(bytes))
: (codec?.decode(bytes)))

if (value === undefined) throw new Error('Missing required argument, must either provide "value" or "codec"')

Expand All @@ -224,7 +224,7 @@ interface CreateInput <T, Code extends number, Alg extends number, V extends API
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 value = await Promise.resolve(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')
Expand Down
4 changes: 2 additions & 2 deletions src/codecs/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import type { ArrayBufferView, ByteView } from '../block/interface.js'
export interface BlockEncoder<Code extends number, T> {
name: string
code: Code
encode(data: T): ByteView<T>
encode(data: T): ByteView<T> | PromiseLike<ByteView<T>>
}

/**
* IPLD decoder part of the codec.
*/
export interface BlockDecoder<Code extends number, T> {
code: Code
decode(bytes: ByteView<T> | ArrayBufferView<T>): T
decode(bytes: ByteView<T> | ArrayBufferView<T>): T | PromiseLike<T>
}

/**
Expand Down
47 changes: 35 additions & 12 deletions test/test-block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ const fixture = { hello: 'world' }
const link = CID.parse('bafyreidykglsfhoixmivffc5uwhcgshx4j465xwqntbmu43nb2dzqwfvae')
const buff = bytes.fromString('sadf')

describe('promise-resolve-semantics', () => {
it('should resolve to the value', async () => {
const value = await Promise.resolve('hello')
assert.equal(value, 'hello')
})
it('should resolve to the promise value', async () => {
// eslint-disable-next-line promise/param-names
const value = await Promise.resolve(new Promise<string>(function (rs: (value: string) => void) {
setTimeout(function () { rs('hello') }, 10)
}))
assert.equal(value, 'hello')
})
})

describe('block', () => {
it('basic encode/decode roundtrip', async () => {
const block = await main.encode({ value: fixture, codec, hasher })
Expand All @@ -23,7 +37,7 @@ describe('block', () => {

it('createUnsafe', async () => {
const block = await main.encode({ value: fixture, codec, hasher })
const block2 = main.createUnsafe({ bytes: block.bytes, cid: block.cid, codec })
const block2 = await main.createUnsafe({ bytes: block.bytes, cid: block.cid, codec })
assert.deepStrictEqual(block.cid.equals(block2.cid), true)
})

Expand All @@ -36,25 +50,29 @@ describe('block', () => {
// @ts-expect-error - 'string' is not assignable to parameter of type 'ArrayLike<number>'
bytes: Uint8Array.from('1234')
}
// @ts-expect-error - 'boolean' is not assignable to type 'CID'
const block = main.createUnsafe({ value, codec, hasher, cid: true, bytes: true })

it('links', () => {
it('links', async () => {
const expected = ['link', 'arr/0']
// @ts-expect-error - 'boolean' is not assignable to type 'CID'
const block = await main.createUnsafe({ value, codec, hasher, cid: true, bytes: true })
for (const [path, cid] of block.links()) {
assert.deepStrictEqual(path, expected.shift())
assert.deepStrictEqual(cid.toString(), link.toString())
}
})

it('tree', () => {
it('tree', async () => {
const expected = ['link', 'nope', 'arr', 'arr/0', 'obj', 'obj/arr', 'obj/arr/0', 'obj/arr/0/obj', 'bytes']
// @ts-expect-error - 'boolean' is not assignable to type 'CID'
const block = await main.createUnsafe({ value, codec, hasher, cid: true, bytes: true })
for (const path of block.tree()) {
assert.deepStrictEqual(path, expected.shift())
}
})

it('get', () => {
it('get', async () => {
// @ts-expect-error - 'boolean' is not assignable to type 'CID'
const block = await main.createUnsafe({ value, codec, hasher, cid: true, bytes: true })
let ret = block.get('link/test')
assert.deepStrictEqual(ret.remaining, 'test')
assert.deepStrictEqual(String(ret.value), link.toString())
Expand All @@ -63,8 +81,8 @@ describe('block', () => {
assert.deepStrictEqual(ret, { value: 'skip' })
})

it('null links/tree', () => {
const block = main.createUnsafe({
it('null links/tree', async () => {
const block = await main.createUnsafe({
value: null,
codec,
hasher,
Expand Down Expand Up @@ -95,9 +113,9 @@ describe('block', () => {
assert.equal(links[0][1].toString(), link.toString())
})

it('kitchen sink', () => {
it('kitchen sink', async () => {
const sink = { one: { two: { arr: [true, false, null], three: 3, buff, link } } }
const block = main.createUnsafe({
const block = await main.createUnsafe({
value: sink,
codec,
// @ts-expect-error - 'boolean' is not assignable to type 'ByteView<unknown>'
Expand Down Expand Up @@ -132,8 +150,13 @@ describe('block', () => {
})

it('createUnsafe', async () => {
// @ts-expect-error testing invalid usage
assert.throws(() => main.createUnsafe({}), 'Missing required argument, must either provide "value" or "codec"')
try {
// @ts-expect-error testing invalid usage
await main.createUnsafe({})
assert(false, 'Missing required argument, must either provide "value" or "codec"')
} catch (/** @type {Error} */ err) {
/* c8 ignore next */
}
})

it('create', async () => {
Expand Down
6 changes: 4 additions & 2 deletions test/test-multibase-spec.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,11 @@ describe('spec test', () => {
}

for (const base of Object.values(bases)) {
it('should fail decode with invalid char', function () {
// eslint-disable-next-line @typescript-eslint/method-signature-style
it('should fail decode with invalid char', function (this: { skip: () => void }) {
if (base.name === 'identity') {
return this.skip()
this.skip()
return
}

assert.throws(() => base.decode(base.prefix + '^!@$%!#$%@#y'), `Non-${base.name} character`)
Expand Down
Loading