From 0883fb408a363a2b4f1f36e67b99cd797f0e8c93 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 28 Mar 2023 00:58:29 -0700 Subject: [PATCH 01/14] feat!: implement receipts --- packages/client/src/connection.js | 4 +- packages/client/test/api.ts | 6 +- packages/client/test/service.js | 5 +- packages/core/package.json | 12 + .../src/cbor/codec.js => core/src/cbor.js} | 4 +- packages/core/src/dag.js | 102 ++++++ packages/core/src/delegation.js | 11 + packages/core/src/invocation.js | 40 ++- packages/core/src/lib.js | 5 + packages/core/src/receipt.js | 157 +++++++++ packages/core/src/receipt/outcome.js | 128 ++++++++ packages/core/test/cbor.spec.js | 75 +++++ packages/core/test/delegation.spec.js | 19 ++ packages/core/test/invocation.spec.js | 9 +- packages/core/test/receipt.spec.js | 304 ++++++++++++++++++ packages/interface/src/lib.ts | 196 ++++++++++- packages/interface/src/query.ts | 223 ------------- packages/interface/src/transport.ts | 12 +- packages/server/src/server.js | 72 +++-- packages/server/test/server.spec.js | 82 +++-- packages/transport/src/car.js | 60 +--- packages/transport/src/car/codec.js | 8 +- packages/transport/src/car/request.js | 69 ++++ packages/transport/src/car/response.js | 67 ++++ packages/transport/src/cbor.js | 6 +- packages/transport/src/http.js | 4 +- packages/transport/test/car.spec.js | 102 +++++- 27 files changed, 1409 insertions(+), 373 deletions(-) rename packages/{transport/src/cbor/codec.js => core/src/cbor.js} (95%) create mode 100644 packages/core/src/dag.js create mode 100644 packages/core/src/receipt.js create mode 100644 packages/core/src/receipt/outcome.js create mode 100644 packages/core/test/cbor.spec.js create mode 100644 packages/core/test/receipt.spec.js delete mode 100644 packages/interface/src/query.ts create mode 100644 packages/transport/src/car/request.js create mode 100644 packages/transport/src/car/response.js diff --git a/packages/client/src/connection.js b/packages/client/src/connection.js index 1ecaf30a..1d4695b2 100644 --- a/packages/client/src/connection.js +++ b/packages/client/src/connection.js @@ -8,7 +8,7 @@ import { sha256 } from 'multiformats/hashes/sha2' * @param {API.ConnectionOptions} options * @returns {API.ConnectionView} */ -export const connect = (options) => new Connection(options) +export const connect = options => new Connection(options) /** * @template {Record} T @@ -42,7 +42,7 @@ class Connection { * @template {API.Tuple>} I * @param {API.Connection} connection * @param {I} invocations - * @returns {Promise>} + * @returns {Promise>} */ export const execute = async (invocations, connection) => { const request = await connection.encoder.encode(invocations, connection) diff --git a/packages/client/test/api.ts b/packages/client/test/api.ts index 12ec0ca5..4658fca7 100644 --- a/packages/client/test/api.ts +++ b/packages/client/test/api.ts @@ -133,16 +133,16 @@ export interface AccessProvider { * Associates a DID with another DID in the system. If there is no account * associated with a `to` DID will produce an error. */ - link(member: DID, group: DID, proof: Link): Result + link(member: DID, group: DID, proof: Link): Result<{}, UnknownDIDError> - unlink(member: DID, group: DID, proof: Link): Result + unlink(member: DID, group: DID, proof: Link): Result<{}, UnknownDIDError> /** * Associates new child DID with an accound of the parent DID. If there is no * account associated with a parent it creates account with `parent` did first * and then associates child DID with it. */ - register(member: DID, group: DID, proof: Link): Result + register(member: DID, group: DID, proof: Link): Result<{}, UnknownDIDError> /** * Resolves account DID associated with a given DID. Returns either account diff --git a/packages/client/test/service.js b/packages/client/test/service.js index cb687a47..a242b54b 100644 --- a/packages/client/test/service.js +++ b/packages/client/test/service.js @@ -114,7 +114,7 @@ class AccessService { * with: API.DID * }} Identify * @param {API.Invocation} ucan - * @returns {Promise>} + * @returns {Promise>} */ async identify(ucan) { const [capability] = ucan.capabilities @@ -133,9 +133,6 @@ class AccessService { /** @type {any} */ (ucan).link ) } - // } else { - // return access - // } } } diff --git a/packages/core/package.json b/packages/core/package.json index f5054fb6..fa934842 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -75,6 +75,18 @@ "./delegation": { "types": "./dist/src/delegation.d.ts", "import": "./src/delegation.js" + }, + "./receipt": { + "types": "./dist/src/receipt.d.ts", + "import": "./src/receipt.js" + }, + "./cbor": { + "types": "./dist/src/cbor.d.ts", + "import": "./src/cbor.js" + }, + "./dag": { + "types": "./dist/src/dag.d.ts", + "import": "./src/dag.js" } }, "c8": { diff --git a/packages/transport/src/cbor/codec.js b/packages/core/src/cbor.js similarity index 95% rename from packages/transport/src/cbor/codec.js rename to packages/core/src/cbor.js index 0f32838d..cf1c4754 100644 --- a/packages/transport/src/cbor/codec.js +++ b/packages/core/src/cbor.js @@ -1,8 +1,8 @@ import * as API from '@ucanto/interface' import * as CBOR from '@ipld/dag-cbor' -export { code, decode } from '@ipld/dag-cbor' +export { code, name, decode } from '@ipld/dag-cbor' import { sha256 } from 'multiformats/hashes/sha2' -import { createLink, isLink } from '@ucanto/core' +import { create as createLink, isLink } from 'multiformats/link' /** * @param {unknown} data diff --git a/packages/core/src/dag.js b/packages/core/src/dag.js new file mode 100644 index 00000000..c411acc8 --- /dev/null +++ b/packages/core/src/dag.js @@ -0,0 +1,102 @@ +import * as API from '@ucanto/interface' +import { create as createLink } from './link.js' +import { sha256 } from 'multiformats/hashes/sha2' +import * as MF from 'multiformats/interface' +import * as CBOR from './cbor.js' + +/** + * @param {unknown} value + * @returns {IterableIterator} + */ +export const iterate = function* (value) { + if ( + value && + typeof value === 'object' && + 'iterateIPLDBlocks' in value && + typeof value.iterateIPLDBlocks === 'function' + ) { + yield* value.iterateIPLDBlocks() + } +} + +/** + * @template T + * @typedef {Map, API.Block>} BlockStore + */ + +/** + * @template [T=unknown] + * @returns {BlockStore} + */ +export const createStore = () => new Map() + +/** + * @template T + * @template {T} U + * @param {U} source + * @param {BlockStore} store + * @param {object} options + * @param {MF.BlockEncoder} [options.codec] + * @param {MF.MultihashHasher} [options.hasher] + * @returns {Promise & { data: U }>} + */ +export const encodeInto = async ( + source, + store, + { codec = CBOR, hasher = sha256 } = {} +) => { + const bytes = codec.encode(source) + const digest = await hasher.digest(bytes) + /** @type {API.Link} */ + const link = createLink(codec.code, digest) + store.set(/** @type {API.ToString} */ (link.toString()), { + bytes, + cid: link, + }) + + return { bytes, cid: link, data: source } +} + +/** + * @template T + * @template {T} U + * @param {API.Block} block + * @param {BlockStore} store + * @returns {API.Block} + */ +export const addInto = ({ cid, bytes }, store) => { + store.set(/** @type {API.ToString} */ (cid.toString()), { + bytes, + cid, + }) + + return { bytes, cid } +} + +/** + * @template T + * @template {T} U + * @param {Iterable>} source + * @param {BlockStore} store + */ +export const addEveryInto = (source, store) => { + for (const block of source) { + addInto(block, store) + } +} + +/** + * @template T + * @param {API.Link} link + * @param {BlockStore} store + * @returns {API.Block & { data: T }} + */ +export const decodeFrom = (link, store) => { + const block = store.get(`${link}`) + /* c8 ignore next 3 */ + if (!block) { + throw new Error(`Block for the ${link} is not found`) + } + const data = /** @type {T} */ (CBOR.decode(block.bytes)) + return { cid: link, bytes: block.bytes, data } +} diff --git a/packages/core/src/delegation.js b/packages/core/src/delegation.js index 711793ab..07157092 100644 --- a/packages/core/src/delegation.js +++ b/packages/core/src/delegation.js @@ -177,6 +177,9 @@ export class Delegation { get cid() { return this.root.cid } + link() { + return this.root.cid + } get asCID() { return this.cid } @@ -192,6 +195,10 @@ export class Delegation { return exportDAG(this.root, this.blocks) } + iterateIPLDBlocks() { + return exportDAG(this.root, this.blocks) + } + /** * @type {API.Proof[]} */ @@ -262,6 +269,10 @@ export class Delegation { return this } + buildIPLDView() { + return this + } + /** * @returns {API.DelegationJSON} */ diff --git a/packages/core/src/invocation.js b/packages/core/src/invocation.js index 07ec3994..986ec8d1 100644 --- a/packages/core/src/invocation.js +++ b/packages/core/src/invocation.js @@ -1,5 +1,6 @@ import * as API from '@ucanto/interface' -import { delegate } from './delegation.js' +import { delegate, Delegation } from './delegation.js' +import * as DAG from './dag.js' /** * @template {API.Capability} Capability @@ -8,6 +9,30 @@ import { delegate } from './delegation.js' */ export const invoke = options => new IssuedInvocation(options) +/** + * @template {API.Capability} C + * @param {object} dag + * @param {API.UCANLink<[C]>} dag.root + * @param {Map} dag.blocks + * @returns {API.Invocation} + */ +export const view = ({ root, blocks }) => { + const { bytes, cid } = DAG.decodeFrom(root, blocks) + return new Invocation({ bytes, cid }, blocks) +} + +/** + * @template {API.Invocation} Invocation + * @param {object} dag + * @param {ReturnType} dag.root + * @param {Map} dag.blocks + * @returns {Invocation|ReturnType} + */ +export const embed = ({ root, blocks }) => + blocks.has(root.toString()) + ? /** @type {Invocation} */ (view({ root, blocks })) + : root + /** * @template {API.Capability} Capability * @implements {API.IssuedInvocationView} @@ -52,10 +77,14 @@ class IssuedInvocation { return delegate(this) } + buildIPLDView() { + return delegate(this) + } + /** * @template {API.InvocationService} Service * @param {API.ConnectionView} connection - * @returns {Promise>} + * @returns {Promise>} */ async execute(connection) { /** @type {API.ServiceInvocation} */ @@ -67,3 +96,10 @@ class IssuedInvocation { return result } } + +/** + * @template {API.Capability} Capability + * @implements {API.Invocation} + * @extends {Delegation<[Capability]>} + */ +export class Invocation extends Delegation {} diff --git a/packages/core/src/lib.js b/packages/core/src/lib.js index 96492458..5fe2492f 100644 --- a/packages/core/src/lib.js +++ b/packages/core/src/lib.js @@ -1,4 +1,9 @@ +export * as API from '@ucanto/interface' export * as Delegation from './delegation.js' +export * as Invocation from './invocation.js' +export * as Receipt from './receipt.js' +import * as DAG from './dag.js' +import * as CBOR from './cbor.js' export { delegate, isDelegation } from './delegation.js' export { invoke } from './invocation.js' export { diff --git a/packages/core/src/receipt.js b/packages/core/src/receipt.js new file mode 100644 index 00000000..1a48f53c --- /dev/null +++ b/packages/core/src/receipt.js @@ -0,0 +1,157 @@ +import * as API from '@ucanto/interface' +import * as Outcome from './receipt/outcome.js' +import * as DID from '@ipld/dag-ucan/did' +import * as Signature from '@ipld/dag-ucan/signature' +import * as DAG from './dag.js' + +export { Outcome } + +/** + * @param {object} input + * @param {API.Link} input.root + * @param {Map} input.blocks + */ +export const view = ({ root, blocks }) => { + const block = DAG.decodeFrom(root, blocks) + const outcome = Outcome.view({ root: block.data.ocm, blocks }) + + return new Receipt({ root: block, store: blocks, outcome }) +} + +/** + * @template {{}} Ok + * @template {{}} Error + * @template {API.Invocation} Ran + * @template {API.SigAlg} [SigAlg=API.SigAlg] + * @implements {API.Receipt} + */ +class Receipt { + /** + * @param {object} input + * @param {Required>>} input.root + * @param {API.Outcome} input.outcome + * @param {Map} input.store + * @param {API.Signature>, SigAlg>} [input.signature] + */ + constructor({ root, store, outcome, signature }) { + this.store = store + + this.root = root + this.outcome = outcome + this._signature = signature + } + + get issuer() { + return this.outcome.issuer + } + + get ran() { + return this.outcome.ran + } + get proofs() { + return this.outcome.proofs + } + + buildIPLDView() { + return this + } + /** + * @returns {IterableIterator} + */ + *iterateIPLDBlocks() { + yield* DAG.iterate(this.outcome) + + yield this.root + } + + get out() { + return this.outcome.out + } + + get fx() { + return this.outcome.fx + } + + get meta() { + return this.outcome.meta + } + + get signature() { + const signature = this._signature + if (signature) { + return signature + } else { + const signature = + /** @type {API.Signature>, SigAlg>} */ ( + Signature.view(this.root.data.sig) + ) + this._signature = signature + return signature + } + } +} + +const NOFX = Object.freeze({ fork: Object.freeze([]) }) + +/** + * @template {{}} Ok + * @template {{}} Error + * @template {API.Invocation} Ran + * @template {API.SigAlg} SigAlg + * @param {object} options + * @param {API.Signer} options.issuer + * @param {Ran|ReturnType} options.ran + * @param {API.ReceiptResult} options.result + * @param {API.EffectsModel} [options.fx] + * @param {API.Proof[]} [options.proofs] + * @param {Record} [options.meta] + * @returns {Promise>} + */ +export const issue = async ({ + issuer, + result, + ran, + proofs = [], + meta = {}, + fx = NOFX, +}) => { + const store = DAG.createStore() + + // copy invocation blocks int + DAG.addEveryInto(DAG.iterate(ran), store) + + // copy proof blocks into store + for (const proof of proofs) { + DAG.addEveryInto(DAG.iterate(proof), store) + } + + const { cid } = await DAG.encodeInto( + { + ran: /** @type {ReturnType} */ (ran.link()), + out: result, + fx, + meta, + iss: issuer.did(), + prf: proofs.map(p => p.link()), + }, + store + ) + + const outcome = Outcome.view({ root: cid, blocks: store }) + /** @type {API.Signature>, SigAlg>} */ + const signature = await issuer.sign(outcome.root.cid.bytes) + + /** @type {API.ReceiptModel} */ + const model = { + ocm: outcome.root.cid, + sig: signature, + } + const root = await DAG.encodeInto(model, store) + + return new Receipt({ + root, + outcome, + store, + signature, + }) +} diff --git a/packages/core/src/receipt/outcome.js b/packages/core/src/receipt/outcome.js new file mode 100644 index 00000000..ea1bd196 --- /dev/null +++ b/packages/core/src/receipt/outcome.js @@ -0,0 +1,128 @@ +import * as API from '@ucanto/interface' +import * as Invocation from '../invocation.js' +import { Delegation } from '../lib.js' +import * as DID from '@ipld/dag-ucan/did' +import * as DAG from '../dag.js' + +/** + * @template {{}} Ok + * @template {{}} Error + * @template {API.Invocation} Ran + * @param {object} source + * @param {API.Link>} source.root + * @param {Map} source.blocks + * @returns {API.Outcome} + */ +export const view = ({ root, blocks }) => { + return new Outcome({ + root: DAG.decodeFrom(root, blocks), + store: blocks, + }) +} + +/** + * @template {{}} Ok + * @template {{}} Error + * @template {API.Invocation} Ran + * @implements {API.Outcome} + * @implements {API.IPLDView>} + */ +export class Outcome { + /** + * + * @param {object} source + * @param {Required>>} source.root + * @param {Map} source.store + * + */ + constructor({ root, store }) { + this.root = root + this.store = store + } + get model() { + return this.root.data + } + + link() { + return this.root.cid + } + + buildIPLDView() { + return this + } + + *iterateIPLDBlocks() { + yield* DAG.iterate(this.ran) + + const { fork, join } = this.fx + for (const concurrent of fork) { + yield* DAG.iterate(concurrent) + } + + if (join) { + yield* DAG.iterate(join) + } + + for (const proof of this.proofs) { + yield* DAG.iterate(proof) + } + + yield this.root + } + /** + * @returns {Ran|ReturnType} + */ + get ran() { + const ran = this._ran + if (!ran) { + const ran = Invocation.embed({ root: this.model.ran, blocks: this.store }) + this._ran = ran + return ran + } else { + return ran + } + } + get proofs() { + const proofs = this._proofs + if (proofs) { + return proofs + } else { + const { store: blocks, model } = this + const proofs = [] + if (model.prf) { + for (const link of model.prf) { + const root = blocks.get(link.toString()) + if (root) { + proofs.push(Delegation.create({ root, blocks: blocks })) + } else { + proofs.push(link) + } + } + } + + this._proofs = proofs + return proofs + } + } + get meta() { + return this.model.meta + } + get issuer() { + const issuer = this._issuer + if (issuer) { + return issuer + } else if (this.model.iss) { + const issuer = DID.parse(this.model.iss) + this._issuer = issuer + return issuer + } + } + + get out() { + return this.model.out + } + + get fx() { + return this.model.fx + } +} diff --git a/packages/core/test/cbor.spec.js b/packages/core/test/cbor.spec.js new file mode 100644 index 00000000..c30e2a8e --- /dev/null +++ b/packages/core/test/cbor.spec.js @@ -0,0 +1,75 @@ +import { test, assert } from './test.js' +import * as CBOR from '../src/cbor.js' +import { decode, encode } from '@ipld/dag-cbor' + +test('encode / decode', async () => { + const bytes = CBOR.encode([{ ok: true, value: 1 }]) + + assert.deepEqual(bytes, encode([{ ok: true, value: 1 }])) + + assert.deepEqual(await CBOR.decode(bytes), [{ ok: true, value: 1 }]) +}) + +{ + const { encode, decode, write } = CBOR + + /** + * @template T + * @param {T} value + */ + const transcode = value => decode(encode(value)) + + const dataset = [ + undefined, + null, + Symbol('hello'), + [1, , 3], + { x: 1, y: undefined }, + { x: 3, p: Symbol('hi') }, + { + x: 1, + y: 2, + toJSON() { + return [1, 2] + }, + }, + ] + + for (const data of dataset) { + test(`encode / decode ${JSON.stringify(data)}`, async () => { + const actual = transcode(data) + const expect = JSON.parse(JSON.stringify(data) || 'null') + assert.deepEqual(actual, expect) + }) + } + + test(`encode / decode bytes`, async () => { + const UTF8 = new TextEncoder() + const actual = transcode({ bytes: UTF8.encode('hello') }) + assert.deepEqual(actual, { bytes: UTF8.encode('hello') }) + }) + + test('circular objects throw', () => { + const circular = { a: 1, circle: {} } + circular.circle = circular + + const nested = { pointer: {} } + const structure = { + x: 1, + sub: { + items: [1, nested], + }, + } + nested.pointer = structure + + assert.throws(() => transcode(nested), /Can not encode circular structure/) + }) + + test('cids', async () => { + const hello = await write({ hello: 'world' }) + + assert.deepEqual(transcode({ message: hello.cid }), { + message: hello.cid, + }) + }) +} diff --git a/packages/core/test/delegation.spec.js b/packages/core/test/delegation.spec.js index 52be65d4..d53eb8d2 100644 --- a/packages/core/test/delegation.spec.js +++ b/packages/core/test/delegation.spec.js @@ -268,3 +268,22 @@ test('.delegate() return same value', async () => { assert.equal(ucan.delegate(), ucan) }) + +test('.buildIPLDView() return same value', async () => { + const ucan = await delegate({ + issuer: alice, + audience: w3, + capabilities: [ + { + with: alice.did(), + can: 'test/echo', + nb: { + message: 'data:1', + }, + }, + ], + expiration: Infinity, + }) + + assert.equal(ucan.buildIPLDView(), ucan) +}) diff --git a/packages/core/test/invocation.spec.js b/packages/core/test/invocation.spec.js index 2bb49eed..b2b37cfb 100644 --- a/packages/core/test/invocation.spec.js +++ b/packages/core/test/invocation.spec.js @@ -1,4 +1,4 @@ -import { invoke, UCAN } from '../src/lib.js' +import { invoke, UCAN, Invocation } from '../src/lib.js' import { alice, service as w3 } from './fixtures.js' import { assert, test } from './test.js' @@ -44,7 +44,7 @@ test('expired invocation', async () => { expiration, }) - assert.deepNestedInclude(await invocation.delegate(), { + assert.deepNestedInclude(await invocation.buildIPLDView(), { capabilities: [ { can: 'store/add', @@ -67,7 +67,7 @@ test('invocation with notBefore', async () => { notBefore, }) - assert.deepNestedInclude(await invocation.delegate(), { + assert.deepNestedInclude(await invocation.buildIPLDView(), { capabilities: [ { can: 'store/add', @@ -89,7 +89,7 @@ test('invocation with nonce', async () => { nonce: 'hello', }) - assert.deepNestedInclude(await invocation.delegate(), { + assert.deepNestedInclude(await invocation.buildIPLDView(), { capabilities: [ { can: 'store/add', @@ -154,5 +154,6 @@ test('execute invocation', async () => { }, }) + // @ts-expect-error assert.deepEqual(result, { hello: 'world' }) }) diff --git a/packages/core/test/receipt.spec.js b/packages/core/test/receipt.spec.js new file mode 100644 index 00000000..e476f43b --- /dev/null +++ b/packages/core/test/receipt.spec.js @@ -0,0 +1,304 @@ +import { Receipt, invoke, API, delegate } from '../src/lib.js' +import { alice, bob, service as w3 } from './fixtures.js' +import { assert, test } from './test.js' +import * as CBOR from '../src/cbor.js' + +test('basic receipt', async () => { + const invocation = await invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'test/echo', + with: alice.did(), + }, + }).delegate() + + const receipt = await Receipt.issue({ + issuer: w3, + result: { ok: { hello: 'message' } }, + ran: invocation, + }) + + await assertReceipt(receipt, { + out: { ok: { hello: 'message' } }, + meta: {}, + fx: { fork: [] }, + ran: invocation, + issuer: w3, + verifier: w3, + proofs: [], + }) + + await assertRoundtrip(receipt) + + assert.equal(receipt.buildIPLDView().buildIPLDView(), receipt) + assert.equal(receipt.outcome.buildIPLDView(), receipt.outcome) +}) + +test('receipt with ran as link', async () => { + const invocation = await invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'test/echo', + with: alice.did(), + }, + }).delegate() + + const receipt = await Receipt.issue({ + issuer: w3, + result: { ok: { hello: 'message' } }, + ran: invocation.link(), + }) + + await assertReceipt(receipt, { + out: { ok: { hello: 'message' } }, + meta: {}, + fx: { fork: [] }, + ran: invocation.link(), + issuer: w3, + verifier: w3, + proofs: [], + }) + + await assertRoundtrip(receipt) +}) + +test('receipt with proofs', async () => { + const invocation = await invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'test/echo', + with: alice.did(), + }, + }).delegate() + + const proof = await delegate({ + issuer: w3, + audience: bob, + capabilities: [ + { + with: w3.did(), + can: '*', + }, + ], + }) + + const { cid: proofCid } = await await delegate({ + issuer: w3, + audience: bob, + capabilities: [ + { + with: w3.did(), + can: '*', + }, + ], + nonce: 'second one', + }) + + const receipt = await Receipt.issue({ + issuer: bob, + result: { ok: { hello: 'message' } }, + ran: invocation, + proofs: [proof, proofCid], + }) + + await assertReceipt(receipt, { + out: { ok: { hello: 'message' } }, + meta: {}, + fx: { fork: [] }, + ran: invocation, + issuer: bob, + verifier: bob, + proofs: [proof, proofCid], + }) + + await assertRoundtrip(receipt) +}) + +test('receipt with meta', async () => { + const invocation = await invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'test/echo', + with: alice.did(), + }, + }).delegate() + + const receipt = await Receipt.issue({ + issuer: w3, + result: { ok: { hello: 'message' } }, + ran: invocation, + meta: { test: 'metadata' }, + }) + + await assertReceipt(receipt, { + out: { ok: { hello: 'message' } }, + meta: { test: 'metadata' }, + fx: { fork: [] }, + ran: invocation, + issuer: w3, + verifier: w3, + proofs: [], + }) + + await assertRoundtrip(receipt) +}) + +test('receipt with fx.fork', async () => { + const invocation = await invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'test/echo', + with: alice.did(), + }, + }).delegate() + + const echo = await CBOR.write( + /** @type {API.InstructionModel} */ ({ + op: 'debug/echo', + rsc: alice.did(), + input: {}, + nnc: '', + }) + ) + + const receipt = await Receipt.issue({ + issuer: w3, + result: { ok: { hello: 'message' } }, + ran: invocation, + meta: { test: 'metadata' }, + fx: { + fork: [echo.cid], + }, + }) + + await assertReceipt(receipt, { + out: { ok: { hello: 'message' } }, + meta: { test: 'metadata' }, + fx: { + fork: [echo.cid], + }, + ran: invocation, + issuer: w3, + verifier: w3, + proofs: [], + }) + + await assertRoundtrip(receipt) +}) + +test('receipt with fx.join', async () => { + const invocation = await invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'test/echo', + with: alice.did(), + }, + }).delegate() + + const echo = await CBOR.write( + /** @type {API.InstructionModel} */ ({ + op: 'debug/echo', + rsc: alice.did(), + input: {}, + nnc: '', + }) + ) + + const receipt = await Receipt.issue({ + issuer: w3, + result: { ok: { hello: 'message' } }, + ran: invocation, + meta: { test: 'metadata' }, + fx: { + fork: [], + join: echo.cid, + }, + }) + + await assertReceipt(receipt, { + out: { ok: { hello: 'message' } }, + meta: { test: 'metadata' }, + fx: { + fork: [], + join: echo.cid, + }, + ran: invocation, + issuer: w3, + verifier: w3, + proofs: [], + }) + + await assertRoundtrip(receipt) +}) + +/** + * @template {API.Receipt} Receipt + * @param {Receipt} receipt + * @param {Partial & { verifier?: API.Verifier }} expect + */ +const assertReceipt = async (receipt, expect) => { + if (expect.out) { + assert.deepEqual(receipt.out, expect.out, 'out is correct') + } + + if (expect.meta) { + assert.deepEqual(receipt.meta, expect.meta, 'meta is correct') + } + + if (expect.fx) { + assert.deepEqual(receipt.fx, expect.fx, 'fx is correct') + } + + if (expect.issuer) { + assert.deepEqual( + receipt.issuer?.did(), + expect.issuer.did(), + 'issuer is correct' + ) + } + + if (expect.ran) { + assert.deepEqual(receipt.ran, expect.ran, 'ran field is correct') + } + + if (expect.verifier) { + assert.deepEqual( + await expect.verifier.verify( + receipt.outcome.link().bytes, + receipt.signature + ), + true, + 'signature is valid' + ) + } + + if (expect.proofs) { + assert.deepEqual(receipt.proofs, expect.proofs, 'proofs are correct') + } +} + +/** + * @template {API.Receipt} Receipt + * @param {Receipt} receipt + */ +const assertRoundtrip = async receipt => { + const blocks = new Map() + for await (const block of receipt.iterateIPLDBlocks()) { + blocks.set(block.cid.toString(), block) + } + + const view = Receipt.view({ root: receipt.root.cid, blocks }) + assert.deepEqual(view.out, receipt.out) + assert.deepEqual(view.meta, receipt.meta) + assert.deepEqual(view.fx, receipt.fx) + assert.deepEqual(view.ran, receipt.ran) + assert.deepEqual(view.issuer, receipt.issuer) + assert.deepEqual(view.proofs, receipt.proofs) + assert.deepEqual(view.signature, receipt.signature) +} diff --git a/packages/interface/src/lib.ts b/packages/interface/src/lib.ts index 46bcb6f2..24479484 100644 --- a/packages/interface/src/lib.ts +++ b/packages/interface/src/lib.ts @@ -130,11 +130,35 @@ export interface DelegationOptions extends UCANOptions { proofs?: Proof[] } +/** + * An interface for representing an IPLD DAG View that can be materialized into + * on demand. It is a useful abstraction that can be used to defer encoding of + * IPLD blocks. + */ +export interface IPLDViewBuilder { + /** + * Encodes all the blocks and creates a new IPLDView instance over them. Can + * be passed an multihasher to parameterize hashing algorithm. + * + * Please note that some `IPLDView`s also implement `IPLDViewBuilder` + * interface and they will discard any options. + */ + buildIPLDView(options?: Transport.EncodeOptions): Await> +} + +export interface IPLDView + extends IPLDViewBuilder { + buildIPLDView(): IPLDView + root: Block + iterateIPLDBlocks(): IterableIterator +} + /** * A materialized view of a UCAN delegation, which can be encoded into a UCAN token and * used as proof for an invocation or further delegations. */ -export interface Delegation { +export interface Delegation + extends IPLDView> { readonly root: UCANBlock /** * Map of all the IPLD blocks that were included with this delegation DAG. @@ -147,6 +171,7 @@ export interface Delegation { * Also note that map may contain blocks that are not part of this * delegation DAG. That is because `Delegation` is usually constructed as * view / selection over the CAR which may contain bunch of other blocks. + * @deprecated */ readonly blocks: Map @@ -155,6 +180,7 @@ export interface Delegation { readonly data: UCAN.View asCID: UCANLink + link(): UCANLink export(): IterableIterator @@ -318,6 +344,136 @@ export type LinkJSON = ToJSON< export interface Invocation extends Delegation<[C]> {} +export interface OutcomeModel< + Ok extends {} = {}, + Error extends {} = {}, + Ran extends Invocation = Invocation +> { + ran: ReturnType + out: ReceiptResult + fx: EffectsModel + meta: Meta + iss?: DID + prf: UCANLink[] +} + +export interface Outcome< + Ok extends {} = {}, + Error extends {} = {}, + Ran extends Invocation = Invocation +> extends IPLDView> { + link(): Link> + ran: Ran | ReturnType + out: ReceiptResult + fx: Effects + meta: Meta + issuer?: Principal + proofs: Proof[] +} + +/** + * A receipt is an attestation of the Result and requested Effects by a task + * invocation. It is issued and signed by the task executor or its delegate. + * + * @see https://github.com/ucan-wg/invocation#8-receipt + */ +export interface ReceiptModel< + Ok extends {} = {}, + Error extends {} = {}, + Ran extends Invocation = Invocation +> { + ocm: Link> + sig: Signature +} + +export interface Receipt< + Ok extends {} = {}, + Error extends {} = {}, + Ran extends Invocation = Invocation, + Alg extends SigAlg = SigAlg +> extends IPLDView> { + outcome: Outcome + ran: Ran | ReturnType + out: ReceiptResult + fx: Effects + meta: Meta + + issuer?: Principal + proofs: Proof[] + + signature: Signature>, Alg> +} + +export interface Meta extends Record {} + +export interface EffectsModel { + fork: readonly Link[] + join?: Link +} + +export interface Effects extends EffectsModel {} +export interface InstructionModel< + Op extends Ability = Ability, + URI extends Resource = Resource, + Input extends Record = Record +> { + op: Op + rsc: URI + input: Input + nnc: string +} + +/** + * Defines result type as per invocation spec + * + * @see https://github.com/ucan-wg/invocation/#6-result + */ + +export type ReceiptResult = Variant<{ + ok: T + error: X +}> + +/** + * Utility type for defining a [keyed union] type as in IPLD Schema. In practice + * this just works around typescript limitation that requires discriminant field + * on all variants. + * + * ```ts + * type Result = + * | { ok: T } + * | { error: X } + * + * const demo = (result: Result) => { + * if (result.ok) { + * // ^^^^^^^^^ Property 'ok' does not exist on type '{ error: Error; }` + * } + * } + * ``` + * + * Using `Variant` type we can define same union type that works as expected: + * + * ```ts + * type Result = Variant<{ + * ok: T + * error: X + * }> + * + * const demo = (result: Result) => { + * if (result.ok) { + * result.ok.toUpperCase() + * } + * } + * ``` + * + * [keyed union]:https://ipld.io/docs/schemas/features/representation-strategies/#union-keyed-representation + */ +export type Variant> = { + [Key in keyof U]: { [K in Exclude]?: never } & { + [K in Key]: U[Key] + } +}[keyof U] + /** * A {@link UCANOptions} instance that includes options specific to {@link Invocation}s. */ @@ -330,7 +486,8 @@ export interface InvocationOptions capability: C } -export interface IssuedInvocation { +export interface IssuedInvocation + extends IPLDViewBuilder> { readonly issuer: Principal readonly audience: Principal readonly capabilities: [C] @@ -429,6 +586,24 @@ export type InferServiceInvocationReturn< > : never +export type InferServiceInvocationReceipt< + C extends Capability, + S extends Record +> = ResolveServiceMethod extends ServiceMethod< + infer _, + infer T, + infer X +> + ? Receipt< + T & {}, + | X + | HandlerNotFound + | HandlerExecutionError + | InvalidAudience + | Unauthorized + > + : never + export type InferServiceInvocations< I extends unknown[], T extends Record @@ -438,12 +613,21 @@ export type InferServiceInvocations< ? [InferServiceInvocationReturn, ...InferServiceInvocations] : never +export type InferWorkflowReceipts< + I extends unknown[], + T extends Record +> = I extends [] + ? [] + : I extends [ServiceInvocation, ...infer Rest] + ? [InferServiceInvocationReceipt, ...InferWorkflowReceipts] + : never + export interface IssuedInvocationView extends IssuedInvocation { - delegate(): Promise> + delegate(): Await> execute>( service: ConnectionView - ): Await> + ): Await> } export type ServiceInvocations = IssuedInvocation & @@ -531,7 +715,7 @@ export interface ConnectionView> I extends Transport.Tuple> >( ...invocations: I - ): Await> + ): Await> } export interface InboundTransportOptions { @@ -569,7 +753,7 @@ export interface ServerOptions * Service DID which will be used to verify that received invocation * audience matches it. */ - readonly id: Verifier + readonly id: Signer } /** diff --git a/packages/interface/src/query.ts b/packages/interface/src/query.ts deleted file mode 100644 index 5f2b5ba5..00000000 --- a/packages/interface/src/query.ts +++ /dev/null @@ -1,223 +0,0 @@ -import type { - InvocationOptions, - IssuedInvocation, - IssuedInvocationView, - Invocation, - Proof, - UCAN, - Result, - Connection, - ConnectionView, - Service, - Principal, - Signer, - Failure, -} from './lib.js' - -export type QueryInput = { - [K in string]: Select | QueryInput -} - -export interface Input - extends Record { - with: Resource - - proofs?: Proof[] -} - -export type InstructionHandler< - Ability extends UCAN.Ability = UCAN.Ability, - In extends Input = Input, - T = unknown, - X extends Failure = Failure -> = (instruction: Invocation) => Result - -interface Select { - input: In - selector: S -} - -export type Selector = - | true - | { - [K in string]: Selector - } - -export interface Query { - input: In - - queryService(): QueryService - - execute>( - service: Connection - ): ExecuteQuery -} - -type ExecuteQuery> = { - [Key in keyof In & keyof Service & string]: ExecuteSubQuery< - Key, - In[Key], - Service[Key] - > -} - -type ExecuteSubQuery = { - [Key in keyof In & keyof Service & string]: In[Key] extends Select< - infer Input, - infer Selector - > - ? Service[Key] extends InstructionHandler< - `${Path}/${Key}`, - Input, - infer T, - infer X - > - ? ExecuteSelect - : never - : ExecuteSubQuery<`${Path}/${Key}`, In[Key], Service[Key]> -} - -type ExecuteSelect = S extends true - ? Result - : Result, X> - -type ExecuteSubSelect = T extends infer E | infer U - ? Pick | Pick - : Pick - -type QueryService = { - [Key in keyof In & string]: SubService -} - -type SubService = { - [Key in keyof In & string]: In[Key] extends Select - ? InstructionHandler<`${Path}/${Key}`, In, Matches> - : SubService<`${Path}/${Key}`, In[Key]> -} - -export type Matches = S extends true - ? unknown - : { - [Key in keyof S]: S[Key] extends Selector ? Matches : unknown - } - -export declare function invoke( - input: InvocationOptions -): IssuedInvocationView - -export declare function connection< - T extends Record ->(): Connection - -export declare function query(query: In): Query -export declare function select( - input: In, - selector?: S -): S extends true ? Select : Select - -type Match = { - [Key in keyof T]: T[Key] extends (input: In) => infer Out ? Out : never -}[keyof T] - -type StoreAdd = ( - input: Invocation<{ can: 'store/add'; with: UCAN.DID; link: UCAN.Link }> -) => Result< - | { status: 'done'; with: UCAN.DID; link: UCAN.Link } - | { status: 'pending'; with: UCAN.DID; link: UCAN.Link; url: string }, - Failure -> - -type StoreRemove = ( - input: Invocation<{ can: 'store/remove'; with: UCAN.DID; link: UCAN.Link }> -) => Result - -type Store = { - add: StoreAdd - remove: StoreRemove -} -declare var store: Store -declare var channel: ConnectionView<{ store: Store }> -declare const alice: Signer -declare const bob: Principal -declare const car: UCAN.Link - -type ToPath = T extends `${infer Base}/${infer Path}` - ? [Base, ...ToPath] - : [T] - -type A = ToPath<''> -type B = ToPath<'foo'> -type C = ToPath<'foo/bar'> - -type Unpack = T extends infer A & infer B ? [A, B] : [] - -type U = Unpack - -// store({ -// issuer: alice, -// audence: bob.did(), -// capabilities: [ -// { -// with: alice.did(), -// can: 'store/add', -// link: car, -// }, -// ], -// }) - -declare var host: ConnectionView<{ store: Store }> - -const demo = async () => { - const add = invoke({ - issuer: alice, - audience: bob, - capability: { - can: 'store/add', - with: alice.did(), - link: car, - }, - }) - - const remove = invoke({ - issuer: alice, - audience: bob, - capability: { - can: 'store/remove', - with: alice.did(), - link: car, - }, - }) - - const a = add.execute(channel) - const b = remove.execute(channel) - - const result = await add.execute(channel) - if (!result.error) { - result - } - - const q = query({ - store: { - add: select( - { - with: alice.did(), - link: car, - proofs: [], - }, - { - link: true, - } - ), - remove: select({ - with: alice.did(), - link: car, - }), - }, - }) - - const r3 = q.execute(host) - if (!r3.store.add.error) { - if (r3.store.add) { - } - } -} diff --git a/packages/interface/src/transport.ts b/packages/interface/src/transport.ts index 6a63b5b9..392d755c 100644 --- a/packages/interface/src/transport.ts +++ b/packages/interface/src/transport.ts @@ -7,8 +7,9 @@ import type { Phantom, Await } from '@ipld/dag-ucan' import * as UCAN from '@ipld/dag-ucan' import type { ServiceInvocation, - InferServiceInvocations, + InferWorkflowReceipts, InferInvocations, + Receipt, } from './lib.js' /** @@ -25,7 +26,7 @@ export interface EncodeOptions { export interface Channel> extends Phantom { request>>( request: HTTPRequest - ): Await>> + ): Await>> } export interface RequestEncoder { @@ -42,11 +43,14 @@ export interface RequestDecoder { } export interface ResponseEncoder { - encode(result: I, options?: EncodeOptions): Await> + encode>( + result: I, + options?: EncodeOptions + ): Await> } export interface ResponseDecoder { - decode(response: HTTPResponse): Await + decode>(response: HTTPResponse): Await } export interface HTTPRequest extends Phantom { diff --git a/packages/server/src/server.js b/packages/server/src/server.js index b215b9c1..06913eb4 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -7,6 +7,7 @@ export { Failure, MalformedCapability, } from '@ucanto/validator' +import { Receipt } from '@ucanto/core' /** * Creates a connection to a service. @@ -61,11 +62,11 @@ class Server { * @template {API.Tuple>} I * @param {API.ServerView} server * @param {API.HTTPRequest} request - * @returns {Promise>>} + * @returns {Promise>>} */ export const handle = async (server, request) => { - const invocations = await server.decoder.decode(request) - const result = await execute(invocations, server) + const workflow = await server.decoder.decode(request) + const result = await execute(workflow, server) return server.encoder.encode(result) } @@ -73,21 +74,22 @@ export const handle = async (server, request) => { * @template {Record} Service * @template {API.Capability} C * @template {API.Tuple>} I - * @param {API.InferInvocations} invocations + * @param {API.InferInvocations} workflow * @param {API.ServerView} server - * @returns {Promise>} + * @returns {Promise & API.Tuple>} */ -export const execute = async (invocations, server) => { - const results = [] +export const execute = async (workflow, server) => { const input = /** @type {API.InferInvocation>[]} */ ( - invocations + workflow ) - for (const invocation of input) { - results.push(await invoke(invocation, server)) - } - return /** @type {API.InferServiceInvocations} */ (results) + const promises = input.map(invocation => invoke(invocation, server)) + const results = await Promise.all(promises) + + return /** @type {API.InferWorkflowReceipts & API.Tuple} */ ( + results + ) } /** @@ -95,14 +97,18 @@ export const execute = async (invocations, server) => { * @template {API.Capability} C * @param {API.InferInvocation>} invocation * @param {API.ServerView} server - * @returns {Promise>} + * @returns {Promise} */ export const invoke = async (invocation, server) => { // Invocation needs to have one single capability if (invocation.capabilities.length !== 1) { - return /** @type {API.Result} */ ( - new InvocationCapabilityError(invocation.capabilities) - ) + return await Receipt.issue({ + issuer: server.id, + ran: invocation, + result: { + error: new InvocationCapabilityError(invocation.capabilities), + }, + }) } const [capability] = invocation.capabilities @@ -111,21 +117,37 @@ export const invoke = async (invocation, server) => { const method = /** @type {string} */ (path.pop()) const handler = resolve(server.service, path) if (handler == null || typeof handler[method] !== 'function') { - return /** @type {API.Result} */ ( - new HandlerNotFound(capability) - ) + return await Receipt.issue({ + issuer: server.id, + ran: invocation, + result: { + /** @type {API.HandlerNotFound} */ + error: new HandlerNotFound(capability), + }, + }) } else { try { - return await handler[method](invocation, server.context) - } catch (error) { - const err = new HandlerExecutionError( + const value = await handler[method](invocation, server.context) + return await Receipt.issue({ + issuer: server.id, + ran: invocation, + result: /** @type {API.ReceiptResult<{}>} */ ( + value?.error ? { error: value } : { ok: value || {} } + ), + }) + } catch (cause) { + const error = new HandlerExecutionError( capability, - /** @type {Error} */ (error) + /** @type {Error} */ (cause) ) - server.catch(err) + server.catch(error) - return /** @type {API.Result} */ (err) + return await Receipt.issue({ + issuer: server.id, + ran: invocation, + result: { error }, + }) } } } diff --git a/packages/server/test/server.spec.js b/packages/server/test/server.spec.js index cf518dac..c6da2812 100644 --- a/packages/server/test/server.spec.js +++ b/packages/server/test/server.spec.js @@ -67,15 +67,15 @@ test('encode delegated invocation', async () => { const server = Server.create({ service: Service.create(), - decoder: CAR, - encoder: CBOR, + decoder: CAR.request, + encoder: CAR.response, id: w3, }) const connection = Client.connect({ id: w3, - encoder: CAR, - decoder: CBOR, + encoder: CAR.request, + decoder: CAR.response, channel: server, }) @@ -115,23 +115,30 @@ test('encode delegated invocation', async () => { }, }) - const result = await Client.execute([add, remove], connection) + { + const result = await Client.execute([add, remove], connection) - assert.deepEqual(result, [ - { - error: true, + assert.equal(result.length, 2) + const [r1, r2] = result - name: 'UnknownDIDError', - did: alice.did(), - message: `DID ${alice.did()} has no account`, - }, - { - error: true, - name: 'UnknownDIDError', - did: alice.did(), - message: `DID ${alice.did()} has no account`, - }, - ]) + assert.deepEqual(r1.out, { + error: { + error: true, + name: 'UnknownDIDError', + did: alice.did(), + message: `DID ${alice.did()} has no account`, + }, + }) + + assert.deepEqual(r2.out, { + error: { + error: true, + name: 'UnknownDIDError', + did: alice.did(), + message: `DID ${alice.did()} has no account`, + }, + }) + } const identify = Client.invoke({ issuer: alice, @@ -144,25 +151,32 @@ test('encode delegated invocation', async () => { const register = await identify.execute(connection) - assert.deepEqual(register, null) + assert.deepEqual(register.out, { ok: {} }) - const result2 = await Client.execute([add, remove], connection) + { + const receipts = await Client.execute([add, remove], connection) + assert.deepEqual(receipts.length, 2) + const [r1, r2] = receipts - assert.deepEqual(result2, [ - { - status: 'upload', - with: alice.did(), - link: car.cid, - url: 'http://localhost:9090/', - }, - { - can: 'store/remove', - with: alice.did(), - nb: { + assert.deepEqual(r1.out, { + ok: { + status: 'upload', + with: alice.did(), link: car.cid, + url: 'http://localhost:9090/', }, - }, - ]) + }) + + assert.deepEqual(r2.out, { + ok: { + can: 'store/remove', + with: alice.did(), + nb: { + link: car.cid, + }, + }, + }) + } }) test('unknown handler', async () => { diff --git a/packages/transport/src/car.js b/packages/transport/src/car.js index 24baa9ea..b693f8cf 100644 --- a/packages/transport/src/car.js +++ b/packages/transport/src/car.js @@ -1,67 +1,25 @@ import * as API from '@ucanto/interface' import * as CAR from './car/codec.js' -import { Delegation } from '@ucanto/core' +import * as request from './car/request.js' +import * as response from './car/response.js' -export { CAR as codec } +export { CAR as codec, request, response } const HEADERS = Object.freeze({ 'content-type': 'application/car', }) /** - * Encodes invocation batch into an HTTPRequest. - * + * @deprecated * @template {API.Tuple} I * @param {I} invocations - * @param {API.EncodeOptions} [options] + * @param {API.EncodeOptions & { headers?: Record }} [options] * @returns {Promise>} */ -export const encode = async (invocations, options) => { - const roots = [] - const blocks = new Map() - for (const invocation of invocations) { - const delegation = await invocation.delegate() - roots.push(delegation.root) - for (const block of delegation.export()) { - blocks.set(block.cid.toString(), block) - } - blocks.delete(delegation.root.cid.toString()) - } - const body = CAR.encode({ roots, blocks }) - - return { - headers: HEADERS, - body, - } -} +export const encode = (invocations, options) => + request.encode(invocations, { headers: HEADERS, ...options }) /** - * Decodes HTTPRequest to an invocation batch. - * - * @template {API.Tuple} Invocations - * @param {API.HTTPRequest} request - * @returns {Promise>} + * @deprecated */ -export const decode = async ({ headers, body }) => { - const contentType = headers['content-type'] || headers['Content-Type'] - if (contentType !== 'application/car') { - throw TypeError( - `Only 'content-type: application/car' is supported, instead got '${contentType}'` - ) - } - - const { roots, blocks } = CAR.decode(body) - - const invocations = [] - - for (const root of /** @type {API.UCANBlock[]} */ (roots)) { - invocations.push( - Delegation.create({ - root, - blocks: /** @type {Map} */ (blocks), - }) - ) - } - - return /** @type {API.InferInvocations} */ (invocations) -} +export const decode = request.decode diff --git a/packages/transport/src/car/codec.js b/packages/transport/src/car/codec.js index 77c33f27..5ccbefd4 100644 --- a/packages/transport/src/car/codec.js +++ b/packages/transport/src/car/codec.js @@ -1,5 +1,5 @@ import * as API from '@ucanto/interface' -import { CarBufferReader } from '@ipld/car/buffer-reader' +import { CarBufferReader } from '@ipld/car/buffer-reader' import * as CarBufferWriter from '@ipld/car/buffer-writer' import { base32 } from 'multiformats/bases/base32' import { UCAN, createLink } from '@ucanto/core' @@ -107,9 +107,9 @@ export const decode = bytes => { } for (const block of reader.blocks()) { - if (!roots.includes(block)) { - blocks.set(block.cid.toString(), block) - } + // if (!roots.includes(block)) { + blocks.set(block.cid.toString(), block) + // } } return { roots, blocks } diff --git a/packages/transport/src/car/request.js b/packages/transport/src/car/request.js new file mode 100644 index 00000000..67e33981 --- /dev/null +++ b/packages/transport/src/car/request.js @@ -0,0 +1,69 @@ +import * as API from '@ucanto/interface' +import * as CAR from './codec.js' +import { Delegation } from '@ucanto/core' + +export { CAR as codec } + +const HEADERS = Object.freeze({ + 'content-type': 'application/car', + // We will signal that we want to receive a CAR file in the response + accept: 'application/car', +}) + +/** + * Encodes invocation batch into an HTTPRequest. + * + * @template {API.Tuple} I + * @param {I} invocations + * @param {API.EncodeOptions & { headers?: Record }} [options] + * @returns {Promise>} + */ +export const encode = async (invocations, options) => { + const roots = [] + const blocks = new Map() + for (const invocation of invocations) { + const delegation = await invocation.delegate() + roots.push(delegation.root) + for (const block of delegation.export()) { + blocks.set(block.cid.toString(), block) + } + blocks.delete(delegation.root.cid.toString()) + } + const body = CAR.encode({ roots, blocks }) + + return { + headers: options?.headers || HEADERS, + body, + } +} + +/** + * Decodes HTTPRequest to an invocation batch. + * + * @template {API.Tuple} Invocations + * @param {API.HTTPRequest} request + * @returns {Promise>} + */ +export const decode = async ({ headers, body }) => { + const contentType = headers['content-type'] || headers['Content-Type'] + if (contentType !== 'application/car') { + throw TypeError( + `Only 'content-type: application/car' is supported, instead got '${contentType}'` + ) + } + + const { roots, blocks } = CAR.decode(body) + + const invocations = [] + + for (const root of /** @type {API.UCANBlock[]} */ (roots)) { + invocations.push( + Delegation.create({ + root, + blocks: /** @type {Map} */ (blocks), + }) + ) + } + + return /** @type {API.InferInvocations} */ (invocations) +} diff --git a/packages/transport/src/car/response.js b/packages/transport/src/car/response.js new file mode 100644 index 00000000..3e0ecfd3 --- /dev/null +++ b/packages/transport/src/car/response.js @@ -0,0 +1,67 @@ +import * as API from '@ucanto/interface' +import * as CAR from './codec.js' +import { Receipt } from '@ucanto/core' + +export { CAR as codec } + +const HEADERS = Object.freeze({ + 'content-type': 'application/car', +}) + +/** + * Encodes invocation batch into an HTTPRequest. + * + * @template {API.Tuple} I + * @param {I} receipts + * @param {API.EncodeOptions} [options] + * @returns {Promise>} + */ +export const encode = async (receipts, options) => { + const roots = [] + const blocks = new Map() + for (const receipt of receipts) { + const reader = await receipt.buildIPLDView() + roots.push(reader.root) + for (const block of reader.iterateIPLDBlocks()) { + blocks.set(block.cid.toString(), block) + } + // blocks.delete(reader.root.cid.toString()) + } + const body = CAR.encode({ roots, blocks }) + + return { + headers: HEADERS, + body, + } +} + +/** + * Decodes HTTPRequest to an invocation batch. + * + * @template {API.Tuple} I + * @param {API.HTTPRequest} request + * @returns {I} + */ +export const decode = ({ headers, body }) => { + const contentType = headers['content-type'] || headers['Content-Type'] + if (contentType !== 'application/car') { + throw TypeError( + `Only 'content-type: application/car' is supported, instead got '${contentType}'` + ) + } + + const { roots, blocks } = CAR.decode(body) + + const receipts = /** @type {API.Receipt[]} */ ([]) + + for (const root of /** @type {API.UCANBlock[]} */ (roots)) { + receipts.push( + Receipt.view({ + root: root.cid, + blocks: /** @type {Map} */ (blocks), + }) + ) + } + + return /** @type {I} */ (receipts) +} diff --git a/packages/transport/src/cbor.js b/packages/transport/src/cbor.js index 1dd29015..a11d6147 100644 --- a/packages/transport/src/cbor.js +++ b/packages/transport/src/cbor.js @@ -1,5 +1,5 @@ import * as API from '@ucanto/interface' -import * as CBOR from './cbor/codec.js' +import * as CBOR from '@ucanto/core/cbor' const HEADERS = Object.freeze({ 'content-type': 'application/cbor', @@ -14,7 +14,7 @@ export const codec = CBOR * @param {I} result * @returns {API.HTTPResponse} */ -export const encode = (result) => { +export const encode = result => { return { headers: HEADERS, body: CBOR.encode(result), @@ -32,7 +32,7 @@ export const decode = async ({ headers, body }) => { const contentType = headers['content-type'] || headers['Content-Type'] if (contentType !== 'application/cbor') { throw TypeError( - `Only 'content-type: application/cbor' is supported, intsead got '${contentType}'` + `Only 'content-type: application/cbor' is supported, instead got '${contentType}'` ) } diff --git a/packages/transport/src/http.js b/packages/transport/src/http.js index 19c29190..97db4834 100644 --- a/packages/transport/src/http.js +++ b/packages/transport/src/http.js @@ -64,8 +64,8 @@ class Channel { return { headers: response.headers.entries ? Object.fromEntries(response.headers.entries()) - /* c8 ignore next */ - : {}, + : /* c8 ignore next */ + {}, body: new Uint8Array(buffer), } } diff --git a/packages/transport/test/car.spec.js b/packages/transport/test/car.spec.js index 14e195d4..ab882054 100644 --- a/packages/transport/test/car.spec.js +++ b/packages/transport/test/car.spec.js @@ -4,15 +4,14 @@ import * as CBOR from '../src/cbor.js' import { delegate, invoke, + Receipt, Delegation, UCAN, parseLink, isLink, } from '@ucanto/core' -import * as UTF8 from '../src/utf8.js' import { alice, bob, mallory, service } from './fixtures.js' import { CarReader } from '@ipld/car/reader' -import * as API from '@ucanto/interface' import { collect } from './util.js' test('encode / decode', async () => { @@ -61,7 +60,7 @@ test('encode / decode', async () => { assert.deepEqual([expect], await CAR.decode(request), 'roundtrips') }) -test('decode requires application/car contet type', async () => { +test('decode requires application/car content type', async () => { const { body } = await CAR.encode([ invoke({ issuer: alice, @@ -237,7 +236,7 @@ test('codec', async () => { roots: [root], }) const { blocks, roots } = await CAR.codec.decode(bytes) - assert.equal(blocks.size, 0) + assert.equal(blocks.size, 1) assert.deepEqual(roots, [root]) const car = await CAR.codec.write({ roots: [root] }) @@ -258,3 +257,98 @@ test('car writer', async () => { assert.deepEqual(car.roots, []) assert.deepEqual([...car.blocks], [[hello.cid.toString(), hello]]) }) + +test('CAR.request encode / decode', async () => { + const cid = parseLink( + 'bafyreiaxnmoptsqiehdff2blpptvdbenxcz6xgrbojw5em36xovn2xea4y' + ) + const expiration = 1654298135 + + const request = await CAR.request.encode([ + invoke({ + issuer: alice, + audience: bob, + capability: { + can: 'store/add', + with: alice.did(), + }, + expiration, + proofs: [], + }), + ]) + + assert.deepEqual(request.headers, { + 'content-type': 'application/car', + accept: 'application/car', + }) + const reader = await CarReader.fromBytes(request.body) + + assert.deepEqual( + await reader.getRoots(), + // @ts-expect-error - CAR refers to old CID + [cid] + ) + + const expect = await Delegation.delegate({ + issuer: alice, + audience: bob, + capabilities: [ + { + can: 'store/add', + with: alice.did(), + }, + ], + expiration, + proofs: [], + }) + + assert.deepEqual([expect], await CAR.request.decode(request), 'roundtrips') +}) + +test('CAR.response encode/decode', async () => { + const ran = await invoke({ + issuer: bob, + audience: service, + capability: { + can: 'test/hello', + with: alice.did(), + }, + }).delegate() + + const receipt = await Receipt.issue({ + issuer: alice, + result: { + ok: { hello: 'world' }, + }, + ran, + meta: { test: 'run' }, + }) + + const message = await CAR.response.encode([receipt]) + assert.deepEqual(message.headers, { + 'content-type': 'application/car', + }) + + const reader = await CarReader.fromBytes(message.body) + assert.deepEqual( + await reader.getRoots(), + // @ts-expect-error - CAR refers to old CID + [receipt.root.cid] + ) + + const [received, ...other] = await CAR.response.decode(message) + assert.equal(other.length, 0) + assert.deepEqual(received.issuer, receipt.issuer) + assert.deepEqual(received.meta, receipt.meta) + assert.deepEqual(received.ran, receipt.ran) + assert.deepEqual(received.proofs, receipt.proofs) + assert.deepEqual(received.fx, receipt.fx) + assert.deepEqual(received.signature, receipt.signature) + + assert.throws(() => { + CAR.response.decode({ + headers: {}, + body: message.body, + }) + }) +}) From cd26404d036935057719f2adb225cce68026c3bb Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 02:08:00 -0700 Subject: [PATCH 02/14] implement content negotiation --- packages/client/src/connection.js | 39 +++++- packages/interface/src/lib.ts | 38 ++++-- packages/interface/src/transport.ts | 9 +- packages/server/src/lib.js | 13 +- packages/server/src/server.js | 41 +++--- packages/server/test/handler.spec.js | 68 ++++----- packages/server/test/server.spec.js | 146 ++++++++++++++------ packages/transport/src/car.js | 21 +++ packages/transport/src/codec.js | 182 +++++++++++++++++++++++++ packages/transport/src/legacy.js | 43 ++++++ packages/transport/src/lib.js | 2 + packages/transport/test/legacy.spec.js | 80 +++++++++++ 12 files changed, 560 insertions(+), 122 deletions(-) create mode 100644 packages/transport/src/codec.js create mode 100644 packages/transport/src/legacy.js create mode 100644 packages/transport/test/legacy.spec.js diff --git a/packages/client/src/connection.js b/packages/client/src/connection.js index 1d4695b2..54c33c3e 100644 --- a/packages/client/src/connection.js +++ b/packages/client/src/connection.js @@ -1,5 +1,7 @@ import * as API from '@ucanto/interface' import { sha256 } from 'multiformats/hashes/sha2' +import { parseLink, Receipt } from '@ucanto/core' +import * as Signature from '@ipld/dag-ucan/signature' /** * Creates a connection to a service. @@ -21,8 +23,7 @@ class Connection { constructor(options) { this.id = options.id this.options = options - this.encoder = options.encoder - this.decoder = options.decoder + this.codec = options.codec this.channel = options.channel this.hasher = options.hasher || sha256 } @@ -41,12 +42,36 @@ class Connection { * @template {Record} T * @template {API.Tuple>} I * @param {API.Connection} connection - * @param {I} invocations + * @param {I} workflow * @returns {Promise>} */ -export const execute = async (invocations, connection) => { - const request = await connection.encoder.encode(invocations, connection) +export const execute = async (workflow, connection) => { + const request = await connection.codec.encode(workflow, connection) const response = await connection.channel.request(request) - const result = await connection.decoder.decode(response) - return result + try { + return await connection.codec.decode(response) + } catch (error) { + const { message, ...cause } = /** @type {Error} */ (error) + const receipts = [] + for await (const invocation of workflow) { + const { cid } = await invocation.delegate() + const receipt = await Receipt.issue({ + ran: cid, + result: { error: { ...cause, message } }, + // @ts-expect-error + issuer: { + did() { + return connection.id.did() + }, + sign(payload) { + return Signature.createNonStandard('', new Uint8Array()) + }, + }, + }) + + receipts.push(receipt) + } + + return /** @type {any} */ (receipts) + } } diff --git a/packages/interface/src/lib.ts b/packages/interface/src/lib.ts index 24479484..ea7ae271 100644 --- a/packages/interface/src/lib.ts +++ b/packages/interface/src/lib.ts @@ -683,17 +683,20 @@ export interface HandlerExecutionError extends Failure { export type API = T[keyof T] -export interface OutboundTransportOptions { - readonly encoder: Transport.RequestEncoder - readonly decoder: Transport.ResponseDecoder -} +export interface OutboundCodec + extends Transport.RequestEncoder, + Transport.ResponseDecoder {} + +/** @deprecated */ +export interface OutboundTransportOptions extends OutboundCodec {} + export interface ConnectionOptions> - extends Transport.EncodeOptions, - OutboundTransportOptions { + extends Transport.EncodeOptions { /** * DID of the target service. */ readonly id: Principal + readonly codec: OutboundCodec readonly channel: Transport.Channel } @@ -709,7 +712,7 @@ export interface Connection> export interface ConnectionView> extends Connection { - id: Principal + id: Signer execute< C extends Capability, I extends Transport.Tuple> @@ -718,7 +721,7 @@ export interface ConnectionView> ): Await> } -export interface InboundTransportOptions { +export interface InboundAcceptCodec { /** * Request decoder which is will be used by a server to decode HTTP Request * into an invocation `Batch` that will be executed using a `service`. @@ -732,6 +735,19 @@ export interface InboundTransportOptions { readonly encoder: Transport.ResponseEncoder } +export interface InboundCodec { + accept( + request: Transport.HTTPRequest + ): ReceiptResult +} + +export interface HTTPError { + readonly status: number + readonly statusText?: string + readonly headers?: Record + readonly message?: string +} + /** * Options for UCAN validation. */ @@ -746,14 +762,14 @@ export interface ValidatorOptions { readonly resolve?: InvocationContext['resolve'] } -export interface ServerOptions - extends InboundTransportOptions, - ValidatorOptions { +export interface ServerOptions extends ValidatorOptions { /** * Service DID which will be used to verify that received invocation * audience matches it. */ readonly id: Signer + + readonly codec: InboundCodec } /** diff --git a/packages/interface/src/transport.ts b/packages/interface/src/transport.ts index 392d755c..3ff4a68b 100644 --- a/packages/interface/src/transport.ts +++ b/packages/interface/src/transport.ts @@ -23,16 +23,20 @@ export interface EncodeOptions { readonly hasher?: UCAN.MultihashHasher } +export interface RequestEncodeOptions extends EncodeOptions { + accept?: string +} + export interface Channel> extends Phantom { request>>( request: HTTPRequest - ): Await>> + ): Await & Tuple>> } export interface RequestEncoder { encode>( invocations: I, - options?: EncodeOptions + options?: RequestEncodeOptions ): Await> } @@ -60,6 +64,7 @@ export interface HTTPRequest extends Phantom { } export interface HTTPResponse extends Phantom { + status?: number headers: Readonly> body: Uint8Array } diff --git a/packages/server/src/lib.js b/packages/server/src/lib.js index f8130188..1a4b21a8 100644 --- a/packages/server/src/lib.js +++ b/packages/server/src/lib.js @@ -1,7 +1,14 @@ +export * from './api.js' export * from './server.js' +export { + Failure, + MalformedCapability, + HandlerNotFound, + Link, + URI, +} from './server.js' export * from '@ucanto/core' -export { invoke } from '@ucanto/core' -// @ts-ignore -export * from './api.js' +export { invoke, Invocation, Receipt, Delegation, DID } from '@ucanto/core' + export * from './handler.js' export * as API from './api.js' diff --git a/packages/server/src/server.js b/packages/server/src/server.js index 06913eb4..2e5787b9 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -26,19 +26,11 @@ class Server { /** * @param {API.Server} options */ - constructor({ - id, - service, - encoder, - decoder, - principal = Verifier, - ...rest - }) { + constructor({ id, service, codec, principal = Verifier, ...rest }) { const { catch: fail, ...context } = rest this.context = { id, principal, ...context } this.service = service - this.encoder = encoder - this.decoder = decoder + this.codec = codec this.catch = fail || (() => {}) } get id() { @@ -46,28 +38,35 @@ class Server { } /** - * @template {API.Capability} C - * @template {API.Tuple>} I - * @param {API.HTTPRequest} request - * @returns {API.Await>>} + * @type {API.Channel['request']} */ request(request) { - return handle(/** @type {API.ServerView} */ (this), request) + return handle(this, request) } } /** * @template {Record} T - * @template {API.Capability} C - * @template {API.Tuple>} I + * @template {API.Tuple>} I * @param {API.ServerView} server * @param {API.HTTPRequest} request - * @returns {Promise>>} */ export const handle = async (server, request) => { - const workflow = await server.decoder.decode(request) - const result = await execute(workflow, server) - return server.encoder.encode(result) + const selection = server.codec.accept(request) + if (selection.error) { + const { status, headers = {}, message } = selection.error + return { + status, + headers, + body: new TextEncoder().encode(message), + } + } else { + const { encoder, decoder } = selection.ok + const workflow = await decoder.decode(request) + const result = await execute(workflow, server) + const response = await encoder.encode(result) + return response + } } /** diff --git a/packages/server/test/handler.spec.js b/packages/server/test/handler.spec.js index 6c1ab532..5e0e22fe 100644 --- a/packages/server/test/handler.spec.js +++ b/packages/server/test/handler.spec.js @@ -107,14 +107,12 @@ test('checks service id', async () => { const server = Server.create({ id: w3, service: { identity: Access }, - decoder: CAR, - encoder: CBOR, + codec: CAR.inbound, }) const client = Client.connect({ id: w3, - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, channel: server, }) @@ -137,19 +135,22 @@ test('checks service id', async () => { proofs: [proof], }) - const result = await invocation.execute(client) + const receipt = await invocation.execute(client) - assert.deepNestedInclude(result, { - error: true, - name: 'InvalidAudience', + assert.containSubset(receipt, { + out: { + error: { + name: 'InvalidAudience', + }, + }, }) assert.equal( - result?.message.includes(w3.did()), + receipt.out.error?.message.includes(w3.did()), true, 'mentions expected audience' ) assert.equal( - result?.message.includes(mallory.did()), + receipt.out.error?.message.includes(mallory.did()), true, 'mentions actual audience' ) @@ -163,12 +164,14 @@ test('checks service id', async () => { proofs: [proof], }) - const result = await invocation.execute(client) + const receipt = await invocation.execute(client) - assert.equal(result?.error, true) + assert.equal(receipt.out.error != null, true) assert.ok( - result?.message.includes(`can not be (self) issued by '${w3.did()}'`) + receipt.out.error?.message.includes( + `can not be (self) issued by '${w3.did()}'` + ) ) } }) @@ -177,14 +180,12 @@ test('checks for single capability invocation', async () => { const server = Server.create({ id: w3, service: { identity: Access }, - decoder: CAR, - encoder: CBOR, + codec: CAR.inbound, }) const client = Client.connect({ id: w3, - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, channel: server, }) @@ -210,16 +211,19 @@ test('checks for single capability invocation', async () => { with: 'mailto:bob@web.mail', }) - const result = await invocation.execute(client) - - assert.deepNestedInclude(result, { - error: true, - name: 'InvocationCapabilityError', - message: 'Invocation is required to have a single capability.', - capabilities: [ - { can: 'identity/register', with: 'mailto:bob@web.mail' }, - { can: 'identity/register', with: 'mailto:bob@web.mail' }, - ], + const receipt = await invocation.execute(client) + + assert.containSubset(receipt, { + out: { + error: { + name: 'InvocationCapabilityError', + message: 'Invocation is required to have a single capability.', + capabilities: [ + { can: 'identity/register', with: 'mailto:bob@web.mail' }, + { can: 'identity/register', with: 'mailto:bob@web.mail' }, + ], + }, + }, }) }) @@ -227,8 +231,7 @@ test('test access/claim provider', async () => { const server = Server.create({ id: w3, service: { access: Access }, - decoder: CAR, - encoder: CBOR, + codec: CAR.inbound, }) /** @@ -240,8 +243,7 @@ test('test access/claim provider', async () => { */ const client = Client.connect({ id: w3, - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, channel: server, }) @@ -251,8 +253,8 @@ test('test access/claim provider', async () => { with: alice.did(), }) - const result = await claim.execute(client) - assert.deepEqual(result, []) + const receipt = await claim.execute(client) + assert.deepEqual(receipt.out, { ok: [] }) }) test('handle did:mailto audiences', async () => { diff --git a/packages/server/test/server.spec.js b/packages/server/test/server.spec.js index c6da2812..1f89c95a 100644 --- a/packages/server/test/server.spec.js +++ b/packages/server/test/server.spec.js @@ -2,6 +2,7 @@ import * as Client from '@ucanto/client' import * as Server from '../src/lib.js' import * as CAR from '@ucanto/transport/car' import * as CBOR from '@ucanto/transport/cbor' +import * as Transport from '@ucanto/transport' import { alice, bob, mallory, service as w3 } from './fixtures.js' import * as Service from '../../client/test/service.js' import { test, assert } from './test.js' @@ -67,15 +68,13 @@ test('encode delegated invocation', async () => { const server = Server.create({ service: Service.create(), - decoder: CAR.request, - encoder: CAR.response, + codec: CAR.inbound, id: w3, }) const connection = Client.connect({ id: w3, - encoder: CAR.request, - decoder: CAR.response, + codec: CAR.outbound, channel: server, }) @@ -183,14 +182,12 @@ test('unknown handler', async () => { const server = Server.create({ id: w3, service: Service.create(), - decoder: CAR, - encoder: CBOR, + codec: CAR.inbound, }) const connection = Client.connect({ id: w3, - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, channel: server, }) @@ -206,13 +203,17 @@ test('unknown handler', async () => { // @ts-expect-error - reporst that service has no such capability const error = await register.execute(connection) - assert.deepNestedInclude(error, { - error: true, - name: 'HandlerNotFound', - message: `service does not implement {can: "access/register"} handler`, - capability: { - can: 'access/register', - with: 'did:email:alice@mail.com', + assert.containSubset(error, { + out: { + error: { + error: true, + name: 'HandlerNotFound', + message: `service does not implement {can: "access/register"} handler`, + capability: { + can: 'access/register', + with: 'did:email:alice@mail.com', + }, + }, }, }) @@ -227,13 +228,17 @@ test('unknown handler', async () => { // @ts-expect-error - reporst that service has no such capability const error2 = await boom.execute(connection) - assert.deepNestedInclude(error2, { - error: true, - name: 'HandlerNotFound', - message: `service does not implement {can: "test/boom"} handler`, - capability: { - can: 'test/boom', - with: 'about:me', + assert.containSubset(error2, { + out: { + error: { + error: true, + name: 'HandlerNotFound', + message: `service does not implement {can: "test/boom"} handler`, + capability: { + can: 'test/boom', + with: 'about:me', + }, + }, }, }) }) @@ -243,22 +248,20 @@ test('execution error', async () => { service: { test: { /** - * @param {Server.Invocation<{can: "test/boom", with:string}>} _ + * @param {Server.API.Invocation<{can: "test/boom", with:string}>} _ */ boom(_) { throw new Server.Failure('Boom') }, }, }, - decoder: CAR, - encoder: CBOR, + codec: CAR.inbound, id: w3, }) const connection = Client.connect({ id: w3, - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, channel: server, }) @@ -271,19 +274,24 @@ test('execution error', async () => { }, }) - const error = await boom.execute(connection) + const receipt = await boom.execute(connection) - assert.containSubset(error, { - error: true, - name: 'HandlerExecutionError', - message: `service handler {can: "test/boom"} error: Boom`, - capability: { - can: 'test/boom', - with: alice.did(), - }, - cause: { - message: 'Boom', - name: 'Error', + assert.containSubset(receipt, { + issuer: w3.verifier, + out: { + error: { + error: true, + name: 'HandlerExecutionError', + message: `service handler {can: "test/boom"} error: Boom`, + capability: { + can: 'test/boom', + with: alice.did(), + }, + cause: { + message: 'Boom', + name: 'Error', + }, + }, }, }) }) @@ -295,15 +303,13 @@ test('did:web server', async () => { const server = Server.create({ service: Service.create(), - decoder: CAR, - encoder: CBOR, + codec: CAR.inbound, id: w3.withDID('did:web:web3.storage'), }) const connection = Client.connect({ id: server.id, - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, channel: server, }) @@ -316,7 +322,57 @@ test('did:web server', async () => { }, }) - const register = await identify.execute(connection) + const receipt = await identify.execute(connection) + + assert.containSubset(receipt, { + out: { + ok: {}, + }, + }) - assert.deepEqual(register, null) + assert.deepEqual(receipt.issuer?.did(), server.id.did()) +}) + +test('unsupported content-type', async () => { + const server = Server.create({ + service: Service.create(), + id: w3.withDID('did:web:web3.storage'), + codec: Transport.inbound({ + decoders: { + 'application/workflow+car': CAR.request, + }, + encoders: { + 'application/car': CAR.response, + }, + }), + }) + + const connection = Client.connect({ + id: server.id, + codec: CAR.outbound, + channel: server, + }) + + const identify = Client.invoke({ + issuer: alice, + audience: server.id, + capability: { + can: 'access/identify', + with: 'did:email:alice@mail.com', + }, + }) + + const receipt = await identify.execute(connection) + assert.containSubset(receipt, { + out: { + error: { + message: + 'The server cannot process the request because the payload format is not supported. Please check the content-type header and try again with a supported media type.', + status: 415, + headers: { + accept: 'application/workflow+car', + }, + }, + }, + }) }) diff --git a/packages/transport/src/car.js b/packages/transport/src/car.js index b693f8cf..7a66bfc2 100644 --- a/packages/transport/src/car.js +++ b/packages/transport/src/car.js @@ -2,9 +2,12 @@ import * as API from '@ucanto/interface' import * as CAR from './car/codec.js' import * as request from './car/request.js' import * as response from './car/response.js' +import * as Selector from './codec.js' export { CAR as codec, request, response } +export const contentType = 'application/car' + const HEADERS = Object.freeze({ 'content-type': 'application/car', }) @@ -23,3 +26,21 @@ export const encode = (invocations, options) => * @deprecated */ export const decode = request.decode + +export const inbound = Selector.inbound({ + decoders: { + 'application/car': request, + }, + encoders: { + 'application/car': response, + }, +}) + +export const outbound = Selector.outbound({ + encoders: { + 'application/car': request, + }, + decoders: { + 'application/car': response, + }, +}) diff --git a/packages/transport/src/codec.js b/packages/transport/src/codec.js new file mode 100644 index 00000000..9df6f1a0 --- /dev/null +++ b/packages/transport/src/codec.js @@ -0,0 +1,182 @@ +import * as API from '@ucanto/interface' + +/** + * @typedef {`${Lowercase}/${Lowercase}`|`${Lowercase}/${Lowercase}+${Lowercase}`} ContentType + * @typedef {`${Lowercase}/${Lowercase}`|`${Lowercase}/${Lowercase};q=${number}.${number}`} MediaType + * @param {object} source + * @param {Record} source.decoders + * @param {Record} source.encoders + * @returns {API.InboundCodec} + */ +export const inbound = source => new Inbound(source) + +/** + * @implements {API.InboundCodec} + */ +class Inbound { + /** + * @param {API.HTTPRequest} request + * @returns {API.ReceiptResult} transport + */ + accept({ headers }) { + const contentType = headers['content-type'] || headers['Content-Type'] + const decoder = this.decoders[contentType] + if (!decoder) { + return { + error: { + status: 415, + message: `The server cannot process the request because the payload format is not supported. Please check the content-type header and try again with a supported media type.`, + headers: { + accept: Object.keys(this.decoders).join(', '), + }, + }, + } + } + + const accept = parseAcceptHeader(headers.accept || headers.Accept || '*/*') + for (const { category, type } of accept) { + for (const encoder of this.encoders) { + const select = + (category === '*' || category === encoder.category) && + (type === '*' || type === encoder.type) + + if (select) { + return { ok: { ...encoder, decoder } } + } + } + } + + return { + error: { + status: 406, + message: `The requested resource cannot be served in the requested content type. Please specify a supported content type using the Accept header.`, + headers: { + accept: Object.keys(this.encoders).join(', '), + }, + }, + } + } + + /** + * @param {object} source + * @param {Record} source.decoders + * @param {Record} source.encoders + */ + constructor({ decoders = {}, encoders = {} }) { + this.decoders = decoders + // We sort the encoders by preference, so that we can pick the most + // preferred one when client accepts multiple content types. + this.encoders = Object.entries(encoders) + .map(([contentType, encoder]) => { + return { ...parseMediaType(contentType), encoder } + }) + .sort((a, b) => b.preference - a.preference) + } +} + +/** + * @param {object} source + * @param {Record} source.encoders + * @param {Record} source.decoders + * @returns {API.OutboundCodec} + */ +export const outbound = source => new Outbound(source) + +/** + * @implements {API.OutboundCodec} + */ +class Outbound { + /** + * @param {object} source + * @param {Record} source.encoders + * @param {Record} source.decoders + */ + constructor({ decoders = {}, encoders = {} }) { + this.decoders = decoders + + // We sort the encoders by preference, so that we can pick the most + // preferred one when client accepts multiple content types. + this.encoders = Object.entries(encoders) + .map(([contentType, encoder]) => { + return { ...parseMediaType(contentType), encoder } + }) + .sort((a, b) => b.preference - a.preference) + + this.acceptType = this.encoders + .map( + ({ category, type, preference }) => + `${category}/${type}${preference ? `;q=${preference}` : ''}` + ) + .join(', ') + + if (this.encoders.length === 0) { + throw new Error('At least one encoder MUST be provided') + } + + this.encoder = this.encoders[0].encoder + } + + /** + * @template {API.Tuple} I + * @param {I} workflow + */ + encode(workflow) { + return this.encoder.encode(workflow, { + accept: this.acceptType, + }) + } + /** + * @template {API.Tuple} I + * @param {API.HTTPResponse} response + * @returns {API.Await} + */ + decode(response) { + const { headers } = response + const contentType = headers['content-type'] || headers['Content-Type'] + const decoder = this.decoders[contentType] || this.decoders['*/*'] + switch (response.status) { + case 415: + case 406: + throw Object.assign( + new RangeError(new TextDecoder().decode(response.body)), + { + status: response.status, + headers: response.headers, + } + ) + } + if (!decoder) { + throw TypeError( + `Can not decode response with content-type '${contentType}' because no matching transport decoder is configured.` + ) + } + + return decoder.decode(response) + } +} + +/** + * + * @param {string} source + * @returns {{ category: string, type: string, preference: number }} + */ +export const parseMediaType = source => { + const [mediaType = '*/*', mediaRange = 'q=0'] = source.trim().split(';') + const [category = '*', type = '*'] = mediaType.split('/') + const params = new URLSearchParams(mediaRange) + const preference = parseFloat(params.get('q') || '0') + return { + category, + type, + preference: isNaN(preference) ? 0 : preference, + } +} + +/** + * @param {string} source + */ +export const parseAcceptHeader = source => + source + .split(',') + .map(parseMediaType) + .sort((a, b) => b.preference - a.preference) diff --git a/packages/transport/src/legacy.js b/packages/transport/src/legacy.js new file mode 100644 index 00000000..17176052 --- /dev/null +++ b/packages/transport/src/legacy.js @@ -0,0 +1,43 @@ +import * as API from '@ucanto/interface' +import * as Selector from './codec.js' +import * as CAR from './car.js' +import { encode as encodeCBOR } from '@ucanto/core/cbor' + +export const CBOR = { + /** + * Encodes receipts into a legacy CBOR representation. + * + * @template {API.Tuple} I + * @param {I} receipts + * @param {API.EncodeOptions} [options] + * @returns {API.HTTPResponse} + */ + encode(receipts, options) { + const legacyResults = [] + for (const receipt of receipts) { + const result = receipt.out + if (result.ok) { + legacyResults.push(result.ok) + } else { + legacyResults.push({ + ...result.error, + error: true, + }) + } + } + return /** @type {API.HTTPResponse} */ ({ + headers: { 'content-type': 'application/cbor' }, + body: encodeCBOR(legacyResults), + }) + }, +} + +export const inbound = Selector.inbound({ + decoders: { + 'application/car': CAR.request, + }, + encoders: { + '*/*;q=0.1': CBOR, + 'application/car': CAR.response, + }, +}) diff --git a/packages/transport/src/lib.js b/packages/transport/src/lib.js index a40e6a5a..b5b51c28 100644 --- a/packages/transport/src/lib.js +++ b/packages/transport/src/lib.js @@ -3,3 +3,5 @@ export * as CAR from './car.js' export * as JWT from './jwt.js' export * as HTTP from './http.js' export * as UTF8 from './utf8.js' +export * as Legacy from './legacy.js' +export * from './codec.js' diff --git a/packages/transport/test/legacy.spec.js b/packages/transport/test/legacy.spec.js new file mode 100644 index 00000000..3279a821 --- /dev/null +++ b/packages/transport/test/legacy.spec.js @@ -0,0 +1,80 @@ +import { test, assert } from './test.js' +import * as CAR from '../src/car.js' +import * as CBOR from '../src/cbor.js' +import * as Legacy from '../src/legacy.js' +import { + delegate, + invoke, + Receipt, + Delegation, + UCAN, + parseLink, + isLink, +} from '@ucanto/core' +import { alice, bob, mallory, service } from './fixtures.js' + +test('Legacy decode / encode', async () => { + const expiration = 1654298135 + + const source = await CAR.outbound.encode([ + invoke({ + issuer: alice, + audience: bob, + capability: { + can: 'store/add', + with: alice.did(), + }, + expiration, + proofs: [], + }), + ]) + + const accept = await Legacy.inbound.accept({ + headers: { 'content-type': 'application/car' }, + body: source.body, + }) + if (accept.error) { + return assert.equal(accept.error, undefined) + } + const { encoder, decoder } = accept.ok + + const workflow = await decoder.decode(source) + + const expect = await Delegation.delegate({ + issuer: alice, + audience: bob, + capabilities: [ + { + can: 'store/add', + with: alice.did(), + }, + ], + expiration, + proofs: [], + }) + + assert.deepEqual([expect], workflow, 'roundtrips') + + const success = await Receipt.issue({ + ran: expect.cid, + issuer: bob, + result: { ok: { hello: 'message' } }, + }) + + const failure = await Receipt.issue({ + ran: expect.cid, + issuer: bob, + result: { error: { message: 'Boom' } }, + }) + + const response = await encoder.encode([success, failure]) + const results = await CBOR.decode(response) + + assert.deepEqual( + results, + // @ts-expect-error - This not according to the types but it is what + // we want to return to old clients. + [{ hello: 'message' }, { error: true, message: 'Boom' }], + 'roundtrips' + ) +}) From ff7220c7d80ad6fe4997eaa98eca6eb6f93bbd95 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 08:43:15 -0700 Subject: [PATCH 03/14] cover new transport code --- packages/transport/src/cbor.js | 2 + packages/transport/src/codec.js | 51 ++++-- packages/transport/test/codec.spec.js | 247 ++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 14 deletions(-) create mode 100644 packages/transport/test/codec.spec.js diff --git a/packages/transport/src/cbor.js b/packages/transport/src/cbor.js index a11d6147..40cdf273 100644 --- a/packages/transport/src/cbor.js +++ b/packages/transport/src/cbor.js @@ -13,6 +13,7 @@ export const codec = CBOR * @template I * @param {I} result * @returns {API.HTTPResponse} + * @deprecated */ export const encode = result => { return { @@ -27,6 +28,7 @@ export const encode = result => { * @template I * @param {API.HTTPResponse} request * @returns {Promise} + * @deprecated */ export const decode = async ({ headers, body }) => { const contentType = headers['content-type'] || headers['Content-Type'] diff --git a/packages/transport/src/codec.js b/packages/transport/src/codec.js index 9df6f1a0..0657d8c8 100644 --- a/packages/transport/src/codec.js +++ b/packages/transport/src/codec.js @@ -51,7 +51,7 @@ class Inbound { status: 406, message: `The requested resource cannot be served in the requested content type. Please specify a supported content type using the Accept header.`, headers: { - accept: Object.keys(this.encoders).join(', '), + accept: formatAcceptHeader(Object.values(this.encoders)), }, }, } @@ -64,13 +64,22 @@ class Inbound { */ constructor({ decoders = {}, encoders = {} }) { this.decoders = decoders + + if (Object.keys(decoders).length === 0) { + throw new Error('At least one decoder MUST be provided') + } + // We sort the encoders by preference, so that we can pick the most // preferred one when client accepts multiple content types. this.encoders = Object.entries(encoders) - .map(([contentType, encoder]) => { - return { ...parseMediaType(contentType), encoder } + .map(([mediaType, encoder]) => { + return { ...parseMediaType(mediaType), encoder } }) .sort((a, b) => b.preference - a.preference) + + if (this.encoders.length === 0) { + throw new Error('At least one encoder MUST be provided') + } } } @@ -94,20 +103,19 @@ class Outbound { constructor({ decoders = {}, encoders = {} }) { this.decoders = decoders + if (Object.keys(decoders).length === 0) { + throw new Error('At least one decoder MUST be provided') + } + // We sort the encoders by preference, so that we can pick the most // preferred one when client accepts multiple content types. this.encoders = Object.entries(encoders) - .map(([contentType, encoder]) => { - return { ...parseMediaType(contentType), encoder } + .map(([mediaType, encoder]) => { + return { ...parseMediaType(mediaType), encoder } }) .sort((a, b) => b.preference - a.preference) - this.acceptType = this.encoders - .map( - ({ category, type, preference }) => - `${category}/${type}${preference ? `;q=${preference}` : ''}` - ) - .join(', ') + this.acceptType = formatAcceptHeader(this.encoders) if (this.encoders.length === 0) { throw new Error('At least one encoder MUST be provided') @@ -156,22 +164,31 @@ class Outbound { } /** - * + * @typedef {{ category: string, type: string, preference: number }} Media * @param {string} source - * @returns {{ category: string, type: string, preference: number }} + * @returns {Media} */ export const parseMediaType = source => { - const [mediaType = '*/*', mediaRange = 'q=0'] = source.trim().split(';') + const [mediaType = '*/*', mediaRange = ''] = source.trim().split(';') const [category = '*', type = '*'] = mediaType.split('/') const params = new URLSearchParams(mediaRange) const preference = parseFloat(params.get('q') || '0') return { category, type, + /* c8 ignore next */ preference: isNaN(preference) ? 0 : preference, } } +/** + * @param {Media} media + */ +export const formatMediaType = ({ category, type, preference }) => + /** @type {MediaType} */ ( + `${category}/${type}${preference ? `;q=${preference}` : ''}` + ) + /** * @param {string} source */ @@ -180,3 +197,9 @@ export const parseAcceptHeader = source => .split(',') .map(parseMediaType) .sort((a, b) => b.preference - a.preference) + +/** + * @param {Media[]} source + */ +export const formatAcceptHeader = source => + source.map(formatMediaType).join(', ') diff --git a/packages/transport/test/codec.spec.js b/packages/transport/test/codec.spec.js new file mode 100644 index 00000000..89d37cc7 --- /dev/null +++ b/packages/transport/test/codec.spec.js @@ -0,0 +1,247 @@ +import { test, assert } from './test.js' +import * as CAR from '../src/car.js' +import * as Transport from '../src/lib.js' +import { alice, bob, mallory, service } from './fixtures.js' +import { invoke, API, delegate, parseLink, Receipt } from '@ucanto/core' +import { CarReader } from '@ipld/car/reader' + +test('unsupported inbound content-type', async () => { + const accept = CAR.inbound.accept({ + headers: { 'content-type': 'application/json' }, + body: new Uint8Array(), + }) + + assert.deepEqual(accept, { + error: { + status: 415, + message: `The server cannot process the request because the payload format is not supported. Please check the content-type header and try again with a supported media type.`, + headers: { + accept: `application/car`, + }, + }, + }) +}) + +test('unsupported inbound accept type', async () => { + const accept = CAR.inbound.accept({ + headers: { 'content-type': 'application/car', accept: 'application/cbor' }, + body: new Uint8Array(), + }) + + assert.deepEqual(accept, { + error: { + status: 406, + message: `The requested resource cannot be served in the requested content type. Please specify a supported content type using the Accept header.`, + headers: { + accept: `application/car`, + }, + }, + }) +}) + +test(`requires encoders / decoders`, async () => { + assert.throws( + () => + Transport.outbound({ encoders: { '*/*': CAR.request }, decoders: {} }), + /At least one decoder MUST be provided/ + ) + + assert.throws( + () => + Transport.outbound({ encoders: {}, decoders: { '*/*': CAR.response } }), + /At least one encoder MUST be provided/ + ) + + assert.throws( + () => + Transport.inbound({ encoders: { '*/*': CAR.response }, decoders: {} }), + /At least one decoder MUST be provided/ + ) + + assert.throws( + () => Transport.inbound({ encoders: {}, decoders: { '*/*': CAR.request } }), + /At least one encoder MUST be provided/ + ) +}) + +test('outbound encode', async () => { + const cid = parseLink( + 'bafyreiaxnmoptsqiehdff2blpptvdbenxcz6xgrbojw5em36xovn2xea4y' + ) + const expiration = 1654298135 + + const request = await CAR.outbound.encode([ + invoke({ + issuer: alice, + audience: bob, + capability: { + can: 'store/add', + with: alice.did(), + }, + expiration, + proofs: [], + }), + ]) + + assert.deepEqual(request.headers, { + 'content-type': 'application/car', + accept: 'application/car', + }) + const reader = await CarReader.fromBytes(request.body) + + assert.deepEqual( + await reader.getRoots(), + // @ts-expect-error - CAR refers to old CID + [cid] + ) + + const expect = await delegate({ + issuer: alice, + audience: bob, + capabilities: [ + { + can: 'store/add', + with: alice.did(), + }, + ], + expiration, + proofs: [], + }) + + assert.deepEqual( + [expect], + await CAR.inbound.accept(request).ok?.decoder.decode(request), + 'roundtrips' + ) +}) + +test('outbound decode', async () => { + const { success, failure } = await buildPayload() + + const response = await CAR.response.encode([success, failure]) + const receipts = await CAR.outbound.decode(response) + + assert.deepEqual( + receipts.map($ => $.root), + [success.root, failure.root] + ) +}) + +test('inbound supports Content-Type header', async () => { + const accept = await CAR.inbound.accept({ + headers: { 'Content-Type': 'application/car' }, + body: new Uint8Array(), + }) + + assert.equal(accept.ok != null, true) +}) + +test('outbound supports Content-Type header', async () => { + const { success } = await buildPayload() + const { body } = await CAR.response.encode([success]) + + const receipts = await CAR.outbound.decode({ + headers: { 'Content-Type': 'application/car' }, + body, + }) + + assert.deepEqual(receipts[0].root, success.root) +}) + +test('inbound encode preference', async () => { + const codec = Transport.inbound({ + encoders: { + 'application/car': CAR.response, + }, + decoders: { + 'application/car': CAR.request, + }, + }) + + const accept = await codec.accept({ + headers: { + 'content-type': 'application/car', + accept: 'application/car', + }, + body: new Uint8Array(), + }) + + assert.equal(accept.ok != null, true) +}) + +test('unsupported response content-type', async () => { + const { success } = await buildPayload() + + const response = await CAR.response.encode([success]) + + const badContentType = await wait(() => + CAR.outbound.decode({ + ...response, + headers: { ...response.headers, 'content-type': 'application/json' }, + }) + ).catch(error => error) + + assert.match( + String(badContentType), + /Can not decode response with content-type 'application\/json'/ + ) + + const badStatus = await wait(() => + CAR.outbound.decode({ + ...response, + headers: { ...response.headers, 'content-type': 'text/plain' }, + status: 415, + body: new TextEncoder().encode('Whatever server sets'), + }) + ).catch(error => error) + + assert.match(String(badStatus), /Whatever server sets/) + assert.equal(Object(badStatus).status, 415) +}) + +test('format media type', async () => { + assert.equal( + Transport.formatMediaType({ + category: 'application', + type: 'car', + preference: 1, + }), + 'application/car;q=1' + ) +}) + +const buildPayload = async () => { + const expiration = 1654298135 + const ran = await delegate({ + issuer: alice, + audience: bob, + capabilities: [ + { + can: 'store/add', + with: alice.did(), + }, + ], + expiration, + proofs: [], + }) + + const success = await Receipt.issue({ + ran: ran.cid, + issuer: bob, + result: { ok: { hello: 'message' } }, + }) + + const failure = await Receipt.issue({ + ran: ran.cid, + issuer: bob, + result: { error: { message: 'Boom' } }, + }) + + return { ran, success, failure } +} + +/** + * @template {(...args:any[]) => any} Fn + * @param {Fn} fn + */ +const wait = async fn => fn() From 8c0c88660f0da4d24bc5ca8d75ebf61d8d156ff0 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 10:07:09 -0700 Subject: [PATCH 04/14] fix client types and test --- packages/client/test/client.spec.js | 189 +++++++++++++++-------- packages/client/test/services/account.js | 6 +- packages/core/src/lib.js | 4 +- packages/interface/src/lib.ts | 2 +- packages/transport/src/codec.js | 10 +- packages/transport/src/lib.js | 1 + 6 files changed, 140 insertions(+), 72 deletions(-) diff --git a/packages/client/test/client.spec.js b/packages/client/test/client.spec.js index 9f6dc830..6b7ea709 100644 --- a/packages/client/test/client.spec.js +++ b/packages/client/test/client.spec.js @@ -1,9 +1,9 @@ import { test, assert } from './test.js' import * as Client from '../src/lib.js' import * as HTTP from '@ucanto/transport/http' -import * as CAR from '@ucanto/transport/car' -import * as CBOR from '@ucanto/transport/cbor' +import { CAR, Codec } from '@ucanto/transport' import * as Service from './service.js' +import { Receipt, CBOR } from '@ucanto/core' import { alice, bob, mallory, service as w3 } from './fixtures.js' import fetch from '@web-std/fetch' @@ -12,12 +12,11 @@ test('encode invocation', async () => { const connection = Client.connect({ id: w3, channel: HTTP.open({ url: new URL('about:blank'), fetch }), - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, }) const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world ' })], + roots: [await CBOR.write({ hello: 'world ' })], }) const add = Client.invoke({ @@ -31,10 +30,11 @@ test('encode invocation', async () => { proofs: [], }) - const payload = await connection.encoder.encode([add]) + const payload = await connection.codec.encode([add]) assert.deepEqual(payload.headers, { 'content-type': 'application/car', + accept: 'application/car', }) assert.ok(payload.body instanceof Uint8Array) @@ -56,15 +56,14 @@ test('encode invocation', async () => { test('encode delegated invocation', async () => { const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world ' })], + roots: [await CBOR.write({ hello: 'world ' })], }) /** @type {Client.ConnectionView} */ const connection = Client.connect({ id: w3, channel: HTTP.open({ url: new URL('about:blank'), fetch }), - encoder: CAR, - decoder: CBOR, + codec: CAR.outbound, }) const proof = await Client.delegate({ @@ -99,7 +98,7 @@ test('encode delegated invocation', async () => { }, }) - const payload = await connection.encoder.encode([add, remove]) + const payload = await connection.codec.encode([add, remove]) const request = await CAR.decode(payload) { const [add, remove] = request @@ -137,47 +136,62 @@ test('encode delegated invocation', async () => { }) const service = Service.create() -/** @type {Client.ConnectionView} */ -const connection = Client.connect({ - id: w3, - channel: HTTP.open({ - url: new URL('about:blank'), - fetch: async (url, input) => { - const invocations = await CAR.decode(input) - const promises = invocations.map(invocation => { - const [capabality] = invocation.capabilities - switch (capabality.can) { - case 'store/add': { - return service.store.add( - /** @type {Client.Invocation} */ (invocation) - ) - } - case 'store/remove': { - return service.store.remove( - /** @type {Client.Invocation} */ (invocation) - ) - } + +const channel = HTTP.open({ + url: new URL('about:blank'), + fetch: async (url, input) => { + /** @type {Client.Tuple} */ + const invocations = await CAR.request.decode(input) + const promises = invocations.map(async invocation => { + const [capability] = invocation.capabilities + switch (capability.can) { + case 'store/add': { + const result = await service.store.add( + /** @type {Client.Invocation} */ (invocation) + ) + return Receipt.issue({ + ran: invocation.cid, + issuer: w3, + result: result?.error ? { error: result } : { ok: result }, + }) + } + case 'store/remove': { + const result = await service.store.remove( + /** @type {Client.Invocation} */ (invocation) + ) + return Receipt.issue({ + ran: invocation.cid, + issuer: w3, + result: result?.error ? { error: result } : { ok: result }, + }) } - }) + } + }) - const results = await Promise.all(promises) + const receipts = /** @type {Client.Tuple} */ ( + await Promise.all(promises) + ) - const { headers, body } = await CBOR.encode(results) + const { headers, body } = await CAR.response.encode(receipts) - return { - ok: true, - headers: new Map(Object.entries(headers)), - arrayBuffer: () => body, - } - }, - }), - encoder: CAR, - decoder: CBOR, + return { + ok: true, + headers: new Map(Object.entries(headers)), + arrayBuffer: () => body, + } + }, +}) + +/** @type {Client.ConnectionView} */ +const connection = Client.connect({ + id: w3, + channel, + codec: CAR.outbound, }) test('execute', async () => { const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world ' })], + roots: [await CBOR.write({ hello: 'world ' })], }) const add = Client.invoke({ @@ -203,11 +217,13 @@ test('execute', async () => { const e1 = await add.execute(connection) - assert.deepEqual(e1, { - error: true, - name: 'UnknownDIDError', - message: `DID ${alice.did()} has no account`, - did: alice.did(), + assert.deepEqual(e1.out, { + error: { + error: true, + name: 'UnknownDIDError', + message: `DID ${alice.did()} has no account`, + did: alice.did(), + }, }) // fake register alice @@ -218,17 +234,19 @@ test('execute', async () => { ) const [r1] = await connection.execute(add) - assert.deepEqual(r1, { - with: alice.did(), - link: car.cid, - status: 'upload', - url: 'http://localhost:9090/', + assert.deepEqual(r1.out, { + ok: { + with: alice.did(), + link: car.cid, + status: 'upload', + url: 'http://localhost:9090/', + }, }) }) test('execute with delegations', async () => { const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world ' })], + roots: [await CBOR.write({ hello: 'world ' })], }) const add = Client.invoke({ @@ -244,21 +262,64 @@ test('execute with delegations', async () => { const [e1] = await connection.execute(await add.delegate()) - assert.deepEqual(e1, { - error: true, - name: 'UnknownDIDError', - message: `DID ${bob.did()} has no account`, - did: bob.did(), + assert.deepEqual(e1.out, { + error: { + error: true, + name: 'UnknownDIDError', + message: `DID ${bob.did()} has no account`, + did: bob.did(), + }, }) // fake register alice service.access.accounts.register(bob.did(), 'did:email:bob@web.mail', car.cid) const [r1] = await connection.execute(await add.delegate()) - assert.deepEqual(r1, { - with: bob.did(), - link: car.cid, - status: 'upload', - url: 'http://localhost:9090/', + assert.deepEqual(r1.out, { + ok: { + with: bob.did(), + link: car.cid, + status: 'upload', + url: 'http://localhost:9090/', + }, + }) +}) + +test('decode error', async () => { + const client = Client.connect({ + id: w3, + channel, + codec: Codec.outbound({ + encoders: { + 'application/car': CAR.request, + }, + decoders: { + 'application/car+receipt': CAR.response, + }, + }), + }) + + const car = await CAR.codec.write({ + roots: [await CBOR.write({ hello: 'world ' })], + }) + + const add = Client.invoke({ + issuer: alice, + audience: w3, + capability: { + can: 'store/add', + with: alice.did(), + nb: { link: car.cid }, + }, + proofs: [], + }) + + const [e1] = await client.execute(await add.delegate()) + assert.deepEqual(e1.out, { + error: { + error: true, + message: + "Can not decode response with content-type 'application/car' because no matching transport decoder is configured.", + }, }) }) diff --git a/packages/client/test/services/account.js b/packages/client/test/services/account.js index c353830a..2ed978e4 100644 --- a/packages/client/test/services/account.js +++ b/packages/client/test/services/account.js @@ -69,7 +69,7 @@ const unlink = (model, member, group, proof) => { if (account === resolve(model, member)) { model.delete(member) } - return null + return {} } else { return new UnknownDIDError('Unknown DID', group) } @@ -81,7 +81,7 @@ const unlink = (model, member, group, proof) => { * @param {API.DID} to * @param {API.Link} proof * @param {boolean} create - * @returns {API.SyncResult} + * @returns {API.SyncResult<{}, API.UnknownDIDError>} */ const associate = (accounts, from, to, proof, create) => { const fromAccount = resolve(accounts, from) @@ -110,7 +110,7 @@ const associate = (accounts, from, to, proof, create) => { accounts.set(fromAccount, { account, proof }) } - return null + return {} } /** diff --git a/packages/core/src/lib.js b/packages/core/src/lib.js index 5fe2492f..fb4f5e61 100644 --- a/packages/core/src/lib.js +++ b/packages/core/src/lib.js @@ -2,8 +2,8 @@ export * as API from '@ucanto/interface' export * as Delegation from './delegation.js' export * as Invocation from './invocation.js' export * as Receipt from './receipt.js' -import * as DAG from './dag.js' -import * as CBOR from './cbor.js' +export * as DAG from './dag.js' +export * as CBOR from './cbor.js' export { delegate, isDelegation } from './delegation.js' export { invoke } from './invocation.js' export { diff --git a/packages/interface/src/lib.ts b/packages/interface/src/lib.ts index ea7ae271..49b1d419 100644 --- a/packages/interface/src/lib.ts +++ b/packages/interface/src/lib.ts @@ -712,7 +712,7 @@ export interface Connection> export interface ConnectionView> extends Connection { - id: Signer + id: Principal execute< C extends Capability, I extends Transport.Tuple> diff --git a/packages/transport/src/codec.js b/packages/transport/src/codec.js index 0657d8c8..a1c55d97 100644 --- a/packages/transport/src/codec.js +++ b/packages/transport/src/codec.js @@ -148,14 +148,20 @@ class Outbound { throw Object.assign( new RangeError(new TextDecoder().decode(response.body)), { + error: true, status: response.status, headers: response.headers, } ) } if (!decoder) { - throw TypeError( - `Can not decode response with content-type '${contentType}' because no matching transport decoder is configured.` + throw Object.assign( + TypeError( + `Can not decode response with content-type '${contentType}' because no matching transport decoder is configured.` + ), + { + error: true, + } ) } diff --git a/packages/transport/src/lib.js b/packages/transport/src/lib.js index b5b51c28..20fb6b90 100644 --- a/packages/transport/src/lib.js +++ b/packages/transport/src/lib.js @@ -4,4 +4,5 @@ export * as JWT from './jwt.js' export * as HTTP from './http.js' export * as UTF8 from './utf8.js' export * as Legacy from './legacy.js' +export * as Codec from './codec.js' export * from './codec.js' From d3225b6a82219ec2e624dbaca424d7b92a8e7835 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 10:09:32 -0700 Subject: [PATCH 05/14] update validator inference test --- packages/validator/test/inference.spec.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/validator/test/inference.spec.js b/packages/validator/test/inference.spec.js index f7c28655..f7a1a714 100644 --- a/packages/validator/test/inference.spec.js +++ b/packages/validator/test/inference.spec.js @@ -43,13 +43,13 @@ test('execute capability', () => }) const r1 = await redeem.execute(connection) - if (!r1.error) { - r1.product.toLowerCase() + if (r1.out.ok) { + r1.out.ok.product.toLowerCase() } const r2 = await claim.execute(connection) - if (!r2.error) { - r2.service.toUpperCase() + if (r2.out.ok) { + r2.out.ok.service.toUpperCase() } }) @@ -88,9 +88,9 @@ test('use InferInvokedCapability', () => }, }) - const result = await redeem.execute(connection) - if (!result.error) { - result.product.toLocaleLowerCase() + const receipt = await redeem.execute(connection) + if (receipt.out.ok) { + receipt.out.ok.product.toLocaleLowerCase() } }) From 0b60a4a47532d99df24dd1a31a6d8b1d2746b4ec Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 10:15:09 -0700 Subject: [PATCH 06/14] update deps --- packages/client/src/connection.js | 3 +-- packages/core/src/lib.js | 1 + packages/core/src/receipt/outcome.js | 3 +-- pnpm-lock.yaml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/client/src/connection.js b/packages/client/src/connection.js index 54c33c3e..38dd3640 100644 --- a/packages/client/src/connection.js +++ b/packages/client/src/connection.js @@ -1,7 +1,6 @@ import * as API from '@ucanto/interface' import { sha256 } from 'multiformats/hashes/sha2' -import { parseLink, Receipt } from '@ucanto/core' -import * as Signature from '@ipld/dag-ucan/signature' +import { Receipt, Signature } from '@ucanto/core' /** * Creates a connection to a service. diff --git a/packages/core/src/lib.js b/packages/core/src/lib.js index fb4f5e61..9c0968f9 100644 --- a/packages/core/src/lib.js +++ b/packages/core/src/lib.js @@ -15,3 +15,4 @@ export { } from './link.js' export * as UCAN from '@ipld/dag-ucan' export * as DID from '@ipld/dag-ucan/did' +export * as Signature from '@ipld/dag-ucan/signature' diff --git a/packages/core/src/receipt/outcome.js b/packages/core/src/receipt/outcome.js index ea1bd196..f6a944b8 100644 --- a/packages/core/src/receipt/outcome.js +++ b/packages/core/src/receipt/outcome.js @@ -1,7 +1,6 @@ import * as API from '@ucanto/interface' import * as Invocation from '../invocation.js' -import { Delegation } from '../lib.js' -import * as DID from '@ipld/dag-ucan/did' +import { Delegation, DID } from '../lib.js' import * as DAG from '../dag.js' /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aee768a0..cff3674a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: 5.4 +lockfileVersion: 5.3 importers: From 5b051ef5dfe5a1caddf5e10a8fc6b23731928b7b Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 11:32:32 -0700 Subject: [PATCH 07/14] cover uncovered lines --- packages/server/src/handler.js | 4 +-- packages/server/src/lib.js | 11 +++++++- packages/server/test/server.spec.js | 43 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/server/src/handler.js b/packages/server/src/handler.js index 2a73cd1e..2a428fcd 100644 --- a/packages/server/src/handler.js +++ b/packages/server/src/handler.js @@ -10,7 +10,7 @@ import { access, Schema, Failure } from '@ucanto/validator' * @template {API.Ability} A * @template {API.URI} R * @template {API.Caveats} C - * @template {unknown} U + * @template {{}} U * @param {API.CapabilityParser>>} capability * @param {(input:API.ProviderInput>) => API.Await} handler * @returns {API.ServiceMethod, Exclude, Exclude>>} @@ -30,7 +30,7 @@ export const provide = (capability, handler) => * @template {API.Ability} A * @template {API.URI} R * @template {API.Caveats} C - * @template {unknown} U + * @template {{}} U * @param {object} input * @param {API.Reader} [input.audience] * @param {API.CapabilityParser>>} input.capability diff --git a/packages/server/src/lib.js b/packages/server/src/lib.js index 1a4b21a8..5ae32a4b 100644 --- a/packages/server/src/lib.js +++ b/packages/server/src/lib.js @@ -8,7 +8,16 @@ export { URI, } from './server.js' export * from '@ucanto/core' -export { invoke, Invocation, Receipt, Delegation, DID } from '@ucanto/core' +export { + invoke, + Invocation, + Receipt, + Delegation, + DID, + Signature, +} from '@ucanto/core' + +export { access, claim, Schema } from '@ucanto/validator' export * from './handler.js' export * as API from './api.js' diff --git a/packages/server/test/server.spec.js b/packages/server/test/server.spec.js index 1f89c95a..e831e66a 100644 --- a/packages/server/test/server.spec.js +++ b/packages/server/test/server.spec.js @@ -376,3 +376,46 @@ test('unsupported content-type', async () => { }, }) }) + +test('falsy errors are turned into {}', async () => { + const testNull = Server.capability({ + can: 'test/null', + with: Server.Schema.did(), + nb: Schema.struct({}), + }) + + const server = Server.create({ + service: { + test: { + null: Server.provide( + testNull, + + async () => { + return null + } + ), + }, + }, + id: w3.withDID('did:web:web3.storage'), + codec: CAR.inbound, + }) + + const connection = Client.connect({ + id: server.id, + codec: CAR.outbound, + channel: server, + }) + + const receipt = await Client.invoke({ + issuer: alice, + audience: server.id, + capability: { + can: 'test/null', + with: alice.did(), + }, + }).execute(connection) + + assert.deepEqual(receipt.out, { + ok: {}, + }) +}) From ffca093ef8bd83be903dd4def7b005dd90cebb24 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 12:19:37 -0700 Subject: [PATCH 08/14] refactor to trim deps --- .vscode/settings.json | 6 +- packages/client/package.json | 3 +- packages/client/src/connection.js | 3 +- packages/core/package.json | 4 + .../src/car/codec.js => core/src/car.js} | 4 +- packages/core/src/lib.js | 2 + packages/core/src/link.js | 2 + packages/core/test/car.spec.js | 38 + packages/server/package.json | 1 - packages/server/test/handler.spec.js | 1 - packages/server/test/server.spec.js | 6 +- packages/transport/package.json | 9 +- packages/transport/src/car.js | 8 +- packages/transport/src/car/request.js | 2 +- packages/transport/src/car/response.js | 2 +- packages/transport/src/cbor.js | 42 - packages/transport/src/jwt.js | 2 +- packages/transport/src/legacy.js | 4 +- packages/transport/src/lib.js | 1 - packages/transport/test/car.spec.js | 64 +- packages/transport/test/cbor.spec.js | 102 -- packages/transport/test/codec.spec.js | 12 +- packages/transport/test/https.spec.js | 1 - packages/transport/test/jwt.spec.js | 4 +- packages/transport/test/legacy.spec.js | 16 +- packages/validator/src/error.js | 2 +- packages/validator/src/schema/link.js | 3 +- packages/validator/test/delegate.spec.js | 8 +- pnpm-lock.yaml | 1387 +++++++++-------- 29 files changed, 805 insertions(+), 934 deletions(-) rename packages/{transport/src/car/codec.js => core/src/car.js} (97%) create mode 100644 packages/core/test/car.spec.js delete mode 100644 packages/transport/src/cbor.js delete mode 100644 packages/transport/test/cbor.spec.js diff --git a/.vscode/settings.json b/.vscode/settings.json index a36a0d26..98bdba45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,10 @@ { "typescript.tsdk": "node_modules/typescript/lib", "cSpell.words": [ - "Unextractable" + "bafyreiaxnmoptsqiehdff", + "blpptvdbenxcz", + "Unextractable", + "xgrbojw", + "xovn" ] } diff --git a/packages/client/package.json b/packages/client/package.json index ea878217..038faaf0 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -30,12 +30,11 @@ }, "dependencies": { "@ucanto/interface": "workspace:^", - "multiformats": "^11.0.0" + "@ucanto/core": "workspace:^" }, "devDependencies": { "@types/chai": "^4.3.3", "@types/mocha": "^10.0.1", - "@ucanto/core": "workspace:^", "@ucanto/principal": "workspace:^", "@ucanto/transport": "workspace:^", "@web-std/fetch": "^4.1.0", diff --git a/packages/client/src/connection.js b/packages/client/src/connection.js index 38dd3640..757d59cf 100644 --- a/packages/client/src/connection.js +++ b/packages/client/src/connection.js @@ -1,6 +1,5 @@ import * as API from '@ucanto/interface' -import { sha256 } from 'multiformats/hashes/sha2' -import { Receipt, Signature } from '@ucanto/core' +import { Receipt, Signature, sha256 } from '@ucanto/core' /** * Creates a connection to a service. diff --git a/packages/core/package.json b/packages/core/package.json index fa934842..990a9d99 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -84,6 +84,10 @@ "types": "./dist/src/cbor.d.ts", "import": "./src/cbor.js" }, + "./car": { + "types": "./dist/src/car.d.ts", + "import": "./src/car.js" + }, "./dag": { "types": "./dist/src/dag.d.ts", "import": "./src/dag.js" diff --git a/packages/transport/src/car/codec.js b/packages/core/src/car.js similarity index 97% rename from packages/transport/src/car/codec.js rename to packages/core/src/car.js index 5ccbefd4..cc84687b 100644 --- a/packages/transport/src/car/codec.js +++ b/packages/core/src/car.js @@ -2,7 +2,7 @@ import * as API from '@ucanto/interface' import { CarBufferReader } from '@ipld/car/buffer-reader' import * as CarBufferWriter from '@ipld/car/buffer-writer' import { base32 } from 'multiformats/bases/base32' -import { UCAN, createLink } from '@ucanto/core' +import { create as createLink } from './link.js' import { sha256 } from 'multiformats/hashes/sha2' export const code = 0x0202 @@ -107,9 +107,7 @@ export const decode = bytes => { } for (const block of reader.blocks()) { - // if (!roots.includes(block)) { blocks.set(block.cid.toString(), block) - // } } return { roots, blocks } diff --git a/packages/core/src/lib.js b/packages/core/src/lib.js index 9c0968f9..225bbe80 100644 --- a/packages/core/src/lib.js +++ b/packages/core/src/lib.js @@ -4,6 +4,7 @@ export * as Invocation from './invocation.js' export * as Receipt from './receipt.js' export * as DAG from './dag.js' export * as CBOR from './cbor.js' +export * as CAR from './car.js' export { delegate, isDelegation } from './delegation.js' export { invoke } from './invocation.js' export { @@ -13,6 +14,7 @@ export { parse as parseLink, decode as decodeLink, } from './link.js' +export { sha256 } from 'multiformats/hashes/sha2' export * as UCAN from '@ipld/dag-ucan' export * as DID from '@ipld/dag-ucan/did' export * as Signature from '@ipld/dag-ucan/signature' diff --git a/packages/core/src/link.js b/packages/core/src/link.js index b49b4574..ff15e791 100644 --- a/packages/core/src/link.js +++ b/packages/core/src/link.js @@ -1 +1,3 @@ export * from 'multiformats/link' +export { base32 } from 'multiformats/bases/base32' +export { base58btc } from 'multiformats/bases/base58' diff --git a/packages/core/test/car.spec.js b/packages/core/test/car.spec.js new file mode 100644 index 00000000..a5f33ebb --- /dev/null +++ b/packages/core/test/car.spec.js @@ -0,0 +1,38 @@ +import { test, assert } from './test.js' +import * as CAR from '../src/car.js' +import * as CBOR from '../src/cbor.js' +import { isLink } from '../src/lib.js' + +test('encode <-> decode', async () => { + const root = await CBOR.write({ hello: 'world ' }) + const block = await CBOR.write({ bye: 'world ' }) + const store = new Map([[block.cid.toString(), block]]) + + const bytes = CAR.encode({ + roots: [root], + blocks: store, + }) + const { blocks, roots } = await CAR.decode(bytes) + assert.equal(blocks.size, 2) + assert.deepEqual(roots, [root]) + assert.deepEqual(blocks.get(root.cid.toString()), root) + assert.deepEqual(blocks.get(block.cid.toString()), block) + + const car = await CAR.write({ roots: [root], blocks: store }) + assert.deepEqual(car.bytes, bytes) + assert.equal(isLink(car.cid), true) + + const link = await CAR.link(car.bytes) + assert.deepEqual(car.cid, link) +}) + +test('car writer', async () => { + const hello = await CBOR.write({ hello: 'world ' }) + const writer = CAR.createWriter() + writer.write(hello) + const bytes = writer.flush() + + const car = await CAR.decode(bytes) + assert.deepEqual(car.roots, []) + assert.deepEqual([...car.blocks], [[hello.cid.toString(), hello]]) +}) diff --git a/packages/server/package.json b/packages/server/package.json index 36097c7b..861fa530 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -45,7 +45,6 @@ "chai": "^4.3.6", "chai-subset": "^1.6.0", "mocha": "^10.1.0", - "multiformats": "^11.0.0", "nyc": "^15.1.0", "playwright-test": "^8.2.0", "typescript": "^4.9.5" diff --git a/packages/server/test/handler.spec.js b/packages/server/test/handler.spec.js index 5e0e22fe..0089228a 100644 --- a/packages/server/test/handler.spec.js +++ b/packages/server/test/handler.spec.js @@ -2,7 +2,6 @@ import * as Client from '@ucanto/client' import * as Server from '../src/server.js' import * as Provider from '../src/handler.js' import * as CAR from '@ucanto/transport/car' -import * as CBOR from '@ucanto/transport/cbor' import * as API from '@ucanto/interface' import { alice, bob, mallory, service } from './fixtures.js' import { test, assert } from './test.js' diff --git a/packages/server/test/server.spec.js b/packages/server/test/server.spec.js index e831e66a..51b5bf4b 100644 --- a/packages/server/test/server.spec.js +++ b/packages/server/test/server.spec.js @@ -1,7 +1,7 @@ import * as Client from '@ucanto/client' import * as Server from '../src/lib.js' import * as CAR from '@ucanto/transport/car' -import * as CBOR from '@ucanto/transport/cbor' +import * as CBOR from '@ucanto/core/cbor' import * as Transport from '@ucanto/transport' import { alice, bob, mallory, service as w3 } from './fixtures.js' import * as Service from '../../client/test/service.js' @@ -63,7 +63,7 @@ const store = storeAdd.or(storeRemove) test('encode delegated invocation', async () => { const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world ' })], + roots: [await CBOR.write({ hello: 'world ' })], }) const server = Server.create({ @@ -298,7 +298,7 @@ test('execution error', async () => { test('did:web server', async () => { const car = await CAR.codec.write({ - roots: [await CBOR.codec.write({ hello: 'world ' })], + roots: [await CBOR.write({ hello: 'world ' })], }) const server = Server.create({ diff --git a/packages/transport/package.json b/packages/transport/package.json index 71c886db..851ac77e 100644 --- a/packages/transport/package.json +++ b/packages/transport/package.json @@ -29,11 +29,8 @@ "build": "tsc --build" }, "dependencies": { - "@ipld/car": "^5.1.0", - "@ipld/dag-cbor": "^9.0.0", "@ucanto/core": "workspace:^", - "@ucanto/interface": "workspace:^", - "multiformats": "^11.0.0" + "@ucanto/interface": "workspace:^" }, "devDependencies": { "@types/chai": "^4.3.3", @@ -69,10 +66,6 @@ "types": "./dist/src/car.d.ts", "import": "./src/car.js" }, - "./cbor": { - "types": "./dist/src/cbor.d.ts", - "import": "./src/cbor.js" - }, "./http": { "types": "./dist/src/http.d.ts", "import": "./src/http.js" diff --git a/packages/transport/src/car.js b/packages/transport/src/car.js index 7a66bfc2..11b63950 100644 --- a/packages/transport/src/car.js +++ b/packages/transport/src/car.js @@ -1,8 +1,8 @@ import * as API from '@ucanto/interface' -import * as CAR from './car/codec.js' +import { CAR } from '@ucanto/core' import * as request from './car/request.js' import * as response from './car/response.js' -import * as Selector from './codec.js' +import * as Codec from './codec.js' export { CAR as codec, request, response } @@ -27,7 +27,7 @@ export const encode = (invocations, options) => */ export const decode = request.decode -export const inbound = Selector.inbound({ +export const inbound = Codec.inbound({ decoders: { 'application/car': request, }, @@ -36,7 +36,7 @@ export const inbound = Selector.inbound({ }, }) -export const outbound = Selector.outbound({ +export const outbound = Codec.outbound({ encoders: { 'application/car': request, }, diff --git a/packages/transport/src/car/request.js b/packages/transport/src/car/request.js index 67e33981..65cbd32b 100644 --- a/packages/transport/src/car/request.js +++ b/packages/transport/src/car/request.js @@ -1,5 +1,5 @@ import * as API from '@ucanto/interface' -import * as CAR from './codec.js' +import { CAR } from '@ucanto/core' import { Delegation } from '@ucanto/core' export { CAR as codec } diff --git a/packages/transport/src/car/response.js b/packages/transport/src/car/response.js index 3e0ecfd3..c88dd8ef 100644 --- a/packages/transport/src/car/response.js +++ b/packages/transport/src/car/response.js @@ -1,5 +1,5 @@ import * as API from '@ucanto/interface' -import * as CAR from './codec.js' +import { CAR } from '@ucanto/core' import { Receipt } from '@ucanto/core' export { CAR as codec } diff --git a/packages/transport/src/cbor.js b/packages/transport/src/cbor.js deleted file mode 100644 index 40cdf273..00000000 --- a/packages/transport/src/cbor.js +++ /dev/null @@ -1,42 +0,0 @@ -import * as API from '@ucanto/interface' -import * as CBOR from '@ucanto/core/cbor' - -const HEADERS = Object.freeze({ - 'content-type': 'application/cbor', -}) - -export const codec = CBOR - -/** - * Encodes invocation batch into an HTTPRequest. - * - * @template I - * @param {I} result - * @returns {API.HTTPResponse} - * @deprecated - */ -export const encode = result => { - return { - headers: HEADERS, - body: CBOR.encode(result), - } -} - -/** - * Decodes HTTPRequest to an invocation batch. - * - * @template I - * @param {API.HTTPResponse} request - * @returns {Promise} - * @deprecated - */ -export const decode = async ({ headers, body }) => { - const contentType = headers['content-type'] || headers['Content-Type'] - if (contentType !== 'application/cbor') { - throw TypeError( - `Only 'content-type: application/cbor' is supported, instead got '${contentType}'` - ) - } - - return CBOR.decode(body) -} diff --git a/packages/transport/src/jwt.js b/packages/transport/src/jwt.js index 06656973..195e9a6b 100644 --- a/packages/transport/src/jwt.js +++ b/packages/transport/src/jwt.js @@ -1,6 +1,6 @@ import * as API from '@ucanto/interface' import * as UTF8 from './utf8.js' -import { Delegation, isLink, UCAN } from '@ucanto/core' +import { Delegation, UCAN } from '@ucanto/core' const HEADER_PREFIX = 'x-auth-' diff --git a/packages/transport/src/legacy.js b/packages/transport/src/legacy.js index 17176052..f6800b5e 100644 --- a/packages/transport/src/legacy.js +++ b/packages/transport/src/legacy.js @@ -1,5 +1,5 @@ import * as API from '@ucanto/interface' -import * as Selector from './codec.js' +import * as Codec from './codec.js' import * as CAR from './car.js' import { encode as encodeCBOR } from '@ucanto/core/cbor' @@ -32,7 +32,7 @@ export const CBOR = { }, } -export const inbound = Selector.inbound({ +export const inbound = Codec.inbound({ decoders: { 'application/car': CAR.request, }, diff --git a/packages/transport/src/lib.js b/packages/transport/src/lib.js index 20fb6b90..3705784a 100644 --- a/packages/transport/src/lib.js +++ b/packages/transport/src/lib.js @@ -1,4 +1,3 @@ -export * as CBOR from './cbor.js' export * as CAR from './car.js' export * as JWT from './jwt.js' export * as HTTP from './http.js' diff --git a/packages/transport/test/car.spec.js b/packages/transport/test/car.spec.js index ab882054..61ad25b9 100644 --- a/packages/transport/test/car.spec.js +++ b/packages/transport/test/car.spec.js @@ -1,6 +1,5 @@ import { test, assert } from './test.js' import * as CAR from '../src/car.js' -import * as CBOR from '../src/cbor.js' import { delegate, invoke, @@ -8,10 +7,8 @@ import { Delegation, UCAN, parseLink, - isLink, } from '@ucanto/core' -import { alice, bob, mallory, service } from './fixtures.js' -import { CarReader } from '@ipld/car/reader' +import { alice, bob, service } from './fixtures.js' import { collect } from './util.js' test('encode / decode', async () => { @@ -36,13 +33,6 @@ test('encode / decode', async () => { assert.deepEqual(request.headers, { 'content-type': 'application/car', }) - const reader = await CarReader.fromBytes(request.body) - - assert.deepEqual( - await reader.getRoots(), - // @ts-expect-error - CAR refers to old CID - [cid] - ) const expect = await Delegation.delegate({ issuer: alice, @@ -153,12 +143,6 @@ test('delegated proofs', async () => { }), ]) - const reader = await CarReader.fromBytes(outgoing.body) - const cids = await collect(reader.cids()) - assert.equal(cids.length, 2) - const roots = await reader.getRoots() - assert.equal(roots.length, 1) - const incoming = await CAR.decode(outgoing) assert.deepEqual(incoming, [ @@ -206,10 +190,6 @@ test('omit proof', async () => { }), ]) - const reader = await CarReader.fromBytes(outgoing.body) - const cids = await collect(reader.cids()) - assert.equal(cids.length, 1) - const incoming = await CAR.decode(outgoing) assert.deepEqual(incoming, [ @@ -230,34 +210,6 @@ test('omit proof', async () => { assert.deepEqual(incoming[0].proofs, [proof.cid]) }) -test('codec', async () => { - const root = await CBOR.codec.write({ hello: 'world ' }) - const bytes = CAR.codec.encode({ - roots: [root], - }) - const { blocks, roots } = await CAR.codec.decode(bytes) - assert.equal(blocks.size, 1) - assert.deepEqual(roots, [root]) - - const car = await CAR.codec.write({ roots: [root] }) - assert.deepEqual(car.bytes, bytes) - assert.equal(isLink(car.cid), true) - - const link = await CAR.codec.link(car.bytes) - assert.deepEqual(car.cid, link) -}) - -test('car writer', async () => { - const hello = await CBOR.codec.write({ hello: 'world ' }) - const writer = CAR.codec.createWriter() - writer.write(hello) - const bytes = writer.flush() - - const car = await CAR.codec.decode(bytes) - assert.deepEqual(car.roots, []) - assert.deepEqual([...car.blocks], [[hello.cid.toString(), hello]]) -}) - test('CAR.request encode / decode', async () => { const cid = parseLink( 'bafyreiaxnmoptsqiehdff2blpptvdbenxcz6xgrbojw5em36xovn2xea4y' @@ -281,13 +233,6 @@ test('CAR.request encode / decode', async () => { 'content-type': 'application/car', accept: 'application/car', }) - const reader = await CarReader.fromBytes(request.body) - - assert.deepEqual( - await reader.getRoots(), - // @ts-expect-error - CAR refers to old CID - [cid] - ) const expect = await Delegation.delegate({ issuer: alice, @@ -329,13 +274,6 @@ test('CAR.response encode/decode', async () => { 'content-type': 'application/car', }) - const reader = await CarReader.fromBytes(message.body) - assert.deepEqual( - await reader.getRoots(), - // @ts-expect-error - CAR refers to old CID - [receipt.root.cid] - ) - const [received, ...other] = await CAR.response.decode(message) assert.equal(other.length, 0) assert.deepEqual(received.issuer, receipt.issuer) diff --git a/packages/transport/test/cbor.spec.js b/packages/transport/test/cbor.spec.js deleted file mode 100644 index a782381e..00000000 --- a/packages/transport/test/cbor.spec.js +++ /dev/null @@ -1,102 +0,0 @@ -import { test, assert } from './test.js' -import * as CBOR from '../src/cbor.js' -import { decode, encode } from '@ipld/dag-cbor' -import * as UTF8 from '../src/utf8.js' -test('encode / decode', async () => { - // @ts-ignore - const response = CBOR.encode([{ ok: true, value: 1 }]) - - assert.deepEqual(response, { - headers: { - 'content-type': 'application/cbor', - }, - body: encode([{ ok: true, value: 1 }]), - }) - - assert.deepEqual(await CBOR.decode(response), [{ ok: true, value: 1 }]) -}) - -test('throws on wrong content-type', async () => { - try { - await CBOR.decode({ - headers: { 'content-type': 'application/octet-stream' }, - body: encode([{ ok: true, value: 1 }]), - }) - assert.fail('should have failed') - } catch (error) { - assert.match(String(error), /application\/cbor/) - } -}) - -test('content-type case', async () => { - assert.deepEqual( - await CBOR.decode({ - headers: { 'Content-Type': 'application/cbor' }, - body: encode([{ ok: true, value: 1 }]), - }), - [{ ok: true, value: 1 }] - ) -}) - -{ - const { encode, decode, write } = CBOR.codec - - /** - * @template T - * @param {T} value - */ - const transcode = value => decode(encode(value)) - - const dataset = [ - undefined, - null, - Symbol('hello'), - [1, , 3], - { x: 1, y: undefined }, - { x: 3, p: Symbol('hi') }, - { - x: 1, - y: 2, - toJSON() { - return [1, 2] - }, - }, - ] - - for (const data of dataset) { - test(`encode / decode ${JSON.stringify(data)}`, async () => { - const actual = transcode(data) - const expect = JSON.parse(JSON.stringify(data) || 'null') - assert.deepEqual(actual, expect) - }) - } - - test(`encode / decode bytes`, async () => { - const actual = transcode({ bytes: UTF8.encode('hello') }) - assert.deepEqual(actual, { bytes: UTF8.encode('hello') }) - }) - - test('circular objects throw', () => { - const circular = { a: 1, circle: {} } - circular.circle = circular - - const nested = { pointer: {} } - const structure = { - x: 1, - sub: { - items: [1, nested], - }, - } - nested.pointer = structure - - assert.throws(() => transcode(nested), /Can not encode circular structure/) - }) - - test('cids', async () => { - const hello = await write({ hello: 'world' }) - - assert.deepEqual(transcode({ message: hello.cid }), { - message: hello.cid, - }) - }) -} diff --git a/packages/transport/test/codec.spec.js b/packages/transport/test/codec.spec.js index 89d37cc7..82d555c1 100644 --- a/packages/transport/test/codec.spec.js +++ b/packages/transport/test/codec.spec.js @@ -1,9 +1,8 @@ import { test, assert } from './test.js' import * as CAR from '../src/car.js' import * as Transport from '../src/lib.js' -import { alice, bob, mallory, service } from './fixtures.js' -import { invoke, API, delegate, parseLink, Receipt } from '@ucanto/core' -import { CarReader } from '@ipld/car/reader' +import { alice, bob } from './fixtures.js' +import { invoke, delegate, parseLink, Receipt } from '@ucanto/core' test('unsupported inbound content-type', async () => { const accept = CAR.inbound.accept({ @@ -87,13 +86,6 @@ test('outbound encode', async () => { 'content-type': 'application/car', accept: 'application/car', }) - const reader = await CarReader.fromBytes(request.body) - - assert.deepEqual( - await reader.getRoots(), - // @ts-expect-error - CAR refers to old CID - [cid] - ) const expect = await delegate({ issuer: alice, diff --git a/packages/transport/test/https.spec.js b/packages/transport/test/https.spec.js index 2027dfe9..5f8bfac6 100644 --- a/packages/transport/test/https.spec.js +++ b/packages/transport/test/https.spec.js @@ -2,7 +2,6 @@ import { test, assert } from './test.js' import * as HTTP from '../src/http.js' import * as UTF8 from '../src/utf8.js' -import { CAR, JWT, CBOR } from '../src/lib.js' test('encode / decode', async () => { const channel = HTTP.open({ url: new URL('about:blank'), diff --git a/packages/transport/test/jwt.spec.js b/packages/transport/test/jwt.spec.js index 9c03c82c..14d91432 100644 --- a/packages/transport/test/jwt.spec.js +++ b/packages/transport/test/jwt.spec.js @@ -2,9 +2,7 @@ import { test, assert } from './test.js' import * as JWT from '../src/jwt.js' import { delegate, invoke, Delegation, UCAN } from '@ucanto/core' import * as UTF8 from '../src/utf8.js' -import { alice, bob, mallory, service } from './fixtures.js' -import * as API from '@ucanto/interface' -import { base64url } from 'multiformats/bases/base64' +import { alice, bob, service } from './fixtures.js' const NOW = 1654298135 diff --git a/packages/transport/test/legacy.spec.js b/packages/transport/test/legacy.spec.js index 3279a821..834f4467 100644 --- a/packages/transport/test/legacy.spec.js +++ b/packages/transport/test/legacy.spec.js @@ -1,17 +1,8 @@ import { test, assert } from './test.js' import * as CAR from '../src/car.js' -import * as CBOR from '../src/cbor.js' import * as Legacy from '../src/legacy.js' -import { - delegate, - invoke, - Receipt, - Delegation, - UCAN, - parseLink, - isLink, -} from '@ucanto/core' -import { alice, bob, mallory, service } from './fixtures.js' +import { invoke, Receipt, Delegation, CBOR } from '@ucanto/core' +import { alice, bob } from './fixtures.js' test('Legacy decode / encode', async () => { const expiration = 1654298135 @@ -68,11 +59,10 @@ test('Legacy decode / encode', async () => { }) const response = await encoder.encode([success, failure]) - const results = await CBOR.decode(response) + const results = await CBOR.decode(response.body) assert.deepEqual( results, - // @ts-expect-error - This not according to the types but it is what // we want to return to old clients. [{ hello: 'message' }, { error: true, message: 'Boom' }], 'roundtrips' diff --git a/packages/validator/src/error.js b/packages/validator/src/error.js index 0d3d5889..f23d30b0 100644 --- a/packages/validator/src/error.js +++ b/packages/validator/src/error.js @@ -1,6 +1,6 @@ import * as API from '@ucanto/interface' import { the } from './util.js' -import { isLink } from 'multiformats/link' +import { isLink } from '@ucanto/core/link' /** * @implements {API.Failure} diff --git a/packages/validator/src/schema/link.js b/packages/validator/src/schema/link.js index 2d91d351..a2f314c3 100644 --- a/packages/validator/src/schema/link.js +++ b/packages/validator/src/schema/link.js @@ -1,6 +1,5 @@ import * as API from '@ucanto/interface' -import { create, createLegacy, isLink, parse } from '@ucanto/core/link' -import { base32 } from 'multiformats/bases/base32' +import { create, createLegacy, isLink, parse, base32 } from '@ucanto/core/link' import * as Schema from './schema.js' export { create, createLegacy, isLink, parse } diff --git a/packages/validator/test/delegate.spec.js b/packages/validator/test/delegate.spec.js index 518cfa46..172559be 100644 --- a/packages/validator/test/delegate.spec.js +++ b/packages/validator/test/delegate.spec.js @@ -1,11 +1,9 @@ -import { capability, DID, URI, Link, unknown, Schema } from '../src/lib.js' -import { invoke, parseLink, delegate, UCAN } from '@ucanto/core' +import { capability, DID, URI, Link, Schema } from '../src/lib.js' +import { parseLink, delegate, UCAN } from '@ucanto/core' import * as API from '@ucanto/interface' import { Failure } from '../src/error.js' -import { the } from '../src/util.js' -import { CID } from 'multiformats' import { test, assert } from './test.js' -import { alice, bob, mallory, service as w3 } from './fixtures.js' +import { alice, service as w3 } from './fixtures.js' const echo = capability({ can: 'test/echo', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cff3674a..ccf471d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,247 +1,323 @@ -lockfileVersion: 5.3 +lockfileVersion: '6.0' importers: .: - specifiers: - mocha: ^10.1.0 - prettier: 2.8.4 - typescript: ^4.9.5 devDependencies: - mocha: 10.2.0 - prettier: 2.8.4 - typescript: 4.9.5 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + prettier: + specifier: 2.8.4 + version: 2.8.4 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/client: - specifiers: - '@types/chai': ^4.3.3 - '@types/mocha': ^10.0.1 - '@ucanto/core': workspace:^ - '@ucanto/interface': workspace:^ - '@ucanto/principal': workspace:^ - '@ucanto/transport': workspace:^ - '@web-std/fetch': ^4.1.0 - '@web-std/file': ^3.0.2 - c8: ^7.13.0 - chai: ^4.3.6 - mocha: ^10.1.0 - multiformats: ^11.0.0 - nyc: ^15.1.0 - playwright-test: ^8.2.0 - typescript: ^4.9.5 - dependencies: - '@ucanto/interface': link:../interface - multiformats: 11.0.1 + dependencies: + '@ucanto/core': + specifier: workspace:^ + version: link:../core + '@ucanto/interface': + specifier: workspace:^ + version: link:../interface devDependencies: - '@types/chai': 4.3.4 - '@types/mocha': 10.0.1 - '@ucanto/core': link:../core - '@ucanto/principal': link:../principal - '@ucanto/transport': link:../transport - '@web-std/fetch': 4.1.0 - '@web-std/file': 3.0.2 - c8: 7.13.0 - chai: 4.3.7 - mocha: 10.2.0 - nyc: 15.1.0 - playwright-test: 8.2.0 - typescript: 4.9.5 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.4 + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + '@ucanto/principal': + specifier: workspace:^ + version: link:../principal + '@ucanto/transport': + specifier: workspace:^ + version: link:../transport + '@web-std/fetch': + specifier: ^4.1.0 + version: 4.1.0 + '@web-std/file': + specifier: ^3.0.2 + version: 3.0.2 + c8: + specifier: ^7.13.0 + version: 7.13.0 + chai: + specifier: ^4.3.6 + version: 4.3.7 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + playwright-test: + specifier: ^8.2.0 + version: 8.2.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/core: - specifiers: - '@ipld/car': ^5.1.0 - '@ipld/dag-cbor': ^9.0.0 - '@ipld/dag-ucan': ^3.2.0 - '@types/chai': ^4.3.3 - '@types/mocha': ^10.0.1 - '@ucanto/interface': workspace:^ - '@ucanto/principal': workspace:^ - c8: ^7.13.0 - chai: ^4.3.6 - mocha: ^10.1.0 - multiformats: ^11.0.0 - nyc: ^15.1.0 - playwright-test: ^8.2.0 - typescript: ^4.9.5 - dependencies: - '@ipld/car': 5.1.0 - '@ipld/dag-cbor': 9.0.0 - '@ipld/dag-ucan': 3.2.0 - '@ucanto/interface': link:../interface - multiformats: 11.0.1 + dependencies: + '@ipld/car': + specifier: ^5.1.0 + version: 5.1.0 + '@ipld/dag-cbor': + specifier: ^9.0.0 + version: 9.0.0 + '@ipld/dag-ucan': + specifier: ^3.2.0 + version: 3.2.0 + '@ucanto/interface': + specifier: workspace:^ + version: link:../interface + multiformats: + specifier: ^11.0.0 + version: 11.0.1 devDependencies: - '@types/chai': 4.3.4 - '@types/mocha': 10.0.1 - '@ucanto/principal': link:../principal - c8: 7.13.0 - chai: 4.3.7 - mocha: 10.2.0 - nyc: 15.1.0 - playwright-test: 8.2.0 - typescript: 4.9.5 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.4 + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + '@ucanto/principal': + specifier: workspace:^ + version: link:../principal + c8: + specifier: ^7.13.0 + version: 7.13.0 + chai: + specifier: ^4.3.6 + version: 4.3.7 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + playwright-test: + specifier: ^8.2.0 + version: 8.2.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/interface: - specifiers: - '@ipld/dag-ucan': ^3.2.0 - multiformats: ^11.0.0 - typescript: ^4.9.5 dependencies: - '@ipld/dag-ucan': 3.2.0 - multiformats: 11.0.1 + '@ipld/dag-ucan': + specifier: ^3.2.0 + version: 3.2.0 + multiformats: + specifier: ^11.0.0 + version: 11.0.1 devDependencies: - typescript: 4.9.5 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/principal: - specifiers: - '@ipld/dag-ucan': ^3.2.0 - '@noble/ed25519': ^1.7.3 - '@types/chai': ^4.3.3 - '@types/mocha': ^10.0.1 - '@ucanto/interface': workspace:^ - c8: ^7.13.0 - chai: ^4.3.6 - mocha: ^10.1.0 - multiformats: ^11.0.0 - nyc: ^15.1.0 - one-webcrypto: ^1.0.3 - playwright-test: ^8.2.0 - typescript: ^4.9.5 - dependencies: - '@ipld/dag-ucan': 3.2.0 - '@noble/ed25519': 1.7.3 - '@ucanto/interface': link:../interface - multiformats: 11.0.1 - one-webcrypto: 1.0.3 + dependencies: + '@ipld/dag-ucan': + specifier: ^3.2.0 + version: 3.2.0 + '@noble/ed25519': + specifier: ^1.7.3 + version: 1.7.3 + '@ucanto/interface': + specifier: workspace:^ + version: link:../interface + multiformats: + specifier: ^11.0.0 + version: 11.0.1 + one-webcrypto: + specifier: ^1.0.3 + version: 1.0.3 devDependencies: - '@types/chai': 4.3.4 - '@types/mocha': 10.0.1 - c8: 7.13.0 - chai: 4.3.7 - mocha: 10.2.0 - nyc: 15.1.0 - playwright-test: 8.2.0 - typescript: 4.9.5 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.4 + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + c8: + specifier: ^7.13.0 + version: 7.13.0 + chai: + specifier: ^4.3.6 + version: 4.3.7 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + playwright-test: + specifier: ^8.2.0 + version: 8.2.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/server: - specifiers: - '@types/chai': ^4.3.3 - '@types/chai-subset': ^1.3.3 - '@types/mocha': ^10.0.1 - '@ucanto/client': workspace:^ - '@ucanto/core': workspace:^ - '@ucanto/interface': workspace:^ - '@ucanto/principal': workspace:^ - '@ucanto/transport': workspace:^ - '@ucanto/validator': workspace:^ - '@web-std/fetch': ^4.1.0 - '@web-std/file': ^3.0.2 - c8: ^7.13.0 - chai: ^4.3.6 - chai-subset: ^1.6.0 - mocha: ^10.1.0 - multiformats: ^11.0.0 - nyc: ^15.1.0 - playwright-test: ^8.2.0 - typescript: ^4.9.5 - dependencies: - '@ucanto/core': link:../core - '@ucanto/interface': link:../interface - '@ucanto/validator': link:../validator + dependencies: + '@ucanto/core': + specifier: workspace:^ + version: link:../core + '@ucanto/interface': + specifier: workspace:^ + version: link:../interface + '@ucanto/validator': + specifier: workspace:^ + version: link:../validator devDependencies: - '@types/chai': 4.3.4 - '@types/chai-subset': 1.3.3 - '@types/mocha': 10.0.1 - '@ucanto/client': link:../client - '@ucanto/principal': link:../principal - '@ucanto/transport': link:../transport - '@web-std/fetch': 4.1.0 - '@web-std/file': 3.0.2 - c8: 7.13.0 - chai: 4.3.7 - chai-subset: 1.6.0 - mocha: 10.2.0 - multiformats: 11.0.1 - nyc: 15.1.0 - playwright-test: 8.2.0 - typescript: 4.9.5 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.4 + '@types/chai-subset': + specifier: ^1.3.3 + version: 1.3.3 + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + '@ucanto/client': + specifier: workspace:^ + version: link:../client + '@ucanto/principal': + specifier: workspace:^ + version: link:../principal + '@ucanto/transport': + specifier: workspace:^ + version: link:../transport + '@web-std/fetch': + specifier: ^4.1.0 + version: 4.1.0 + '@web-std/file': + specifier: ^3.0.2 + version: 3.0.2 + c8: + specifier: ^7.13.0 + version: 7.13.0 + chai: + specifier: ^4.3.6 + version: 4.3.7 + chai-subset: + specifier: ^1.6.0 + version: 1.6.0 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + playwright-test: + specifier: ^8.2.0 + version: 8.2.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/transport: - specifiers: - '@ipld/car': ^5.1.0 - '@ipld/dag-cbor': ^9.0.0 - '@types/chai': ^4.3.3 - '@types/mocha': ^10.0.1 - '@ucanto/core': workspace:^ - '@ucanto/interface': workspace:^ - '@ucanto/principal': workspace:^ - '@web-std/fetch': ^4.1.0 - c8: ^7.13.0 - chai: ^4.3.6 - mocha: ^10.1.0 - multiformats: ^11.0.0 - nyc: ^15.1.0 - playwright-test: ^8.2.0 - typescript: ^4.9.5 - dependencies: - '@ipld/car': 5.1.0 - '@ipld/dag-cbor': 9.0.0 - '@ucanto/core': link:../core - '@ucanto/interface': link:../interface - multiformats: 11.0.1 + dependencies: + '@ucanto/core': + specifier: workspace:^ + version: link:../core + '@ucanto/interface': + specifier: workspace:^ + version: link:../interface devDependencies: - '@types/chai': 4.3.4 - '@types/mocha': 10.0.1 - '@ucanto/principal': link:../principal - '@web-std/fetch': 4.1.0 - c8: 7.13.0 - chai: 4.3.7 - mocha: 10.2.0 - nyc: 15.1.0 - playwright-test: 8.2.0 - typescript: 4.9.5 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.4 + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + '@ucanto/principal': + specifier: workspace:^ + version: link:../principal + '@web-std/fetch': + specifier: ^4.1.0 + version: 4.1.0 + c8: + specifier: ^7.13.0 + version: 7.13.0 + chai: + specifier: ^4.3.6 + version: 4.3.7 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + playwright-test: + specifier: ^8.2.0 + version: 8.2.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages/validator: - specifiers: - '@ipld/car': ^5.1.0 - '@ipld/dag-cbor': ^9.0.0 - '@types/chai': ^4.3.3 - '@types/chai-subset': ^1.3.3 - '@types/mocha': ^10.0.1 - '@ucanto/client': workspace:^ - '@ucanto/core': workspace:^ - '@ucanto/interface': workspace:^ - '@ucanto/principal': workspace:^ - c8: ^7.13.0 - chai: ^4.3.6 - chai-subset: ^1.6.0 - mocha: ^10.1.0 - multiformats: ^11.0.0 - nyc: ^15.1.0 - playwright-test: ^8.2.0 - typescript: ^4.9.5 - dependencies: - '@ipld/car': 5.1.0 - '@ipld/dag-cbor': 9.0.0 - '@ucanto/core': link:../core - '@ucanto/interface': link:../interface - multiformats: 11.0.1 + dependencies: + '@ipld/car': + specifier: ^5.1.0 + version: 5.1.0 + '@ipld/dag-cbor': + specifier: ^9.0.0 + version: 9.0.0 + '@ucanto/core': + specifier: workspace:^ + version: link:../core + '@ucanto/interface': + specifier: workspace:^ + version: link:../interface + multiformats: + specifier: ^11.0.0 + version: 11.0.1 devDependencies: - '@types/chai': 4.3.4 - '@types/chai-subset': 1.3.3 - '@types/mocha': 10.0.1 - '@ucanto/client': link:../client - '@ucanto/principal': link:../principal - c8: 7.13.0 - chai: 4.3.7 - chai-subset: 1.6.0 - mocha: 10.2.0 - nyc: 15.1.0 - playwright-test: 8.2.0 - typescript: 4.9.5 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.4 + '@types/chai-subset': + specifier: ^1.3.3 + version: 1.3.3 + '@types/mocha': + specifier: ^10.0.1 + version: 10.0.1 + '@ucanto/client': + specifier: workspace:^ + version: link:../client + '@ucanto/principal': + specifier: workspace:^ + version: link:../principal + c8: + specifier: ^7.13.0 + version: 7.13.0 + chai: + specifier: ^4.3.6 + version: 4.3.7 + chai-subset: + specifier: ^1.6.0 + version: 1.6.0 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + playwright-test: + specifier: ^8.2.0 + version: 8.2.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 packages: - /@ampproject/remapping/2.2.0: + /@ampproject/remapping@2.2.0: resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} engines: {node: '>=6.0.0'} dependencies: @@ -249,31 +325,31 @@ packages: '@jridgewell/trace-mapping': 0.3.17 dev: true - /@arr/every/1.0.1: + /@arr/every@1.0.1: resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} engines: {node: '>=4'} dev: true - /@babel/code-frame/7.18.6: + /@babel/code-frame@7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 dev: true - /@babel/compat-data/7.20.10: + /@babel/compat-data@7.20.10: resolution: {integrity: sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==} engines: {node: '>=6.9.0'} dev: true - /@babel/core/7.20.12: + /@babel/core@7.20.12: resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 '@babel/generator': 7.20.7 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-module-transforms': 7.20.11 '@babel/helpers': 7.20.7 '@babel/parser': 7.20.7 @@ -281,7 +357,7 @@ packages: '@babel/traverse': 7.20.12 '@babel/types': 7.20.7 convert-source-map: 1.9.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.0 @@ -289,7 +365,7 @@ packages: - supports-color dev: true - /@babel/generator/7.20.7: + /@babel/generator@7.20.7: resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==} engines: {node: '>=6.9.0'} dependencies: @@ -298,7 +374,7 @@ packages: jsesc: 2.5.2 dev: true - /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12: + /@babel/helper-compilation-targets@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -312,12 +388,12 @@ packages: semver: 6.3.0 dev: true - /@babel/helper-environment-visitor/7.18.9: + /@babel/helper-environment-visitor@7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-function-name/7.19.0: + /@babel/helper-function-name@7.19.0: resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} engines: {node: '>=6.9.0'} dependencies: @@ -325,21 +401,21 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/helper-hoist-variables/7.18.6: + /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-module-imports/7.18.6: + /@babel/helper-module-imports@7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-module-transforms/7.20.11: + /@babel/helper-module-transforms@7.20.11: resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==} engines: {node: '>=6.9.0'} dependencies: @@ -355,36 +431,36 @@ packages: - supports-color dev: true - /@babel/helper-simple-access/7.20.2: + /@babel/helper-simple-access@7.20.2: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-split-export-declaration/7.18.6: + /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-string-parser/7.19.4: + /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-validator-identifier/7.19.1: + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-validator-option/7.18.6: + /@babel/helper-validator-option@7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} engines: {node: '>=6.9.0'} dev: true - /@babel/helpers/7.20.7: + /@babel/helpers@7.20.7: resolution: {integrity: sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==} engines: {node: '>=6.9.0'} dependencies: @@ -395,7 +471,7 @@ packages: - supports-color dev: true - /@babel/highlight/7.18.6: + /@babel/highlight@7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: @@ -404,7 +480,7 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser/7.20.7: + /@babel/parser@7.20.7: resolution: {integrity: sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==} engines: {node: '>=6.0.0'} hasBin: true @@ -412,7 +488,7 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/template/7.20.7: + /@babel/template@7.20.7: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} engines: {node: '>=6.9.0'} dependencies: @@ -421,7 +497,7 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/traverse/7.20.12: + /@babel/traverse@7.20.12: resolution: {integrity: sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==} engines: {node: '>=6.9.0'} dependencies: @@ -433,13 +509,13 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.20.7 '@babel/types': 7.20.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types/7.20.7: + /@babel/types@7.20.7: resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==} engines: {node: '>=6.9.0'} dependencies: @@ -448,29 +524,29 @@ packages: to-fast-properties: 2.0.0 dev: true - /@bcoe/v8-coverage/0.2.3: + /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@esbuild/android-arm/0.16.10: - resolution: {integrity: sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==} + /@esbuild/android-arm64@0.16.10: + resolution: {integrity: sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-arm64/0.16.10: - resolution: {integrity: sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw==} + /@esbuild/android-arm@0.16.10: + resolution: {integrity: sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==} engines: {node: '>=12'} - cpu: [arm64] + cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-x64/0.16.10: + /@esbuild/android-x64@0.16.10: resolution: {integrity: sha512-C4PfnrBMcuAcOurQzpF1tTtZz94IXO5JmICJJ3NFJRHbXXsQUg9RFG45KvydKqtFfBaFLCHpduUkUfXwIvGnRg==} engines: {node: '>=12'} cpu: [x64] @@ -479,7 +555,7 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64/0.16.10: + /@esbuild/darwin-arm64@0.16.10: resolution: {integrity: sha512-bH/bpFwldyOKdi9HSLCLhhKeVgRYr9KblchwXgY2NeUHBB/BzTUHtUSBgGBmpydB1/4E37m+ggXXfSrnD7/E7g==} engines: {node: '>=12'} cpu: [arm64] @@ -488,7 +564,7 @@ packages: dev: true optional: true - /@esbuild/darwin-x64/0.16.10: + /@esbuild/darwin-x64@0.16.10: resolution: {integrity: sha512-OXt7ijoLuy+AjDSKQWu+KdDFMBbdeaL6wtgMKtDUXKWHiAMKHan5+R1QAG6HD4+K0nnOvEJXKHeA9QhXNAjOTQ==} engines: {node: '>=12'} cpu: [x64] @@ -497,7 +573,7 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64/0.16.10: + /@esbuild/freebsd-arm64@0.16.10: resolution: {integrity: sha512-shSQX/3GHuspE3Uxtq5kcFG/zqC+VuMnJkqV7LczO41cIe6CQaXHD3QdMLA4ziRq/m0vZo7JdterlgbmgNIAlQ==} engines: {node: '>=12'} cpu: [arm64] @@ -506,7 +582,7 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64/0.16.10: + /@esbuild/freebsd-x64@0.16.10: resolution: {integrity: sha512-5YVc1zdeaJGASijZmTzSO4h6uKzsQGG3pkjI6fuXvolhm3hVRhZwnHJkforaZLmzvNv5Tb7a3QL2FAVmrgySIA==} engines: {node: '>=12'} cpu: [x64] @@ -515,25 +591,25 @@ packages: dev: true optional: true - /@esbuild/linux-arm/0.16.10: - resolution: {integrity: sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA==} + /@esbuild/linux-arm64@0.16.10: + resolution: {integrity: sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-arm64/0.16.10: - resolution: {integrity: sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA==} + /@esbuild/linux-arm@0.16.10: + resolution: {integrity: sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA==} engines: {node: '>=12'} - cpu: [arm64] + cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-ia32/0.16.10: + /@esbuild/linux-ia32@0.16.10: resolution: {integrity: sha512-sqMIEWeyrLGU7J5RB5fTkLRIFwsgsQ7ieWXlDLEmC2HblPYGb3AucD7inw2OrKFpRPKsec1l+lssiM3+NV5aOw==} engines: {node: '>=12'} cpu: [ia32] @@ -542,7 +618,7 @@ packages: dev: true optional: true - /@esbuild/linux-loong64/0.16.10: + /@esbuild/linux-loong64@0.16.10: resolution: {integrity: sha512-O7Pd5hLEtTg37NC73pfhUOGTjx/+aXu5YoSq3ahCxcN7Bcr2F47mv+kG5t840thnsEzrv0oB70+LJu3gUgchvg==} engines: {node: '>=12'} cpu: [loong64] @@ -551,7 +627,7 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el/0.16.10: + /@esbuild/linux-mips64el@0.16.10: resolution: {integrity: sha512-FN8mZOH7531iPHM0kaFhAOqqNHoAb6r/YHW2ZIxNi0a85UBi2DO4Vuyn7t1p4UN8a4LoAnLOT1PqNgHkgBJgbA==} engines: {node: '>=12'} cpu: [mips64el] @@ -560,7 +636,7 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64/0.16.10: + /@esbuild/linux-ppc64@0.16.10: resolution: {integrity: sha512-Dg9RiqdvHOAWnOKIOTsIx8dFX9EDlY2IbPEY7YFzchrCiTZmMkD7jWA9UdZbNUygPjdmQBVPRCrLydReFlX9yg==} engines: {node: '>=12'} cpu: [ppc64] @@ -569,7 +645,7 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64/0.16.10: + /@esbuild/linux-riscv64@0.16.10: resolution: {integrity: sha512-XMqtpjwzbmlar0BJIxmzu/RZ7EWlfVfH68Vadrva0Wj5UKOdKvqskuev2jY2oPV3aoQUyXwnMbMrFmloO2GfAw==} engines: {node: '>=12'} cpu: [riscv64] @@ -578,7 +654,7 @@ packages: dev: true optional: true - /@esbuild/linux-s390x/0.16.10: + /@esbuild/linux-s390x@0.16.10: resolution: {integrity: sha512-fu7XtnoeRNFMx8DjK3gPWpFBDM2u5ba+FYwg27SjMJwKvJr4bDyKz5c+FLXLUSSAkMAt/UL+cUbEbra+rYtUgw==} engines: {node: '>=12'} cpu: [s390x] @@ -587,7 +663,7 @@ packages: dev: true optional: true - /@esbuild/linux-x64/0.16.10: + /@esbuild/linux-x64@0.16.10: resolution: {integrity: sha512-61lcjVC/RldNNMUzQQdyCWjCxp9YLEQgIxErxU9XluX7juBdGKb0pvddS0vPNuCvotRbzijZ1pzII+26haWzbA==} engines: {node: '>=12'} cpu: [x64] @@ -596,7 +672,7 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64/0.16.10: + /@esbuild/netbsd-x64@0.16.10: resolution: {integrity: sha512-JeZXCX3viSA9j4HqSoygjssdqYdfHd6yCFWyfSekLbz4Ef+D2EjvsN02ZQPwYl5a5gg/ehdHgegHhlfOFP0HCA==} engines: {node: '>=12'} cpu: [x64] @@ -605,7 +681,7 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64/0.16.10: + /@esbuild/openbsd-x64@0.16.10: resolution: {integrity: sha512-3qpxQKuEVIIg8SebpXsp82OBrqjPV/OwNWmG+TnZDr3VGyChNnGMHccC1xkbxCHDQNnnXjxhMQNyHmdFJbmbRA==} engines: {node: '>=12'} cpu: [x64] @@ -614,7 +690,7 @@ packages: dev: true optional: true - /@esbuild/sunos-x64/0.16.10: + /@esbuild/sunos-x64@0.16.10: resolution: {integrity: sha512-z+q0xZ+et/7etz7WoMyXTHZ1rB8PMSNp/FOqURLJLOPb3GWJ2aj4oCqFCjPwEbW1rsT7JPpxeH/DwGAWk/I1Bg==} engines: {node: '>=12'} cpu: [x64] @@ -623,7 +699,7 @@ packages: dev: true optional: true - /@esbuild/win32-arm64/0.16.10: + /@esbuild/win32-arm64@0.16.10: resolution: {integrity: sha512-+YYu5sbQ9npkNT9Dec+tn1F/kjg6SMgr6bfi/6FpXYZvCRfu2YFPZGb+3x8K30s8eRxFpoG4sGhiSUkr1xbHEw==} engines: {node: '>=12'} cpu: [arm64] @@ -632,7 +708,7 @@ packages: dev: true optional: true - /@esbuild/win32-ia32/0.16.10: + /@esbuild/win32-ia32@0.16.10: resolution: {integrity: sha512-Aw7Fupk7XNehR1ftHGYwUteyJ2q+em/aE+fVU3YMTBN2V5A7Z4aVCSV+SvCp9HIIHZavPFBpbdP3VfjQpdf6Xg==} engines: {node: '>=12'} cpu: [ia32] @@ -641,7 +717,7 @@ packages: dev: true optional: true - /@esbuild/win32-x64/0.16.10: + /@esbuild/win32-x64@0.16.10: resolution: {integrity: sha512-qddWullt3sC1EIpfHvCRBq3H4g3L86DZpD6n8k2XFjFVyp01D++uNbN1hT/JRsHxTbyyemZcpwL5aRlJwc/zFw==} engines: {node: '>=12'} cpu: [x64] @@ -650,7 +726,7 @@ packages: dev: true optional: true - /@ipld/car/5.1.0: + /@ipld/car@5.1.0: resolution: {integrity: sha512-k9pO0YqJvmFGY5pJDhF2Ocz+mRp3C3r4ikr1NrUXkzN/z4JzhE7XbQzUCcm7daq8k4tRqap0fWPjxZwjS9PUcQ==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: @@ -660,7 +736,7 @@ packages: varint: 6.0.0 dev: false - /@ipld/dag-cbor/9.0.0: + /@ipld/dag-cbor@9.0.0: resolution: {integrity: sha512-zdsiSiYDEOIDW7mmWOYWC9gukjXO+F8wqxz/LfN7iSwTfIyipC8+UQrCbPupFMRb/33XQTZk8yl3My8vUQBRoA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: @@ -668,7 +744,7 @@ packages: multiformats: 11.0.1 dev: false - /@ipld/dag-json/10.0.0: + /@ipld/dag-json@10.0.0: resolution: {integrity: sha512-u/PfR2sT9AiZZDUl1VNspx3OP13zuvBXAd3sKiURlSOoWfoLigxTCs+sXeaXA0hoXU7u1M2DECMt4LCUHuApSA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: @@ -676,7 +752,7 @@ packages: multiformats: 11.0.1 dev: false - /@ipld/dag-ucan/3.2.0: + /@ipld/dag-ucan@3.2.0: resolution: {integrity: sha512-CTClaGx4F3iEMJgYaYVOVBEvtNXzPc77Mi6p3vBtylSzDWhbf1Gou9ij7PlblOqWKA1H7XI8fp6yweTb6iXNKQ==} dependencies: '@ipld/dag-cbor': 9.0.0 @@ -684,7 +760,7 @@ packages: multiformats: 11.0.1 dev: false - /@istanbuljs/load-nyc-config/1.1.0: + /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} dependencies: @@ -695,12 +771,12 @@ packages: resolve-from: 5.0.0 dev: true - /@istanbuljs/schema/0.1.3: + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true - /@jridgewell/gen-mapping/0.1.1: + /@jridgewell/gen-mapping@0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} dependencies: @@ -708,7 +784,7 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@jridgewell/gen-mapping/0.3.2: + /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} dependencies: @@ -717,32 +793,32 @@ packages: '@jridgewell/trace-mapping': 0.3.17 dev: true - /@jridgewell/resolve-uri/3.1.0: + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/set-array/1.1.2: + /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.14: + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true - /@jridgewell/trace-mapping/0.3.17: + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@noble/ed25519/1.7.3: + /@noble/ed25519@1.7.3: resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==} dev: false - /@nodelib/fs.scandir/2.1.5: + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} dependencies: @@ -750,12 +826,12 @@ packages: run-parallel: 1.2.0 dev: true - /@nodelib/fs.stat/2.0.5: + /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} dev: true - /@nodelib/fs.walk/1.2.8: + /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} dependencies: @@ -763,40 +839,40 @@ packages: fastq: 1.15.0 dev: true - /@polka/url/0.5.0: + /@polka/url@0.5.0: resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} dev: true - /@polka/url/1.0.0-next.21: + /@polka/url@1.0.0-next.21: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true - /@types/chai-subset/1.3.3: + /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: '@types/chai': 4.3.4 dev: true - /@types/chai/4.3.4: + /@types/chai@4.3.4: resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} dev: true - /@types/istanbul-lib-coverage/2.0.4: + /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} dev: true - /@types/mocha/10.0.1: + /@types/mocha@10.0.1: resolution: {integrity: sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==} dev: true - /@web-std/blob/3.0.4: + /@web-std/blob@3.0.4: resolution: {integrity: sha512-+dibyiw+uHYK4dX5cJ7HA+gtDAaUUe6JsOryp2ZpAC7h4ICsh49E34JwHoEKPlPvP0llCrNzz45vvD+xX5QDBg==} dependencies: '@web-std/stream': 1.0.0 web-encoding: 1.1.5 dev: true - /@web-std/fetch/4.1.0: + /@web-std/fetch@4.1.0: resolution: {integrity: sha512-ZRizMcP8YyuRlhIsRYNFD9x/w28K7kbUhNGmKM9hDy4qeQ5xMTk//wA89EF+Clbl6EP4ksmCcN+4TqBMSRL8Zw==} engines: {node: ^10.17 || >=12.3} dependencies: @@ -808,41 +884,41 @@ packages: mrmime: 1.0.1 dev: true - /@web-std/file/3.0.2: + /@web-std/file@3.0.2: resolution: {integrity: sha512-pIH0uuZsmY8YFvSHP1NsBIiMT/1ce0suPrX74fEeO3Wbr1+rW0fUGEe4d0R99iLwXtyCwyserqCFI4BJkJlkRA==} dependencies: '@web-std/blob': 3.0.4 dev: true - /@web-std/form-data/3.0.2: + /@web-std/form-data@3.0.2: resolution: {integrity: sha512-rhc8IRw66sJ0FHcnC84kT3mTN6eACTuNftkt1XSl1Ef6WRKq4Pz65xixxqZymAZl1K3USpwhLci4SKNn4PYxWQ==} dependencies: web-encoding: 1.1.5 dev: true - /@web-std/stream/1.0.0: + /@web-std/stream@1.0.0: resolution: {integrity: sha512-jyIbdVl+0ZJyKGTV0Ohb9E6UnxP+t7ZzX4Do3AHjZKxUXKMs9EmqnBDQgHF7bEw0EzbQygOjtt/7gvtmi//iCQ==} dependencies: web-streams-polyfill: 3.2.1 dev: true - /@web-std/stream/1.0.1: + /@web-std/stream@1.0.1: resolution: {integrity: sha512-tsz4Y0WNDgFA5jwLSeV7/UV5rfMIlj0cPsSLVfTihjaVW0OJPd5NxJ3le1B3yLyqqzRpeG5OAfJAADLc4VoGTA==} dependencies: web-streams-polyfill: 3.2.1 dev: true - /@web3-storage/multipart-parser/1.0.0: + /@web3-storage/multipart-parser@1.0.0: resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} dev: true - /@zxing/text-encoding/0.9.0: + /@zxing/text-encoding@0.9.0: resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} requiresBuild: true dev: true optional: true - /aggregate-error/3.1.0: + /aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} dependencies: @@ -850,7 +926,7 @@ packages: indent-string: 4.0.0 dev: true - /aggregate-error/4.0.1: + /aggregate-error@4.0.1: resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} engines: {node: '>=12'} dependencies: @@ -858,36 +934,36 @@ packages: indent-string: 5.0.0 dev: true - /ansi-colors/4.1.1: + /ansi-colors@4.1.1: resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} engines: {node: '>=6'} dev: true - /ansi-regex/5.0.1: + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true - /ansi-regex/6.0.1: + /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} dev: true - /ansi-styles/3.2.1: + /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} dependencies: color-convert: 1.9.3 dev: true - /ansi-styles/4.3.0: + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 dev: true - /anymatch/3.1.3: + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} dependencies: @@ -895,28 +971,28 @@ packages: picomatch: 2.3.1 dev: true - /append-transform/2.0.0: + /append-transform@2.0.0: resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} engines: {node: '>=8'} dependencies: default-require-extensions: 3.0.1 dev: true - /archy/1.0.0: + /archy@1.0.0: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: true - /argparse/1.0.10: + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 dev: true - /argparse/2.0.1: + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true - /array.prototype.every/1.1.4: + /array.prototype.every@1.1.4: resolution: {integrity: sha512-Aui35iRZk1HHLRAyF7QP0KAnOnduaQ6fo6k1NVWfRc0xTs2AZ70ytlXvOmkC6Di4JmUs2Wv3DYzGtCQFSk5uGg==} engines: {node: '>= 0.4'} dependencies: @@ -926,34 +1002,34 @@ packages: is-string: 1.0.7 dev: true - /arrify/3.0.0: + /arrify@3.0.0: resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} engines: {node: '>=12'} dev: true - /assertion-error/1.1.0: + /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /available-typed-arrays/1.0.5: + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} dev: true - /balanced-match/1.0.2: + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /base64-js/1.5.1: + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true - /binary-extensions/2.2.0: + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: true - /bl/5.1.0: + /bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} dependencies: buffer: 6.0.3 @@ -961,31 +1037,31 @@ packages: readable-stream: 3.6.1 dev: true - /brace-expansion/1.1.11: + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 dev: true - /brace-expansion/2.0.1: + /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 dev: true - /braces/3.0.2: + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} dependencies: fill-range: 7.0.1 dev: true - /browser-stdout/1.3.1: + /browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} dev: true - /browserslist/4.21.4: + /browserslist@4.21.4: resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -993,17 +1069,17 @@ packages: caniuse-lite: 1.0.30001445 electron-to-chromium: 1.4.284 node-releases: 2.0.8 - update-browserslist-db: 1.0.10_browserslist@4.21.4 + update-browserslist-db: 1.0.10(browserslist@4.21.4) dev: true - /buffer/6.0.3: + /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 dev: true - /c8/7.13.0: + /c8@7.13.0: resolution: {integrity: sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==} engines: {node: '>=10.12.0'} hasBin: true @@ -1022,7 +1098,7 @@ packages: yargs-parser: 20.2.9 dev: true - /caching-transform/4.0.0: + /caching-transform@4.0.0: resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} engines: {node: '>=8'} dependencies: @@ -1032,43 +1108,43 @@ packages: write-file-atomic: 3.0.3 dev: true - /call-bind/1.0.2: + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: function-bind: 1.1.1 get-intrinsic: 1.2.0 dev: true - /camelcase/5.3.1: + /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} dev: true - /camelcase/6.3.0: + /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} dev: true - /camelcase/7.0.1: + /camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} dev: true - /caniuse-lite/1.0.30001445: + /caniuse-lite@1.0.30001445: resolution: {integrity: sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==} dev: true - /cborg/1.10.0: + /cborg@1.10.0: resolution: {integrity: sha512-/eM0JCaL99HDHxjySNQJLaolZFVdl6VA0/hEKIoiQPcQzE5LrG5QHdml0HaBt31brgB9dNe1zMr3f8IVrpotRQ==} hasBin: true dev: false - /chai-subset/1.6.0: + /chai-subset@1.6.0: resolution: {integrity: sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==} engines: {node: '>=4'} dev: true - /chai/4.3.7: + /chai@4.3.7: resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} engines: {node: '>=4'} dependencies: @@ -1081,7 +1157,7 @@ packages: type-detect: 4.0.8 dev: true - /chalk/2.4.2: + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} dependencies: @@ -1090,7 +1166,7 @@ packages: supports-color: 5.5.0 dev: true - /chalk/4.1.2: + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: @@ -1098,16 +1174,16 @@ packages: supports-color: 7.2.0 dev: true - /chalk/5.2.0: + /chalk@5.2.0: resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: true - /check-error/1.0.2: + /check-error@1.0.2: resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} dev: true - /chokidar/3.5.3: + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} dependencies: @@ -1122,31 +1198,31 @@ packages: fsevents: 2.3.2 dev: true - /clean-stack/2.2.0: + /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} dev: true - /clean-stack/4.2.0: + /clean-stack@4.2.0: resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} engines: {node: '>=12'} dependencies: escape-string-regexp: 5.0.0 dev: true - /cli-cursor/4.0.0: + /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: restore-cursor: 4.0.0 dev: true - /cli-spinners/2.7.0: + /cli-spinners@2.7.0: resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} engines: {node: '>=6'} dev: true - /cliui/6.0.0: + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: string-width: 4.2.3 @@ -1154,7 +1230,7 @@ packages: wrap-ansi: 6.2.0 dev: true - /cliui/7.0.4: + /cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: string-width: 4.2.3 @@ -1162,45 +1238,45 @@ packages: wrap-ansi: 7.0.0 dev: true - /clone/1.0.4: + /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} dev: true - /color-convert/1.9.3: + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 dev: true - /color-convert/2.0.1: + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 dev: true - /color-name/1.1.3: + /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} dev: true - /color-name/1.1.4: + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /commondir/1.0.1: + /commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} dev: true - /concat-map/0.0.1: + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /convert-source-map/1.9.0: + /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true - /cp-file/9.1.0: + /cp-file@9.1.0: resolution: {integrity: sha512-3scnzFj/94eb7y4wyXRWwvzLFaQp87yyfTnChIjlfYrVqp5lVO3E2hIJMeQIltUT0K2ZAB3An1qXcBmwGyvuwA==} engines: {node: '>=10'} dependencies: @@ -1210,7 +1286,7 @@ packages: p-event: 4.2.0 dev: true - /cpy/9.0.1: + /cpy@9.0.1: resolution: {integrity: sha512-D9U0DR5FjTCN3oMTcFGktanHnAG5l020yvOCR1zKILmAyPP7I/9pl6NFgRbDcmSENtbK1sQLBz1p9HIOlroiNg==} engines: {node: ^12.20.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -1224,7 +1300,7 @@ packages: p-map: 5.5.0 dev: true - /cross-spawn/7.0.3: + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: @@ -1233,31 +1309,19 @@ packages: which: 2.0.2 dev: true - /crypto-random-string/4.0.0: + /crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} dependencies: type-fest: 1.4.0 dev: true - /data-uri-to-buffer/3.0.1: + /data-uri-to-buffer@3.0.1: resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} engines: {node: '>= 6'} dev: true - /debug/4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: true - - /debug/4.3.4_supports-color@8.1.1: + /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -1270,24 +1334,24 @@ packages: supports-color: 8.1.1 dev: true - /decamelize/1.2.0: + /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} dev: true - /decamelize/4.0.0: + /decamelize@4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} dev: true - /deep-eql/4.1.3: + /deep-eql@4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} dependencies: type-detect: 4.0.8 dev: true - /deep-equal/2.2.0: + /deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} dependencies: call-bind: 1.0.2 @@ -1309,20 +1373,20 @@ packages: which-typed-array: 1.1.9 dev: true - /default-require-extensions/3.0.1: + /default-require-extensions@3.0.1: resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} engines: {node: '>=8'} dependencies: strip-bom: 4.0.0 dev: true - /defaults/1.0.4: + /defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 dev: true - /define-properties/1.2.0: + /define-properties@1.2.0: resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} engines: {node: '>= 0.4'} dependencies: @@ -1330,38 +1394,38 @@ packages: object-keys: 1.1.1 dev: true - /defined/1.0.1: + /defined@1.0.1: resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} dev: true - /diff/5.0.0: + /diff@5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} dev: true - /dir-glob/3.0.1: + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dependencies: path-type: 4.0.0 dev: true - /dotignore/0.1.2: + /dotignore@0.1.2: resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==} hasBin: true dependencies: minimatch: 3.1.2 dev: true - /electron-to-chromium/1.4.284: + /electron-to-chromium@1.4.284: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true - /emoji-regex/8.0.0: + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true - /es-abstract/1.21.1: + /es-abstract@1.21.1: resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} engines: {node: '>= 0.4'} dependencies: @@ -1400,7 +1464,7 @@ packages: which-typed-array: 1.1.9 dev: true - /es-get-iterator/1.1.3: + /es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} dependencies: call-bind: 1.0.2 @@ -1414,7 +1478,7 @@ packages: stop-iteration-iterator: 1.0.0 dev: true - /es-set-tostringtag/2.0.1: + /es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} engines: {node: '>= 0.4'} dependencies: @@ -1423,7 +1487,7 @@ packages: has-tostringtag: 1.0.0 dev: true - /es-to-primitive/1.2.1: + /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} dependencies: @@ -1432,11 +1496,11 @@ packages: is-symbol: 1.0.4 dev: true - /es6-error/4.1.1: + /es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true - /esbuild/0.16.10: + /esbuild@0.16.10: resolution: {integrity: sha512-z5dIViHoVnw2l+NCJ3zj5behdXjYvXne9gL18OOivCadXDUhyDkeSvEtLcGVAJW2fNmh33TDUpsi704XYlDodw==} engines: {node: '>=12'} hasBin: true @@ -1466,38 +1530,38 @@ packages: '@esbuild/win32-x64': 0.16.10 dev: true - /escalade/3.1.1: + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} dev: true - /escape-string-regexp/1.0.5: + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} dev: true - /escape-string-regexp/4.0.0: + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - /escape-string-regexp/5.0.0: + /escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} dev: true - /esprima/4.0.1: + /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true dev: true - /events/3.3.0: + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} dev: true - /fast-glob/3.2.12: + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} dependencies: @@ -1508,20 +1572,20 @@ packages: micromatch: 4.0.5 dev: true - /fastq/1.15.0: + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 dev: true - /fill-range/7.0.1: + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 dev: true - /find-cache-dir/3.3.2: + /find-cache-dir@3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} dependencies: @@ -1530,7 +1594,7 @@ packages: pkg-dir: 4.2.0 dev: true - /find-up/4.1.0: + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} dependencies: @@ -1538,7 +1602,7 @@ packages: path-exists: 4.0.0 dev: true - /find-up/5.0.0: + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} dependencies: @@ -1546,18 +1610,18 @@ packages: path-exists: 4.0.0 dev: true - /flat/5.0.2: + /flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true dev: true - /for-each/0.3.3: + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 dev: true - /foreground-child/2.0.0: + /foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} dependencies: @@ -1565,15 +1629,15 @@ packages: signal-exit: 3.0.7 dev: true - /fromentries/1.3.2: + /fromentries@1.3.2: resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} dev: true - /fs.realpath/1.0.0: + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents/2.3.2: + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -1581,11 +1645,11 @@ packages: dev: true optional: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /function.prototype.name/1.1.5: + /function.prototype.name@1.1.5: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} engines: {node: '>= 0.4'} dependencies: @@ -1595,25 +1659,25 @@ packages: functions-have-names: 1.2.3 dev: true - /functions-have-names/1.2.3: + /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gensync/1.0.0-beta.2: + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} dev: true - /get-caller-file/2.0.5: + /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} dev: true - /get-func-name/2.0.0: + /get-func-name@2.0.0: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} dev: true - /get-intrinsic/1.2.0: + /get-intrinsic@1.2.0: resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} dependencies: function-bind: 1.1.1 @@ -1621,12 +1685,12 @@ packages: has-symbols: 1.0.3 dev: true - /get-package-type/0.1.0: + /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} dev: true - /get-symbol-description/1.0.0: + /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} dependencies: @@ -1634,14 +1698,14 @@ packages: get-intrinsic: 1.2.0 dev: true - /glob-parent/5.1.2: + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 dev: true - /glob/7.2.0: + /glob@7.2.0: resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} dependencies: fs.realpath: 1.0.0 @@ -1652,7 +1716,7 @@ packages: path-is-absolute: 1.0.1 dev: true - /glob/7.2.3: + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 @@ -1663,19 +1727,19 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals/11.12.0: + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} dev: true - /globalthis/1.0.3: + /globalthis@1.0.3: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.0 dev: true - /globby/13.1.3: + /globby@13.1.3: resolution: {integrity: sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: @@ -1686,68 +1750,68 @@ packages: slash: 4.0.0 dev: true - /gopd/1.0.1: + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: get-intrinsic: 1.2.0 dev: true - /graceful-fs/4.2.10: + /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true - /has-bigints/1.0.2: + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true - /has-dynamic-import/2.0.1: + /has-dynamic-import@2.0.1: resolution: {integrity: sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==} dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.0 dev: true - /has-flag/3.0.0: + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} dev: true - /has-flag/4.0.0: + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: true - /has-property-descriptors/1.0.0: + /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} dependencies: get-intrinsic: 1.2.0 dev: true - /has-proto/1.0.1: + /has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} dev: true - /has-symbols/1.0.3: + /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} dev: true - /has-tostringtag/1.0.0: + /has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 dev: true - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /hasha/5.2.2: + /hasha@5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} dependencies: @@ -1755,51 +1819,51 @@ packages: type-fest: 0.8.1 dev: true - /he/1.2.0: + /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true dev: true - /html-escaper/2.0.2: + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true - /ieee754/1.2.1: + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: true - /ignore/5.2.4: + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} dev: true - /imurmurhash/0.1.4: + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} dev: true - /indent-string/4.0.0: + /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} dev: true - /indent-string/5.0.0: + /indent-string@5.0.0: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} dev: true - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 dev: true - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true - /internal-slot/1.0.5: + /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} dependencies: @@ -1808,7 +1872,7 @@ packages: side-channel: 1.0.4 dev: true - /is-arguments/1.1.1: + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} dependencies: @@ -1816,7 +1880,7 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-array-buffer/3.0.2: + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: call-bind: 1.0.2 @@ -1824,20 +1888,20 @@ packages: is-typed-array: 1.1.10 dev: true - /is-bigint/1.0.4: + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: has-bigints: 1.0.2 dev: true - /is-binary-path/2.1.0: + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 dev: true - /is-boolean-object/1.1.2: + /is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: @@ -1845,80 +1909,80 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-callable/1.2.7: + /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} dev: true - /is-core-module/2.11.0: + /is-core-module@2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true - /is-date-object/1.0.5: + /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: true - /is-extglob/2.1.1: + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} dev: true - /is-fullwidth-code-point/3.0.0: + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} dev: true - /is-generator-function/1.0.10: + /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: true - /is-glob/4.0.3: + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 dev: true - /is-interactive/2.0.0: + /is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} dev: true - /is-map/2.0.2: + /is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} dev: true - /is-negative-zero/2.0.2: + /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} dev: true - /is-number-object/1.0.7: + /is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: true - /is-number/7.0.0: + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true - /is-plain-obj/2.1.0: + /is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} dev: true - /is-regex/1.1.4: + /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: @@ -1926,41 +1990,41 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-set/2.0.2: + /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} dev: true - /is-shared-array-buffer/1.0.2: + /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: call-bind: 1.0.2 dev: true - /is-stream/2.0.1: + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} dev: true - /is-stream/3.0.0: + /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /is-string/1.0.7: + /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 dev: true - /is-symbol/1.0.4: + /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 dev: true - /is-typed-array/1.1.10: + /is-typed-array@1.1.10: resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} engines: {node: '>= 0.4'} dependencies: @@ -1971,63 +2035,63 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-typedarray/1.0.0: + /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true - /is-unicode-supported/0.1.0: + /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} dev: true - /is-unicode-supported/1.3.0: + /is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} dev: true - /is-weakmap/2.0.1: + /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} dev: true - /is-weakref/1.0.2: + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.2 dev: true - /is-weakset/2.0.2: + /is-weakset@2.0.2: resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.0 dev: true - /is-windows/1.0.2: + /is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} dev: true - /isarray/2.0.5: + /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} dev: true - /isexe/2.0.0: + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /istanbul-lib-coverage/3.2.0: + /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} dev: true - /istanbul-lib-hook/3.0.0: + /istanbul-lib-hook@3.0.0: resolution: {integrity: sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==} engines: {node: '>=8'} dependencies: append-transform: 2.0.0 dev: true - /istanbul-lib-instrument/4.0.3: + /istanbul-lib-instrument@4.0.3: resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} engines: {node: '>=8'} dependencies: @@ -2039,7 +2103,7 @@ packages: - supports-color dev: true - /istanbul-lib-processinfo/2.0.3: + /istanbul-lib-processinfo@2.0.3: resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==} engines: {node: '>=8'} dependencies: @@ -2051,7 +2115,7 @@ packages: uuid: 8.3.2 dev: true - /istanbul-lib-report/3.0.0: + /istanbul-lib-report@3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} engines: {node: '>=8'} dependencies: @@ -2060,18 +2124,18 @@ packages: supports-color: 7.2.0 dev: true - /istanbul-lib-source-maps/4.0.1: + /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: - supports-color dev: true - /istanbul-reports/3.1.5: + /istanbul-reports@3.1.5: resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} engines: {node: '>=8'} dependencies: @@ -2079,11 +2143,11 @@ packages: istanbul-lib-report: 3.0.0 dev: true - /js-tokens/4.0.0: + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true - /js-yaml/3.14.1: + /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true dependencies: @@ -2091,63 +2155,63 @@ packages: esprima: 4.0.1 dev: true - /js-yaml/4.1.0: + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 dev: true - /jsesc/2.5.2: + /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true dev: true - /json5/2.2.3: + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true dev: true - /junk/4.0.0: + /junk@4.0.0: resolution: {integrity: sha512-ojtSU++zLJ3jQG9bAYjg94w+/DOJtRyD7nPaerMFrBhmdVmiV5/exYH5t4uHga4G/95nT6hr1OJoKIFbYbrW5w==} engines: {node: '>=12.20'} dev: true - /kleur/4.1.5: + /kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} dev: true - /lilconfig/2.1.0: + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} dev: true - /locate-path/5.0.0: + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} dependencies: p-locate: 4.1.0 dev: true - /locate-path/6.0.0: + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} dependencies: p-locate: 5.0.0 dev: true - /lodash.flattendeep/4.4.0: + /lodash.flattendeep@4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} dev: true - /lodash/4.17.21: + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /log-symbols/4.1.0: + /log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} dependencies: @@ -2155,7 +2219,7 @@ packages: is-unicode-supported: 0.1.0 dev: true - /log-symbols/5.1.0: + /log-symbols@5.1.0: resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} engines: {node: '>=12'} dependencies: @@ -2163,45 +2227,45 @@ packages: is-unicode-supported: 1.3.0 dev: true - /loupe/2.3.6: + /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: get-func-name: 2.0.0 dev: true - /lru-cache/5.1.1: + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: yallist: 3.1.1 dev: true - /make-dir/3.1.0: + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: semver: 6.3.0 dev: true - /matchit/1.1.0: + /matchit@1.1.0: resolution: {integrity: sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==} engines: {node: '>=6'} dependencies: '@arr/every': 1.0.1 dev: true - /merge-options/3.0.4: + /merge-options@3.0.4: resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} engines: {node: '>=10'} dependencies: is-plain-obj: 2.1.0 dev: true - /merge2/1.4.1: + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} dev: true - /micromatch/4.0.5: + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} dependencies: @@ -2209,29 +2273,29 @@ packages: picomatch: 2.3.1 dev: true - /mimic-fn/2.1.0: + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} dev: true - /minimatch/3.1.2: + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true - /minimatch/5.0.1: + /minimatch@5.0.1: resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 dev: true - /minimist/1.2.8: + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true - /mocha/10.2.0: + /mocha@10.2.0: resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} engines: {node: '>= 14.0.0'} hasBin: true @@ -2239,7 +2303,7 @@ packages: ansi-colors: 4.1.1 browser-stdout: 1.3.1 chokidar: 3.5.3 - debug: 4.3.4_supports-color@8.1.1 + debug: 4.3.4(supports-color@8.1.1) diff: 5.0.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 @@ -2259,61 +2323,62 @@ packages: yargs-unparser: 2.0.0 dev: true - /mri/1.2.0: + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} dev: true - /mrmime/1.0.1: + /mrmime@1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} dev: true - /ms/2.1.2: + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true - /ms/2.1.3: + /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true - /multiformats/11.0.1: + /multiformats@11.0.1: resolution: {integrity: sha512-atWruyH34YiknSdL5yeIir00EDlJRpHzELYQxG7Iy29eCyL+VrZHpPrX5yqlik3jnuqpLpRKVZ0SGVb9UzKaSA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} + dev: false - /nanoid/3.3.3: + /nanoid@3.3.3: resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true - /nanoid/4.0.1: + /nanoid@4.0.1: resolution: {integrity: sha512-udKGtCCUafD3nQtJg9wBhRP3KMbPglUsgV5JVsXhvyBs/oefqb4sqMEhKBBgqZncYowu58p1prsZQBYvAj/Gww==} engines: {node: ^14 || ^16 || >=18} hasBin: true dev: true - /nested-error-stacks/2.1.1: + /nested-error-stacks@2.1.1: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: true - /node-preload/0.2.1: + /node-preload@0.2.1: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} dependencies: process-on-spawn: 1.0.0 dev: true - /node-releases/2.0.8: + /node-releases@2.0.8: resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} dev: true - /normalize-path/3.0.0: + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: true - /nyc/15.1.0: + /nyc@15.1.0: resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==} engines: {node: '>=8.9'} hasBin: true @@ -2349,11 +2414,11 @@ packages: - supports-color dev: true - /object-inspect/1.12.3: + /object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} dev: true - /object-is/1.1.5: + /object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} dependencies: @@ -2361,12 +2426,12 @@ packages: define-properties: 1.2.0 dev: true - /object-keys/1.1.1: + /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} dev: true - /object.assign/4.1.4: + /object.assign@4.1.4: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} dependencies: @@ -2376,24 +2441,24 @@ packages: object-keys: 1.1.1 dev: true - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true - /one-webcrypto/1.0.3: + /one-webcrypto@1.0.3: resolution: {integrity: sha512-fu9ywBVBPx0gS9K0etIROTiCkvI5S1TDjFsYFb3rC1ewFxeOqsbzq7aIMBHsYfrTHBcGXJaONXXjTl8B01cW1Q==} dev: false - /onetime/5.1.2: + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 dev: true - /ora/6.1.2: + /ora@6.1.2: resolution: {integrity: sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: @@ -2408,92 +2473,92 @@ packages: wcwidth: 1.0.1 dev: true - /p-event/4.2.0: + /p-event@4.2.0: resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} engines: {node: '>=8'} dependencies: p-timeout: 3.2.0 dev: true - /p-filter/3.0.0: + /p-filter@3.0.0: resolution: {integrity: sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: p-map: 5.5.0 dev: true - /p-finally/1.0.0: + /p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} dev: true - /p-limit/2.3.0: + /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} dependencies: p-try: 2.2.0 dev: true - /p-limit/3.1.0: + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 dev: true - /p-locate/4.1.0: + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} dependencies: p-limit: 2.3.0 dev: true - /p-locate/5.0.0: + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} dependencies: p-limit: 3.1.0 dev: true - /p-map/3.0.0: + /p-map@3.0.0: resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} engines: {node: '>=8'} dependencies: aggregate-error: 3.1.0 dev: true - /p-map/5.5.0: + /p-map@5.5.0: resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} engines: {node: '>=12'} dependencies: aggregate-error: 4.0.1 dev: true - /p-timeout/3.2.0: + /p-timeout@3.2.0: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} dependencies: p-finally: 1.0.0 dev: true - /p-timeout/6.1.1: + /p-timeout@6.1.1: resolution: {integrity: sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w==} engines: {node: '>=14.16'} dev: true - /p-try/2.2.0: + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} dev: true - /p-wait-for/5.0.0: + /p-wait-for@5.0.0: resolution: {integrity: sha512-nkxeZInKET8e78NTtqBgxpnxDLbiCiQnGdoTnkLkluovfTyI5UTCrGwPNOr6ewJ90NpWyxEFt1ToZ96LmIXXHQ==} engines: {node: '>=12'} dependencies: p-timeout: 6.1.1 dev: true - /package-hash/4.0.0: + /package-hash@4.0.0: resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} engines: {node: '>=8'} dependencies: @@ -2503,61 +2568,61 @@ packages: release-zalgo: 1.0.0 dev: true - /path-browserify/1.0.1: + /path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} dev: true - /path-exists/4.0.0: + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} dev: true - /path-is-absolute/1.0.1: + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} dev: true - /path-key/3.1.1: + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /path-type/4.0.0: + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} dev: true - /pathval/1.1.1: + /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true - /picocolors/1.0.0: + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true - /picomatch/2.3.1: + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: true - /pkg-dir/4.2.0: + /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} dependencies: find-up: 4.1.0 dev: true - /playwright-core/1.29.0: + /playwright-core@1.29.0: resolution: {integrity: sha512-pboOm1m0RD6z1GtwAbEH60PYRfF87vKdzOSRw2RyO0Y0a7utrMyWN2Au1ojGvQr4umuBMODkKTv607YIRypDSQ==} engines: {node: '>=14'} hasBin: true dev: true - /playwright-test/8.2.0: + /playwright-test@8.2.0: resolution: {integrity: sha512-EcEFHtIQXs1vNvurjsChfrzYL28GtWNfXAj1d6L3LULGp2LWh8A10mrvmP/zIs5O61id/oCb6PoQ1fT5+d34uQ==} engines: {node: '>=16.0.0'} hasBin: true @@ -2592,48 +2657,48 @@ packages: v8-to-istanbul: 9.1.0 dev: true - /polka/0.5.2: + /polka@0.5.2: resolution: {integrity: sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==} dependencies: '@polka/url': 0.5.0 trouter: 2.0.1 dev: true - /premove/4.0.0: + /premove@4.0.0: resolution: {integrity: sha512-zim/Hr4+FVdCIM7zL9b9Z0Wfd5Ya3mnKtiuDv7L5lzYzanSq6cOcVJ7EFcgK4I0pt28l8H0jX/x3nyog380XgQ==} engines: {node: '>=6'} hasBin: true dev: true - /prettier/2.8.4: + /prettier@2.8.4: resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==} engines: {node: '>=10.13.0'} hasBin: true dev: true - /process-on-spawn/1.0.0: + /process-on-spawn@1.0.0: resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==} engines: {node: '>=8'} dependencies: fromentries: 1.3.2 dev: true - /process/0.11.10: + /process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} dev: true - /queue-microtask/1.2.3: + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /randombytes/2.1.0: + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: safe-buffer: 5.2.1 dev: true - /readable-stream/3.6.1: + /readable-stream@3.6.1: resolution: {integrity: sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==} engines: {node: '>= 6'} dependencies: @@ -2642,14 +2707,14 @@ packages: util-deprecate: 1.0.2 dev: true - /readdirp/3.6.0: + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 dev: true - /regexp.prototype.flags/1.4.3: + /regexp.prototype.flags@1.4.3: resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} engines: {node: '>= 0.4'} dependencies: @@ -2658,28 +2723,28 @@ packages: functions-have-names: 1.2.3 dev: true - /release-zalgo/1.0.0: + /release-zalgo@1.0.0: resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==} engines: {node: '>=4'} dependencies: es6-error: 4.1.1 dev: true - /require-directory/2.1.1: + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} dev: true - /require-main-filename/2.0.0: + /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true - /resolve-from/5.0.0: + /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} dev: true - /resolve/2.0.0-next.4: + /resolve@2.0.0-next.4: resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} hasBin: true dependencies: @@ -2688,7 +2753,7 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /restore-cursor/4.0.0: + /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: @@ -2696,42 +2761,42 @@ packages: signal-exit: 3.0.7 dev: true - /resumer/0.0.0: + /resumer@0.0.0: resolution: {integrity: sha512-Fn9X8rX8yYF4m81rZCK/5VmrmsSbqS/i3rDLl6ZZHAXgC2nTAx3dhwG8q8odP/RmdLa2YrybDJaAMg+X1ajY3w==} dependencies: through: 2.3.8 dev: true - /reusify/1.0.4: + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true - /rimraf/3.0.2: + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 dev: true - /run-parallel/1.2.0: + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /sade/1.8.1: + /sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} dependencies: mri: 1.2.0 dev: true - /safe-buffer/5.2.1: + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true - /safe-regex-test/1.0.0: + /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: call-bind: 1.0.2 @@ -2739,34 +2804,34 @@ packages: is-regex: 1.1.4 dev: true - /semver/6.3.0: + /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true dev: true - /serialize-javascript/6.0.0: + /serialize-javascript@6.0.0: resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} dependencies: randombytes: 2.1.0 dev: true - /set-blocking/2.0.0: + /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true - /shebang-command/2.0.0: + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 dev: true - /shebang-regex/3.0.0: + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} dev: true - /side-channel/1.0.4: + /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: call-bind: 1.0.2 @@ -2774,11 +2839,11 @@ packages: object-inspect: 1.12.3 dev: true - /signal-exit/3.0.7: + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true - /sirv/2.0.2: + /sirv@2.0.2: resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} engines: {node: '>= 10'} dependencies: @@ -2787,17 +2852,17 @@ packages: totalist: 3.0.0 dev: true - /slash/4.0.0: + /slash@4.0.0: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} dev: true - /source-map/0.6.1: + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} dev: true - /spawn-wrap/2.0.0: + /spawn-wrap@2.0.0: resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==} engines: {node: '>=8'} dependencies: @@ -2809,25 +2874,25 @@ packages: which: 2.0.2 dev: true - /sprintf-js/1.0.3: + /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /stop-iteration-iterator/1.0.0: + /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} dependencies: internal-slot: 1.0.5 dev: true - /stream-browserify/3.0.0: + /stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} dependencies: inherits: 2.0.4 readable-stream: 3.6.1 dev: true - /string-width/4.2.3: + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} dependencies: @@ -2836,7 +2901,7 @@ packages: strip-ansi: 6.0.1 dev: true - /string.prototype.trim/1.2.7: + /string.prototype.trim@1.2.7: resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} engines: {node: '>= 0.4'} dependencies: @@ -2845,7 +2910,7 @@ packages: es-abstract: 1.21.1 dev: true - /string.prototype.trimend/1.0.6: + /string.prototype.trimend@1.0.6: resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} dependencies: call-bind: 1.0.2 @@ -2853,7 +2918,7 @@ packages: es-abstract: 1.21.1 dev: true - /string.prototype.trimstart/1.0.6: + /string.prototype.trimstart@1.0.6: resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} dependencies: call-bind: 1.0.2 @@ -2861,63 +2926,63 @@ packages: es-abstract: 1.21.1 dev: true - /string_decoder/1.3.0: + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 dev: true - /strip-ansi/6.0.1: + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 dev: true - /strip-ansi/7.0.1: + /strip-ansi@7.0.1: resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 dev: true - /strip-bom/4.0.0: + /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} dev: true - /strip-json-comments/3.1.1: + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true - /supports-color/5.5.0: + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} dependencies: has-flag: 3.0.0 dev: true - /supports-color/7.2.0: + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 dev: true - /supports-color/8.1.1: + /supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} dependencies: has-flag: 4.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /tape/5.6.3: + /tape@5.6.3: resolution: {integrity: sha512-cUDDGSbyoSIpdUAqbqLI/r7i/S4BHuCB9M5j7E/LrLs/x/i4zeAJ798aqo+FGo+kr9seBZwr8AkZW6rjceyAMQ==} hasBin: true dependencies: @@ -2944,12 +3009,12 @@ packages: through: 2.3.8 dev: true - /temp-dir/2.0.0: + /temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} dev: true - /tempy/3.0.0: + /tempy@3.0.0: resolution: {integrity: sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==} engines: {node: '>=14.16'} dependencies: @@ -2959,7 +3024,7 @@ packages: unique-string: 3.0.0 dev: true - /test-exclude/6.0.0: + /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} dependencies: @@ -2968,55 +3033,55 @@ packages: minimatch: 3.1.2 dev: true - /through/2.3.8: + /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true - /to-fast-properties/2.0.0: + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} dev: true - /to-regex-range/5.0.1: + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 dev: true - /totalist/3.0.0: + /totalist@3.0.0: resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} engines: {node: '>=6'} dev: true - /trouter/2.0.1: + /trouter@2.0.1: resolution: {integrity: sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==} engines: {node: '>=6'} dependencies: matchit: 1.1.0 dev: true - /type-detect/4.0.8: + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true - /type-fest/0.8.1: + /type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} dev: true - /type-fest/1.4.0: + /type-fest@1.4.0: resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} engines: {node: '>=10'} dev: true - /type-fest/2.19.0: + /type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} dev: true - /typed-array-length/1.0.4: + /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: call-bind: 1.0.2 @@ -3024,19 +3089,19 @@ packages: is-typed-array: 1.1.10 dev: true - /typedarray-to-buffer/3.1.5: + /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: is-typedarray: 1.0.0 dev: true - /typescript/4.9.5: + /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} hasBin: true dev: true - /unbox-primitive/1.0.2: + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: call-bind: 1.0.2 @@ -3045,14 +3110,14 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /unique-string/3.0.0: + /unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} dependencies: crypto-random-string: 4.0.0 dev: true - /update-browserslist-db/1.0.10_browserslist@4.21.4: + /update-browserslist-db@1.0.10(browserslist@4.21.4): resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true peerDependencies: @@ -3063,11 +3128,11 @@ packages: picocolors: 1.0.0 dev: true - /util-deprecate/1.0.2: + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /util/0.12.5: + /util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} dependencies: inherits: 2.0.4 @@ -3077,12 +3142,12 @@ packages: which-typed-array: 1.1.9 dev: true - /uuid/8.3.2: + /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true dev: true - /v8-to-istanbul/9.1.0: + /v8-to-istanbul@9.1.0: resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} engines: {node: '>=10.12.0'} dependencies: @@ -3091,17 +3156,17 @@ packages: convert-source-map: 1.9.0 dev: true - /varint/6.0.0: + /varint@6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} dev: false - /wcwidth/1.0.1: + /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 dev: true - /web-encoding/1.1.5: + /web-encoding@1.1.5: resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} dependencies: util: 0.12.5 @@ -3109,12 +3174,12 @@ packages: '@zxing/text-encoding': 0.9.0 dev: true - /web-streams-polyfill/3.2.1: + /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} dev: true - /which-boxed-primitive/1.0.2: + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: is-bigint: 1.0.4 @@ -3124,7 +3189,7 @@ packages: is-symbol: 1.0.4 dev: true - /which-collection/1.0.1: + /which-collection@1.0.1: resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} dependencies: is-map: 2.0.2 @@ -3133,11 +3198,11 @@ packages: is-weakset: 2.0.2 dev: true - /which-module/2.0.0: + /which-module@2.0.0: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} dev: true - /which-typed-array/1.1.9: + /which-typed-array@1.1.9: resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} engines: {node: '>= 0.4'} dependencies: @@ -3149,7 +3214,7 @@ packages: is-typed-array: 1.1.10 dev: true - /which/2.0.2: + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true @@ -3157,11 +3222,11 @@ packages: isexe: 2.0.0 dev: true - /workerpool/6.2.1: + /workerpool@6.2.1: resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} dev: true - /wrap-ansi/6.2.0: + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} dependencies: @@ -3170,7 +3235,7 @@ packages: strip-ansi: 6.0.1 dev: true - /wrap-ansi/7.0.0: + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} dependencies: @@ -3179,11 +3244,11 @@ packages: strip-ansi: 6.0.1 dev: true - /wrappy/1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /write-file-atomic/3.0.3: + /write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} dependencies: imurmurhash: 0.1.4 @@ -3192,20 +3257,20 @@ packages: typedarray-to-buffer: 3.1.5 dev: true - /y18n/4.0.3: + /y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} dev: true - /y18n/5.0.8: + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} dev: true - /yallist/3.1.1: + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true - /yargs-parser/18.1.3: + /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} dependencies: @@ -3213,17 +3278,17 @@ packages: decamelize: 1.2.0 dev: true - /yargs-parser/20.2.4: + /yargs-parser@20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} dev: true - /yargs-parser/20.2.9: + /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} dev: true - /yargs-unparser/2.0.0: + /yargs-unparser@2.0.0: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} engines: {node: '>=10'} dependencies: @@ -3233,7 +3298,7 @@ packages: is-plain-obj: 2.1.0 dev: true - /yargs/15.4.1: + /yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} dependencies: @@ -3250,7 +3315,7 @@ packages: yargs-parser: 18.1.3 dev: true - /yargs/16.2.0: + /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} dependencies: @@ -3263,7 +3328,7 @@ packages: yargs-parser: 20.2.9 dev: true - /yocto-queue/0.1.0: + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true From 4a018d70f08a696cf55d55bbff756b76080f6610 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 13:38:43 -0700 Subject: [PATCH 09/14] only untify null and undefined --- packages/server/src/server.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/server/src/server.js b/packages/server/src/server.js index 2e5787b9..6a98b134 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -130,8 +130,12 @@ export const invoke = async (invocation, server) => { return await Receipt.issue({ issuer: server.id, ran: invocation, + // handler returns result in a different format from the receipt + // so we convert it here. We also need to handle the case where + // the handler `null` or `undefined` is returned which in receipt + // form at is unit type `{}`. result: /** @type {API.ReceiptResult<{}>} */ ( - value?.error ? { error: value } : { ok: value || {} } + value?.error ? { error: value } : { ok: value == null ? {} : value } ), }) } catch (cause) { From fc33269706e334c8416a0c9c3f92d254df361f81 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 29 Mar 2023 13:40:31 -0700 Subject: [PATCH 10/14] add code comments --- .gitignore | 1 + .vscode/settings.json | 10 ------ packages/client/src/connection.js | 12 +++++-- packages/interface/src/lib.ts | 49 +++++++++++++++++++++----- packages/transport/src/car/response.js | 1 - packages/transport/src/legacy.js | 12 +++++++ 6 files changed, 64 insertions(+), 21 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index f1aa94a1..d22d5ff4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules coverage .pnpm-debug.log .env +.vscode diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 98bdba45..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib", - "cSpell.words": [ - "bafyreiaxnmoptsqiehdff", - "blpptvdbenxcz", - "Unextractable", - "xgrbojw", - "xovn" - ] -} diff --git a/packages/client/src/connection.js b/packages/client/src/connection.js index 757d59cf..043ca2c6 100644 --- a/packages/client/src/connection.js +++ b/packages/client/src/connection.js @@ -46,6 +46,11 @@ class Connection { export const execute = async (workflow, connection) => { const request = await connection.codec.encode(workflow, connection) const response = await connection.channel.request(request) + // We may fail to decode the response if content type is not supported + // or if data was corrupted. We do not want to throw in such case however, + // because client will get an Error object as opposed to a receipt, to retain + // consistent client API with two kinds of errors we encode caught error as + // a receipts per workflow invocation. try { return await connection.codec.decode(response) } catch (error) { @@ -56,12 +61,15 @@ export const execute = async (workflow, connection) => { const receipt = await Receipt.issue({ ran: cid, result: { error: { ...cause, message } }, - // @ts-expect-error + // @ts-expect-error - we can not really sign a receipt without having + // an access to a signer which client does not have. In the future + // we will change client API requiring a signer to be passed in but + // for now we just use a dummy signer. issuer: { did() { return connection.id.did() }, - sign(payload) { + sign() { return Signature.createNonStandard('', new Uint8Array()) }, }, diff --git a/packages/interface/src/lib.ts b/packages/interface/src/lib.ts index 49b1d419..575e93b9 100644 --- a/packages/interface/src/lib.ts +++ b/packages/interface/src/lib.ts @@ -131,26 +131,59 @@ export interface DelegationOptions extends UCANOptions { } /** - * An interface for representing an IPLD DAG View that can be materialized into - * on demand. It is a useful abstraction that can be used to defer encoding of - * IPLD blocks. + * An interface for representing a materializable IPLD DAG View. It is a useful + * abstraction that can be used to defer actual IPLD encoding. + * + * Note that represented DAG could be partial implying that some of the blocks + * may not be included. This by design allowing a user to include whatever + * blocks they want to include. */ export interface IPLDViewBuilder { /** * Encodes all the blocks and creates a new IPLDView instance over them. Can - * be passed an multihasher to parameterize hashing algorithm. - * - * Please note that some `IPLDView`s also implement `IPLDViewBuilder` - * interface and they will discard any options. + * be passed a multihasher to specify a preferred hashing algorithm. Note + * that there is no guarantee that preferred hasher will be used, it is + * only a hint of preference and not a requirement. */ buildIPLDView(options?: Transport.EncodeOptions): Await> } +/** + * An interface for representing a materialized IPLD DAG View, which provides + * a generic traversal API. It is useful for encoding (potentially partial) IPLD + * DAGs into content archives (e.g. CARs). + */ export interface IPLDView extends IPLDViewBuilder { - buildIPLDView(): IPLDView + /** + * The root block of the IPLD DAG this is the view of. This is the the block + * from which all other blocks are linked directly or transitively. + */ root: Block + + /** + * Returns an iterable of all the IPLD blocks that are included in this view. + * It is RECOMMENDED that implementations return blocks in bottom up order + * (i.e. leaf blocks first, root block last). + * + * Iterator MUST include the root block otherwise it will lead to encoders + * into omitting it when encoding the view into a CAR archive. + * + * Note that we would like to rename this method to `blocks` but that would + * be a breaking change on the Delegate API so we defer it for now. + */ iterateIPLDBlocks(): IterableIterator + + /** + * `IPLDView` also implement `IPLDViewBuilder` API so that you could pass + * it anywhere builder is expected. Also note that `buildIPLDView` does not + * take any options, as the view is already materialized and user preferences + * will have no effect. + * + * Most implementations will just return `this` as they are already + * materialized views. + */ + buildIPLDView(): IPLDView } /** diff --git a/packages/transport/src/car/response.js b/packages/transport/src/car/response.js index c88dd8ef..ebf751ff 100644 --- a/packages/transport/src/car/response.js +++ b/packages/transport/src/car/response.js @@ -25,7 +25,6 @@ export const encode = async (receipts, options) => { for (const block of reader.iterateIPLDBlocks()) { blocks.set(block.cid.toString(), block) } - // blocks.delete(reader.root.cid.toString()) } const body = CAR.encode({ roots, blocks }) diff --git a/packages/transport/src/legacy.js b/packages/transport/src/legacy.js index f6800b5e..6c679407 100644 --- a/packages/transport/src/legacy.js +++ b/packages/transport/src/legacy.js @@ -32,11 +32,23 @@ export const CBOR = { }, } +/** + * This is an inbound codec designed to support legacy clients and encode + * responses in a legacy (CBOR) format. + */ export const inbound = Codec.inbound({ decoders: { 'application/car': CAR.request, }, encoders: { + // Here we configure encoders such that if accept header is `*/*` (which is + // the default if omitted) we will encode the response in CBOR. If + // `application/car` is set we will encode the response in current format + // is CAR. + // Here we exploit the fact that legacy clients do not send an accept header + // and therefore will get response in legacy format. New clients on the other + // hand will send `application/car` and consequently get response in current + // format. '*/*;q=0.1': CBOR, 'application/car': CAR.response, }, From 1dd23f7d9cf7d169715fc79eed152a5ade0edf8a Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 30 Mar 2023 08:47:29 -0700 Subject: [PATCH 11/14] Apply suggestions from code review Co-authored-by: Benjamin Goering <171782+gobengo@users.noreply.github.com> --- packages/client/src/connection.js | 2 ++ packages/core/test/receipt.spec.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/client/src/connection.js b/packages/client/src/connection.js index 043ca2c6..6c6bebed 100644 --- a/packages/client/src/connection.js +++ b/packages/client/src/connection.js @@ -54,6 +54,8 @@ export const execute = async (workflow, connection) => { try { return await connection.codec.decode(response) } catch (error) { + // No third party code is run during decode and we know + // we only throw an Error const { message, ...cause } = /** @type {Error} */ (error) const receipts = [] for await (const invocation of workflow) { diff --git a/packages/core/test/receipt.spec.js b/packages/core/test/receipt.spec.js index e476f43b..70848365 100644 --- a/packages/core/test/receipt.spec.js +++ b/packages/core/test/receipt.spec.js @@ -85,7 +85,7 @@ test('receipt with proofs', async () => { ], }) - const { cid: proofCid } = await await delegate({ + const { cid: proofCid } = await delegate({ issuer: w3, audience: bob, capabilities: [ From 67067298872b4527351657de0533c6201738808b Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 30 Mar 2023 08:47:51 -0700 Subject: [PATCH 12/14] Update packages/core/src/dag.js --- packages/core/src/dag.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/src/dag.js b/packages/core/src/dag.js index c411acc8..82dc3a59 100644 --- a/packages/core/src/dag.js +++ b/packages/core/src/dag.js @@ -5,7 +5,13 @@ import * as MF from 'multiformats/interface' import * as CBOR from './cbor.js' /** - * @param {unknown} value + * Function takes arbitrary value and if it happens to be an `IPLDView` + * it will iterate over it's blocks. It is just a convenience for traversing + * arbitrary structures that may contain `IPLDView`s in them. + * Note if you pass anything other than `IPLDView` it will not attempt + * to find views nested inside them, instead it will just emit no blocks. + * @param {unknown} value. + * * @returns {IterableIterator} */ export const iterate = function* (value) { From cf4c8bed1a8dc3f29637842a89f3129a2d1e3316 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 30 Mar 2023 08:51:20 -0700 Subject: [PATCH 13/14] Apply suggestions from code review --- packages/core/src/dag.js | 2 +- packages/core/src/invocation.js | 5 +++++ packages/core/src/receipt.js | 2 +- packages/core/test/invocation.spec.js | 3 +++ packages/interface/src/lib.ts | 6 ++++++ packages/interface/src/transport.ts | 3 +++ 6 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/core/src/dag.js b/packages/core/src/dag.js index 82dc3a59..1c6ffdf1 100644 --- a/packages/core/src/dag.js +++ b/packages/core/src/dag.js @@ -10,8 +10,8 @@ import * as CBOR from './cbor.js' * arbitrary structures that may contain `IPLDView`s in them. * Note if you pass anything other than `IPLDView` it will not attempt * to find views nested inside them, instead it will just emit no blocks. - * @param {unknown} value. * + * @param {unknown} value. * @returns {IterableIterator} */ export const iterate = function* (value) { diff --git a/packages/core/src/invocation.js b/packages/core/src/invocation.js index 986ec8d1..29d51a06 100644 --- a/packages/core/src/invocation.js +++ b/packages/core/src/invocation.js @@ -10,6 +10,11 @@ import * as DAG from './dag.js' export const invoke = options => new IssuedInvocation(options) /** + * Takes a link of the `root` block and a map of blocks and constructs an + * `Invocation` from it. If `root` is not included in the provided blocks it + * throws an error. If root points to wrong block (that is not an invocation) + * it will misbehave and likely throw some errors on field access. + * * @template {API.Capability} C * @param {object} dag * @param {API.UCANLink<[C]>} dag.root diff --git a/packages/core/src/receipt.js b/packages/core/src/receipt.js index 1a48f53c..24725b87 100644 --- a/packages/core/src/receipt.js +++ b/packages/core/src/receipt.js @@ -21,7 +21,7 @@ export const view = ({ root, blocks }) => { /** * @template {{}} Ok * @template {{}} Error - * @template {API.Invocation} Ran + * @template {API.Invocation} Ran - Invocation this is a receipt of. * @template {API.SigAlg} [SigAlg=API.SigAlg] * @implements {API.Receipt} */ diff --git a/packages/core/test/invocation.spec.js b/packages/core/test/invocation.spec.js index b2b37cfb..c34e9b60 100644 --- a/packages/core/test/invocation.spec.js +++ b/packages/core/test/invocation.spec.js @@ -154,6 +154,9 @@ test('execute invocation', async () => { }, }) + // Note that types expect us to return receipt but here we + // just test that execute simply returns `connection.execute(this)[0]` which + // will not be a receipt due to our implementation above. // @ts-expect-error assert.deepEqual(result, { hello: 'world' }) }) diff --git a/packages/interface/src/lib.ts b/packages/interface/src/lib.ts index 575e93b9..6658abe1 100644 --- a/packages/interface/src/lib.ts +++ b/packages/interface/src/lib.ts @@ -439,12 +439,18 @@ export interface Receipt< export interface Meta extends Record {} +/** + * @see https://github.com/ucan-wg/invocation/blob/v0.2/README.md#7-effect + */ export interface EffectsModel { fork: readonly Link[] join?: Link } export interface Effects extends EffectsModel {} +/** + * @see https://github.com/ucan-wg/invocation/blob/v0.2/README.md#511-instruction + */ export interface InstructionModel< Op extends Ability = Ability, URI extends Resource = Resource, diff --git a/packages/interface/src/transport.ts b/packages/interface/src/transport.ts index 3ff4a68b..404a196b 100644 --- a/packages/interface/src/transport.ts +++ b/packages/interface/src/transport.ts @@ -24,6 +24,9 @@ export interface EncodeOptions { } export interface RequestEncodeOptions extends EncodeOptions { + /** + * If provided will be set as an `accept` header of the request. + */ accept?: string } From 608b21989a93a4e4fbe582017e49b91451a5292e Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 30 Mar 2023 08:53:51 -0700 Subject: [PATCH 14/14] fix jsdoc comment --- packages/core/src/dag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/dag.js b/packages/core/src/dag.js index 1c6ffdf1..29f91dea 100644 --- a/packages/core/src/dag.js +++ b/packages/core/src/dag.js @@ -11,7 +11,7 @@ import * as CBOR from './cbor.js' * Note if you pass anything other than `IPLDView` it will not attempt * to find views nested inside them, instead it will just emit no blocks. * - * @param {unknown} value. + * @param {unknown} value * @returns {IterableIterator} */ export const iterate = function* (value) {