diff --git a/packages/access-client/src/access.js b/packages/access-client/src/access.js index 2fc5dcd00..79cb76c9b 100644 --- a/packages/access-client/src/access.js +++ b/packages/access-client/src/access.js @@ -325,6 +325,7 @@ export const toCapabilities = (access) => { export const spaceAccess = { 'space/*': {}, 'blob/*': {}, + 'index/*': {}, 'store/*': {}, 'upload/*': {}, 'access/*': {}, diff --git a/packages/upload-api/src/blob/accept.js b/packages/upload-api/src/blob/accept.js index 3666d06e4..17f0fe173 100644 --- a/packages/upload-api/src/blob/accept.js +++ b/packages/upload-api/src/blob/accept.js @@ -35,7 +35,9 @@ export function blobAcceptProvider(context) { const url = /** @type {API.URI<'https:'>} */ ( - `https://w3s.link/ipfs/${content}?format=raw&origin=${encodeURIComponent(`r2://${R2_REGION}/${R2_BUCKET}`)}` + `https://w3s.link/ipfs/${content}?format=raw&origin=${encodeURIComponent( + `r2://${R2_REGION}/${R2_BUCKET}` + )}` ) const locationClaim = await Assert.location.delegate({ diff --git a/packages/upload-api/test/storage/blobs-storage.js b/packages/upload-api/test/storage/blobs-storage.js index 48e16607c..595a9d2a3 100644 --- a/packages/upload-api/test/storage/blobs-storage.js +++ b/packages/upload-api/test/storage/blobs-storage.js @@ -135,6 +135,18 @@ export class BlobsStorage { */ async stream(digest) { const key = this.#bucketPath(digest) + if (!this.server) { + const url = new URL(key, this.baseURL) + const res = await fetch(url.toString()) + if (res.status === 404) return error(new BlobNotFound(digest)) + if (!res.ok || !res.body) { + throw new Error( + `serverless blob storage failed to fetch from: ${url} status: ${res.status}` + ) + } + return ok(res.body) + } + const bytes = this.content.get(key) if (!bytes) return error(new BlobNotFound(digest)) diff --git a/packages/upload-client/README.md b/packages/upload-client/README.md index f51c19fcd..f75a10b74 100644 --- a/packages/upload-client/README.md +++ b/packages/upload-client/README.md @@ -77,16 +77,23 @@ const cid = await uploadDirectory(conf, [ The buffering API loads all data into memory so is suitable only for small files. The root data CID is derived from the data before any transfer to the service takes place. ```js -import { UnixFS, CAR, Store, Upload } from '@web3-storage/upload-client' +import { UnixFS, CAR, Blob, Index, Upload } from '@web3-storage/upload-client' +import * as BlobIndexUtil from '@web3-storage/blob-index/util' +import * as Link from 'multiformats/link' // Encode a file as a DAG, get back a root data CID and a set of blocks const { cid, blocks } = await UnixFS.encodeFile(file) // Encode the DAG as a CAR file const car = await CAR.encode(blocks, cid) // Store the CAR file to the service -const carCID = await Store.add(conf, car) +const carDigest = await Blob.add(conf, car) +// Create an index +const index = await BlobIndexUtil.fromShardArchives(cid, [new Uint8Array(await car.arrayBuffer())]) +// Store the index to the service +const indexDigest = await Blob.add(conf, (await index.archive()).ok) +await Index.add(conf, Link.create(CAR.code, indexDigest)) // Register an "upload" - a root CID contained within the passed CAR file(s) -await Upload.add(conf, cid, [carCID]) +await Upload.add(conf, cid, [Link.create(CAR.code, carDigest)]) ``` #### Streaming API @@ -97,11 +104,14 @@ This API offers streaming DAG generation, allowing CAR "shards" to be sent to th import { UnixFS, ShardingStream, - Store, + Blob, + Index, Upload, } from '@web3-storage/upload-client' +import { ShardedDAGIndex } from '@web3-storage/blob-index' let rootCID, carCIDs +const shardIndexes = [] // Encode a file as a DAG, get back a readable stream of blocks. await UnixFS.createFileEncoderStream(file) // Pipe blocks to a stream that yields CARs files - shards of the DAG. @@ -111,13 +121,29 @@ await UnixFS.createFileEncoderStream(file) .pipeTo( new WritableStream({ async write (car) { - const carCID = await Store.add(conf, car) - carCIDs.push(carCID) + const carDigest = await Blob.add(conf, car) + carCIDs.push(Link.create(CAR.code, carDigest)) + + // add the CAR shard itself to the slices + meta.slices.set(carDigest, [0, car.size]) + shardIndexes.push(car.slices) + rootCID = rootCID || car.roots[0] }, }) ) +// Combine the shard indexes to create the complete DAG index +const index = ShardedDAGIndex.create(rootCID) +for (const [i, shard] of carCIDs.entries()) { + const slices = shardIndexes[i] + index.shards.set(shard.multihash, slices) +} + +// Store the index to the service +const indexDigest = await Blob.add(conf, (await index.archive()).ok) +await Index.add(conf, Link.create(CAR.code, indexDigest)) + // Register an "upload" - a root CID contained within the passed CAR file(s) await Upload.add(conf, rootCID, carCIDs) ``` @@ -135,12 +161,13 @@ await Upload.add(conf, rootCID, carCIDs) - [`uploadDirectory`](#uploaddirectory) - [`uploadFile`](#uploadfile) - [`uploadCAR`](#uploadcar) + - [`Blob.add`](#blobadd) + - [`Blob.list`](#bloblist) + - [`Blob.remove`](#blobremove) - [`CAR.BlockStream`](#carblockstream) - [`CAR.encode`](#carencode) + - [`Index.add`](#indexadd) - [`ShardingStream`](#shardingstream) - - [`Store.add`](#storeadd) - - [`Store.list`](#storelist) - - [`Store.remove`](#storeremove) - [`UnixFS.createDirectoryEncoderStream`](#unixfscreatedirectoryencoderstream) - [`UnixFS.createFileEncoderStream`](#unixfscreatefileencoderstream) - [`UnixFS.encodeDirectory`](#unixfsencodedirectory) @@ -178,7 +205,7 @@ function uploadDirectory( Uploads a directory of files to the service and returns the root data CID for the generated DAG. All files are added to a container directory, with paths in file names preserved. -Required delegated capability proofs: `store/add`, `upload/add` +Required delegated capability proofs: `blob/add`, `index/add`, `upload/add`, `filecoin/offer` More information: [`InvocationConfig`](#invocationconfig), [`ShardStoredCallback`](#shardstoredcallback) @@ -200,7 +227,7 @@ function uploadFile( Uploads a file to the service and returns the root data CID for the generated DAG. -Required delegated capability proofs: `store/add`, `upload/add` +Required delegated capability proofs: `blob/add`, `index/add`, `upload/add`, `filecoin/offer` More information: [`InvocationConfig`](#invocationconfig) @@ -221,12 +248,58 @@ function uploadCAR( ): Promise ``` -Uploads a CAR file to the service. The difference between this function and [Store.add](#storeadd) is that the CAR file is automatically sharded and an "upload" is registered (see [`Upload.add`](#uploadadd)), linking the individual shards. Use the `onShardStored` callback to obtain the CIDs of the CAR file shards. +Uploads a CAR file to the service. The difference between this function and [Blob.add](#blobadd) is that the CAR file is automatically sharded, an index is generated, uploaded and registered (see [`Index.add`](#indexadd)) and finally an "upload" is registered (see [`Upload.add`](#uploadadd)), linking the individual shards. Use the `onShardStored` callback to obtain the CIDs of the CAR file shards. -Required delegated capability proofs: `store/add`, `upload/add` +Required delegated capability proofs: `blob/add`, `index/add`, `upload/add`, `filecoin/offer` More information: [`InvocationConfig`](#invocationconfig), [`ShardStoredCallback`](#shardstoredcallback) +### `Blob.add` + +```ts +function add( + blob: Blob, + options: { retries?: number; signal?: AbortSignal } = {} +): Promise +``` + +Store a blob to the service. + +Required delegated capability proofs: `blob/add` + +More information: [`InvocationConfig`](#invocationconfig) + +### `Blob.list` + +```ts +function list( + conf: InvocationConfig, + options: { retries?: number; signal?: AbortSignal } = {} +): Promise> +``` + +List blobs stored in the space. + +Required delegated capability proofs: `blob/list` + +More information: [`InvocationConfig`](#invocationconfig) + +### `Blob.remove` + +```ts +function remove( + conf: InvocationConfig, + digest: MultihashDigest, + options: { retries?: number; signal?: AbortSignal } = {} +): Promise +``` + +Remove a stored blob by multihash digest. + +Required delegated capability proofs: `blob/remove` + +More information: [`InvocationConfig`](#invocationconfig) + ### `CAR.BlockStream` ```ts @@ -252,46 +325,31 @@ const { cid, blocks } = await UnixFS.encodeFile(new Blob(['data'])) const car = await CAR.encode(blocks, cid) ``` -### `ShardingStream` - -```ts -class ShardingStream extends TransformStream -``` - -Shard a set of blocks into a set of CAR files. The last block written to the stream is assumed to be the DAG root and becomes the CAR root CID for the last CAR output. - -More information: [`CARFile`](#carfile) - -### `Store.list` +### `Index.add` ```ts -function list( +function add( conf: InvocationConfig, + index: CID, options: { retries?: number; signal?: AbortSignal } = {} -): Promise> +): Promise ``` -List CAR files stored by the issuer. +Register an "index" with the service. The `index` CID should be the CID of a CAR file, containing an index ad defined by [w3-index](https://github.com/w3s-project/specs/blob/main/w3-index.md). -Required delegated capability proofs: `store/list` +Required delegated capability proofs: `index/add` More information: [`InvocationConfig`](#invocationconfig) -### `Store.remove` +### `ShardingStream` ```ts -function remove( - conf: InvocationConfig, - link: CID, - options: { retries?: number; signal?: AbortSignal } = {} -): Promise +class ShardingStream extends TransformStream ``` -Remove a stored CAR file by CAR CID. - -Required delegated capability proofs: `store/remove` +Shard a set of blocks into a set of CAR files. The last block written to the stream is assumed to be the DAG root and becomes the CAR root CID for the last CAR output. -More information: [`InvocationConfig`](#invocationconfig) +More information: [`CARFile`](#carfile) ### `UnixFS.createDirectoryEncoderStream` diff --git a/packages/upload-client/package.json b/packages/upload-client/package.json index 59dce8924..5e68d7164 100644 --- a/packages/upload-client/package.json +++ b/packages/upload-client/package.json @@ -75,10 +75,11 @@ "@ipld/dag-cbor": "^9.0.6", "@ipld/dag-ucan": "^3.4.0", "@ipld/unixfs": "^2.1.1", - "@ucanto/core": "^10.0.1", "@ucanto/client": "^9.0.1", + "@ucanto/core": "^10.0.1", "@ucanto/interface": "^10.0.1", "@ucanto/transport": "^9.1.1", + "@web3-storage/blob-index": "workspace:^", "@web3-storage/capabilities": "workspace:^", "@web3-storage/data-segment": "^5.1.0", "@web3-storage/filecoin-client": "workspace:^", diff --git a/packages/upload-client/src/car.js b/packages/upload-client/src/car.js index 773170a40..73e50ddb5 100644 --- a/packages/upload-client/src/car.js +++ b/packages/upload-client/src/car.js @@ -6,8 +6,10 @@ import varint from 'varint' * @typedef {import('@ipld/unixfs').Block} Block */ +export const code = 0x0202 + /** Byte length of a CBOR encoded CAR header with zero roots. */ -const NO_ROOTS_HEADER_LENGTH = 17 +const NO_ROOTS_HEADER_LENGTH = 18 /** @param {import('./types.js').AnyLink} [root] */ export function headerEncodingLength(root) { @@ -18,10 +20,15 @@ export function headerEncodingLength(root) { } /** @param {Block} block */ -export function blockEncodingLength(block) { +export function blockHeaderEncodingLength(block) { const payloadLength = block.cid.bytes.length + block.bytes.length const varintLength = varint.encodingLength(payloadLength) - return varintLength + payloadLength + return varintLength + block.cid.bytes.length +} + +/** @param {Block} block */ +export function blockEncodingLength(block) { + return blockHeaderEncodingLength(block) + block.bytes.length } /** diff --git a/packages/upload-client/src/dag-index.js b/packages/upload-client/src/dag-index.js new file mode 100644 index 000000000..572d8c046 --- /dev/null +++ b/packages/upload-client/src/dag-index.js @@ -0,0 +1,63 @@ +import * as IndexCapabilities from '@web3-storage/capabilities/index' +import { SpaceDID } from '@web3-storage/capabilities/utils' +import retry from 'p-retry' +import { servicePrincipal, connection } from './service.js' +import { REQUEST_RETRIES } from './constants.js' + +/** + * Register an "index" with the service. The issuer needs the `index/add` + * delegated capability. + * + * Required delegated capability proofs: `index/add` + * + * @param {import('./types.js').InvocationConfig} conf Configuration + * for the UCAN invocation. An object with `issuer`, `with` and `proofs`. + * + * The `issuer` is the signing authority that is issuing the UCAN + * invocation(s). It is typically the user _agent_. + * + * The `with` is the resource the invocation applies to. It is typically the + * DID of a space. + * + * The `proofs` are a set of capability delegations that prove the issuer + * has the capability to perform the action. + * + * The issuer needs the `index/add` delegated capability. + * @param {import('./types.js').CARLink} index Index to store. + * @param {import('./types.js').RequestOptions} [options] + * @returns {Promise} + */ +export async function add( + { issuer, with: resource, proofs, audience }, + index, + options = {} +) { + /* c8 ignore next */ + const conn = options.connection ?? connection + const result = await retry( + async () => { + return await IndexCapabilities.add + .invoke({ + issuer, + /* c8 ignore next */ + audience: audience ?? servicePrincipal, + with: SpaceDID.from(resource), + nb: { index }, + proofs, + }) + .execute(conn) + }, + { + onFailedAttempt: console.warn, + retries: options.retries ?? REQUEST_RETRIES, + } + ) + + if (!result.out.ok) { + throw new Error(`failed ${IndexCapabilities.add.can} invocation`, { + cause: result.out.error, + }) + } + + return result.out.ok +} diff --git a/packages/upload-client/src/index.js b/packages/upload-client/src/index.js index ac714d5d5..0f573e57c 100644 --- a/packages/upload-client/src/index.js +++ b/packages/upload-client/src/index.js @@ -1,23 +1,26 @@ import * as PieceHasher from '@web3-storage/data-segment/multihash' import { Storefront } from '@web3-storage/filecoin-client' +import { ShardedDAGIndex } from '@web3-storage/blob-index' import * as Link from 'multiformats/link' import * as raw from 'multiformats/codecs/raw' import * as Store from './store.js' import * as Blob from './blob.js' +import * as Index from './dag-index.js' import * as Upload from './upload.js' import * as UnixFS from './unixfs.js' import * as CAR from './car.js' import { ShardingStream, defaultFileComparator } from './sharding.js' import { codec as carCodec } from '@ucanto/transport/car' -export { Blob, Store, Upload, UnixFS, CAR } +export { Blob, Index, Store, Upload, UnixFS, CAR } export * from './sharding.js' /** * Uploads a file to the service and returns the root data CID for the * generated DAG. * - * Required delegated capability proofs: `store/add`, `upload/add` + * Required delegated capability proofs: `blob/add`, `index/add`, + * `filecoin/offer`, `upload/add` * * @param {import('./types.js').InvocationConfig} conf Configuration * for the UCAN invocation. An object with `issuer`, `with` and `proofs`. @@ -31,7 +34,8 @@ export * from './sharding.js' * The `proofs` are a set of capability delegations that prove the issuer * has the capability to perform the action. * - * The issuer needs the `store/add` and `upload/add` delegated capability. + * The issuer needs the `blob/add`, `index/add`, `filecoin/offer` and + * `upload/add` delegated capability. * @param {import('./types.js').BlobLike} file File data. * @param {import('./types.js').UploadOptions} [options] */ @@ -48,7 +52,8 @@ export async function uploadFile(conf, file, options = {}) { * for the generated DAG. All files are added to a container directory, with * paths in file names preserved. * - * Required delegated capability proofs: `store/add`, `upload/add` + * Required delegated capability proofs: `blob/add`, `index/add`, + * `filecoin/offer`, `upload/add` * * @param {import('./types.js').InvocationConfig} conf Configuration * for the UCAN invocation. An object with `issuer`, `with` and `proofs`. @@ -62,7 +67,8 @@ export async function uploadFile(conf, file, options = {}) { * The `proofs` are a set of capability delegations that prove the issuer * has the capability to perform the action. * - * The issuer needs the `store/add` and `upload/add` delegated capability. + * The issuer needs the `blob/add`, `index/add`, `filecoin/offer` and + * `upload/add` delegated capability. * @param {import('./types.js').FileLike[]} files Files that should be in the directory. * To ensure determinism in the IPLD encoding, files are automatically sorted by `file.name`. * To retain the order of the files as passed in the array, set `customOrder` option to `true`. @@ -87,7 +93,8 @@ export async function uploadDirectory(conf, files, options = {}) { * * Use the `onShardStored` callback to obtain the CIDs of the CAR file shards. * - * Required delegated capability proofs: `store/add`, `upload/add` + * Required delegated capability proofs: `blob/add`, `index/add`, + * `filecoin/offer`, `upload/add` * * @param {import('./types.js').InvocationConfig} conf Configuration * for the UCAN invocation. An object with `issuer`, `with` and `proofs`. @@ -101,7 +108,7 @@ export async function uploadDirectory(conf, files, options = {}) { * The `proofs` are a set of capability delegations that prove the issuer * has the capability to perform the action. * - * The issuer needs the `store/add` and `upload/add` delegated capability. + * The issuer needs the `blob/add`, `index/add`, `filecoin/offer` and `upload/add` delegated capability. * @param {import('./types.js').BlobLike} car CAR file. * @param {import('./types.js').UploadOptions} [options] */ @@ -122,6 +129,8 @@ async function uploadBlockStream( blocks, { pieceHasher = PieceHasher, ...options } = {} ) { + /** @type {Array>} */ + const shardIndexes = [] /** @type {import('./types.js').CARLink[]} */ const shards = [] /** @type {import('./types.js').AnyLink?} */ @@ -129,51 +138,59 @@ async function uploadBlockStream( await blocks .pipeThrough(new ShardingStream(options)) .pipeThrough( - new TransformStream({ - async transform(car, controller) { - const bytes = new Uint8Array(await car.arrayBuffer()) - // Invoke blob/add and write bytes to write target - const multihash = await Blob.add(conf, bytes, options) - // Should this be raw instead? - const cid = Link.create(carCodec.code, multihash) - let piece - if (pieceHasher) { - const multihashDigest = await pieceHasher.digest(bytes) - /** @type {import('@web3-storage/capabilities/types').PieceLink} */ - piece = Link.create(raw.code, multihashDigest) - const content = Link.create(raw.code, multihash) + /** @type {TransformStream} */ + ( + new TransformStream({ + async transform(car, controller) { + const bytes = new Uint8Array(await car.arrayBuffer()) + // Invoke blob/add and write bytes to write target + const multihash = await Blob.add(conf, bytes, options) + // Should this be raw instead? + const cid = Link.create(carCodec.code, multihash) + let piece + if (pieceHasher) { + const multihashDigest = await pieceHasher.digest(bytes) + /** @type {import('@web3-storage/capabilities/types').PieceLink} */ + piece = Link.create(raw.code, multihashDigest) + const content = Link.create(raw.code, multihash) - // Invoke filecoin/offer for data - const result = await Storefront.filecoinOffer( - { - issuer: conf.issuer, - audience: conf.audience, - // Resource of invocation is the issuer did for being self issued - with: conf.issuer.did(), - proofs: conf.proofs, - }, - content, - piece, - options - ) - - if (result.out.error) { - throw new Error( - 'failed to offer piece for aggregation into filecoin deal', - { cause: result.out.error } + // Invoke filecoin/offer for data + const result = await Storefront.filecoinOffer( + { + issuer: conf.issuer, + audience: conf.audience, + // Resource of invocation is the issuer did for being self issued + with: conf.issuer.did(), + proofs: conf.proofs, + }, + content, + piece, + options ) + + if (result.out.error) { + throw new Error( + 'failed to offer piece for aggregation into filecoin deal', + { cause: result.out.error } + ) + } } - } - const { version, roots, size } = car - controller.enqueue({ version, roots, size, cid, piece }) - }, - }) + const { version, roots, size, slices } = car + controller.enqueue({ version, roots, size, cid, piece, slices }) + }, + }) + ) ) .pipeTo( new WritableStream({ write(meta) { root = root || meta.roots[0] shards.push(meta.cid) + + // add the CAR shard itself to the slices + meta.slices.set(meta.cid.multihash, [0, meta.size]) + shardIndexes.push(meta.slices) + if (options.onShardStored) options.onShardStored(meta) }, }) @@ -182,6 +199,25 @@ async function uploadBlockStream( /* c8 ignore next */ if (!root) throw new Error('missing root CID') + const index = ShardedDAGIndex.create(root) + for (const [i, shard] of shards.entries()) { + const slices = shardIndexes[i] + index.shards.set(shard.multihash, slices) + } + const indexBytes = await index.archive() + /* c8 ignore next 3 */ + if (!indexBytes.ok) { + throw new Error('failed to archive DAG index', { cause: indexBytes.error }) + } + + // Store the index in the space + const indexDigest = await Blob.add(conf, indexBytes.ok, options) + const indexLink = Link.create(carCodec.code, indexDigest) + + // Register the index with the service + await Index.add(conf, indexLink, options) + // Register an upload with the service await Upload.add(conf, root, shards, options) + return root } diff --git a/packages/upload-client/src/sharding.js b/packages/upload-client/src/sharding.js index 9070326fd..39848f566 100644 --- a/packages/upload-client/src/sharding.js +++ b/packages/upload-client/src/sharding.js @@ -1,4 +1,10 @@ -import { blockEncodingLength, encode, headerEncodingLength } from './car.js' +import { DigestMap } from '@web3-storage/blob-index' +import { + blockEncodingLength, + blockHeaderEncodingLength, + encode, + headerEncodingLength, +} from './car.js' /** * @typedef {import('./types.js').FileLike} FileLike @@ -12,7 +18,7 @@ const SHARD_SIZE = 133_169_152 * received is assumed to be the DAG root and becomes the CAR root CID for the * last CAR output. Set the `rootCID` option to override. * - * @extends {TransformStream} + * @extends {TransformStream} */ export class ShardingStream extends TransformStream { /** @@ -25,16 +31,22 @@ export class ShardingStream extends TransformStream { let blocks = [] /** @type {import('@ipld/unixfs').Block[] | null} */ let readyBlocks = null + /** @type {Map} */ + let slices = new DigestMap() + /** @type {Map | null} */ + let readySlices = null let currentLength = 0 super({ async transform(block, controller) { - if (readyBlocks != null) { - controller.enqueue(await encode(readyBlocks)) + if (readyBlocks != null && readySlices != null) { + controller.enqueue(await encodeCAR(readyBlocks, readySlices)) readyBlocks = null + readySlices = null } - const blockLength = blockEncodingLength(block) + const blockHeaderLength = blockHeaderEncodingLength(block) + const blockLength = blockHeaderLength + block.bytes.length if (blockLength > maxBlockLength) { throw new Error( `block will cause CAR to exceed shard size: ${block.cid}` @@ -43,16 +55,22 @@ export class ShardingStream extends TransformStream { if (blocks.length && currentLength + blockLength > maxBlockLength) { readyBlocks = blocks + readySlices = slices blocks = [] + slices = new DigestMap() currentLength = 0 } blocks.push(block) + slices.set(block.cid.multihash, [ + headerEncodingLength() + currentLength + blockHeaderLength, + block.bytes.length, + ]) currentLength += blockLength }, async flush(controller) { - if (readyBlocks != null) { - controller.enqueue(await encode(readyBlocks)) + if (readyBlocks != null && readySlices != null) { + controller.enqueue(await encodeCAR(readyBlocks, readySlices)) } const rootBlock = blocks.at(-1) @@ -62,7 +80,7 @@ export class ShardingStream extends TransformStream { const headerLength = headerEncodingLength(rootCID) // if adding CAR root overflows the shard limit we move overflowing - // blocks into a another CAR. + // blocks into another CAR. if (headerLength + currentLength > shardSize) { const overage = headerLength + currentLength - shardSize const overflowBlocks = [] @@ -70,6 +88,7 @@ export class ShardingStream extends TransformStream { while (overflowCurrentLength < overage) { const block = blocks[blocks.length - 1] blocks.pop() + slices.delete(block.cid.multihash) overflowBlocks.unshift(block) overflowCurrentLength += blockEncodingLength(block) @@ -79,10 +98,32 @@ export class ShardingStream extends TransformStream { `block will cause CAR to exceed shard size: ${block.cid}` ) } - controller.enqueue(await encode(blocks)) - controller.enqueue(await encode(overflowBlocks, rootCID)) + controller.enqueue(await encodeCAR(blocks, slices)) + + // Finally, re-calc block positions from blocks we moved out of the + // CAR that was too big. + overflowCurrentLength = 0 + /** @type {Map} */ + const overflowSlices = new DigestMap() + for (const block of blocks) { + const overflowBlockHeaderLength = blockHeaderEncodingLength(block) + overflowSlices.set(block.cid.multihash, [ + headerLength + overflowCurrentLength + overflowBlockHeaderLength, + block.bytes.length, + ]) + overflowCurrentLength += + overflowBlockHeaderLength + block.bytes.length + } + controller.enqueue( + await encodeCAR(overflowBlocks, overflowSlices, rootCID) + ) } else { - controller.enqueue(await encode(blocks, rootCID)) + // adjust offsets for longer header in final shard + const diff = headerLength - headerEncodingLength() + for (const slice of slices.values()) { + slice[0] += diff + } + controller.enqueue(await encodeCAR(blocks, slices, rootCID)) } }, }) @@ -119,3 +160,12 @@ function ascending(a, b, getComparedValue) { else if (ask < bsk) return -1 return 1 } + +/** + * @param {Iterable} blocks + * @param {Map} slices + * @param {import('./types.js').AnyLink} [root] + * @returns {Promise} + */ +const encodeCAR = async (blocks, slices, root) => + Object.assign(await encode(blocks, root), { slices }) diff --git a/packages/upload-client/src/types.ts b/packages/upload-client/src/types.ts index b72af7272..8090ae110 100644 --- a/packages/upload-client/src/types.ts +++ b/packages/upload-client/src/types.ts @@ -31,6 +31,9 @@ import { BlobList, BlobListSuccess, BlobListFailure, + IndexAdd, + IndexAddSuccess, + IndexAddFailure, StoreAdd, StoreAddSuccess, StoreAddSuccessUpload, @@ -63,6 +66,12 @@ import { } from '@web3-storage/capabilities/types' import { StorefrontService } from '@web3-storage/filecoin-client/storefront' import { code as pieceHashCode } from '@web3-storage/data-segment/multihash' +import { + ShardedDAGIndex, + ShardDigest, + SliceDigest, + Position, +} from '@web3-storage/blob-index/types' type Override = Omit & R @@ -89,6 +98,9 @@ export type { BlobList, BlobListSuccess, BlobListFailure, + IndexAdd, + IndexAddSuccess, + IndexAddFailure, StoreAdd, StoreAddSuccess, StoreAddSuccessUpload, @@ -116,6 +128,10 @@ export type { ListResponse, CARLink, PieceLink, + ShardedDAGIndex, + ShardDigest, + SliceDigest, + Position, } export interface ProgressStatus extends XHRProgressStatus { @@ -137,6 +153,9 @@ export interface Service extends StorefrontService { remove: ServiceMethod list: ServiceMethod } + index: { + add: ServiceMethod + } store: { add: ServiceMethod get: ServiceMethod @@ -203,6 +222,19 @@ export interface CARHeaderInfo { */ export interface CARFile extends CARHeaderInfo, Blob {} +/** + * An indexed blob. + */ +export interface BlobIndex { + /** Slices and their offset/length within the blob. */ + slices: Map +} + +/** + * A DAG encoded as a CAR with added index information. + */ +export interface IndexedCARFile extends CARFile, BlobIndex {} + /** * Any IPLD link. */ @@ -211,7 +243,7 @@ export type AnyLink = Link /** * Metadata pertaining to a CAR file. */ -export interface CARMetadata extends CARHeaderInfo { +export interface CARMetadata extends CARHeaderInfo, BlobIndex { /** * CID of the CAR file (not the data it contains). */ diff --git a/packages/upload-client/test/dag-index.test.js b/packages/upload-client/test/dag-index.test.js new file mode 100644 index 000000000..5767c9869 --- /dev/null +++ b/packages/upload-client/test/dag-index.test.js @@ -0,0 +1,105 @@ +import assert from 'assert' +import * as Client from '@ucanto/client' +import * as Server from '@ucanto/server' +import * as CAR from '@ucanto/transport/car' +import * as Signer from '@ucanto/principal/ed25519' +import * as IndexCapabilities from '@web3-storage/capabilities/index' +import * as Index from '../src/dag-index.js' +import { serviceSigner } from './fixtures.js' +import { randomCAR } from './helpers/random.js' +import { mockService } from './helpers/mocks.js' +import { validateAuthorization } from './helpers/utils.js' + +describe('Index.add', () => { + it('Registers a DAG index with the service', async () => { + const space = await Signer.generate() + const agent = await Signer.generate() + const car = await randomCAR(128) + + const proofs = [ + await IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), + ] + + const service = mockService({ + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert.equal(capability.nb.index.toString(), car.cid.toString()) + return Server.ok({}) + }, + }), + }, + }) + + const server = Server.create({ + id: serviceSigner, + service, + codec: CAR.inbound, + validateAuthorization, + }) + const connection = Client.connect({ + id: serviceSigner, + codec: CAR.outbound, + channel: server, + }) + + await Index.add( + { issuer: agent, with: space.did(), proofs, audience: serviceSigner }, + car.cid, + { connection } + ) + }) + + it('throws on service error', async () => { + const space = await Signer.generate() + const agent = await Signer.generate() + const car = await randomCAR(128) + + const proofs = [ + await IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), + ] + + const service = mockService({ + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async () => { + throw new Server.Failure('boom') + }, + }), + }, + }) + + const server = Server.create({ + id: serviceSigner, + service, + codec: CAR.inbound, + validateAuthorization, + }) + const connection = Client.connect({ + id: serviceSigner, + codec: CAR.outbound, + channel: server, + }) + + await assert.rejects( + Index.add( + { issuer: agent, with: space.did(), proofs, audience: serviceSigner }, + car.cid, + { connection } + ), + { message: 'failed index/add invocation' } + ) + }) +}) diff --git a/packages/upload-client/test/helpers/mocks.js b/packages/upload-client/test/helpers/mocks.js index 057312d6a..bf863a1b4 100644 --- a/packages/upload-client/test/helpers/mocks.js +++ b/packages/upload-client/test/helpers/mocks.js @@ -8,6 +8,7 @@ const notImplemented = () => { * @param {Partial<{ * ucan: Partial * blob: Partial + * index: Partial * store: Partial * upload: Partial * usage: Partial @@ -24,6 +25,9 @@ export function mockService(impl) { list: withCallCount(impl.blob?.list ?? notImplemented), remove: withCallCount(impl.blob?.remove ?? notImplemented), }, + index: { + add: withCallCount(impl.index?.add ?? notImplemented), + }, store: { add: withCallCount(impl.store?.add ?? notImplemented), get: withCallCount(impl.store?.get ?? notImplemented), diff --git a/packages/upload-client/test/helpers/random.js b/packages/upload-client/test/helpers/random.js index 5cce080e8..9d3e6f1e8 100644 --- a/packages/upload-client/test/helpers/random.js +++ b/packages/upload-client/test/helpers/random.js @@ -19,7 +19,7 @@ export async function randomBytes(size) { } else { crypto.getRandomValues(chunk) } - size -= bytes.length + size -= chunk.length bytes.set(chunk, size) } return bytes diff --git a/packages/upload-client/test/index.test.js b/packages/upload-client/test/index.test.js index 77173d4f6..8808db526 100644 --- a/packages/upload-client/test/index.test.js +++ b/packages/upload-client/test/index.test.js @@ -6,6 +6,7 @@ import * as CAR from '@ucanto/transport/car' import * as Signer from '@ucanto/principal/ed25519' import * as UCAN from '@web3-storage/capabilities/ucan' import * as BlobCapabilities from '@web3-storage/capabilities/blob' +import * as IndexCapabilities from '@web3-storage/capabilities/index' import * as UploadCapabilities from '@web3-storage/capabilities/upload' import * as StorefrontCapabilities from '@web3-storage/capabilities/filecoin/storefront' import { Piece } from '@web3-storage/data-segment' @@ -47,6 +48,12 @@ describe('uploadFile', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -77,6 +84,15 @@ describe('uploadFile', () => { } ), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -131,9 +147,11 @@ describe('uploadFile', () => { ) assert(service.blob.add.called) - assert.equal(service.blob.add.callCount, 1) + assert.equal(service.blob.add.callCount, 2) assert(service.filecoin.offer.called) assert.equal(service.filecoin.offer.callCount, 1) + assert(service.index.add.called) + assert.equal(service.index.add.callCount, 1) assert(service.upload.add.called) assert.equal(service.upload.add.callCount, 1) @@ -157,6 +175,12 @@ describe('uploadFile', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -180,6 +204,15 @@ describe('uploadFile', () => { ) }), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -218,12 +251,12 @@ describe('uploadFile', () => { connection, // chunk size = 1_048_576 // encoded block size = 1_048_615 - // shard size = 2_097_152 (as configured below) + // shard size = 2_097_153 (as configured below) // total file size = 5_242_880 (as above) // so, at least 2 shards, but 2 encoded blocks (_without_ CAR header) = 2_097_230 - // ...which is > shard size of 2_097_152 + // ...which is > shard size of 2_097_153 // so we actually end up with a shard for each block - 5 CARs! - shardSize: 1024 * 1024 * 2, + shardSize: 1024 * 1024 * 2 + 1, onShardStored: (meta) => carCIDs.push(meta.cid), } ) @@ -331,6 +364,12 @@ describe('uploadDirectory', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -359,6 +398,15 @@ describe('uploadDirectory', () => { ) }), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -409,7 +457,9 @@ describe('uploadDirectory', () => { ) assert(service.blob.add.called) - assert.equal(service.blob.add.callCount, 1) + assert.equal(service.blob.add.callCount, 2) + assert(service.index.add.called) + assert.equal(service.index.add.callCount, 1) assert(service.filecoin.offer.called) assert.equal(service.filecoin.offer.callCount, 1) assert(service.upload.add.called) @@ -437,6 +487,12 @@ describe('uploadDirectory', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -460,6 +516,15 @@ describe('uploadDirectory', () => { ) }), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -496,7 +561,7 @@ describe('uploadDirectory', () => { files, { connection, - shardSize: 500_056, // should end up with 2 CAR files + shardSize: 500_057, // should end up with 2 CAR files onShardStored: (meta) => carCIDs.push(meta.cid), } ) @@ -517,6 +582,12 @@ describe('uploadDirectory', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -546,6 +617,15 @@ describe('uploadDirectory', () => { ) }), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -697,6 +777,12 @@ describe('uploadCAR', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -725,6 +811,15 @@ describe('uploadCAR', () => { ) }), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -779,7 +874,9 @@ describe('uploadCAR', () => { ) assert(service.blob.add.called) - assert.equal(service.blob.add.callCount, 2) + assert.equal(service.blob.add.callCount, 3) + assert(service.index.add.called) + assert.equal(service.index.add.callCount, 1) assert(service.filecoin.offer.called) assert.equal(service.filecoin.offer.callCount, 2) assert(service.upload.add.called) @@ -808,6 +905,12 @@ describe('uploadCAR', () => { with: space.did(), expiration: Infinity, }), + IndexCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), UploadCapabilities.add.delegate({ issuer: space, audience: agent, @@ -835,6 +938,15 @@ describe('uploadCAR', () => { ) }), }, + index: { + add: Server.provideAdvanced({ + capability: IndexCapabilities.add, + handler: async ({ capability }) => { + assert(capability.nb.index) + return Server.ok({}) + }, + }), + }, filecoin: { offer: Server.provideAdvanced({ capability: StorefrontCapabilities.filecoinOffer, @@ -887,7 +999,9 @@ describe('uploadCAR', () => { ) assert(service.blob.add.called) - assert.equal(service.blob.add.callCount, 1) + assert.equal(service.blob.add.callCount, 2) + assert(service.index.add.called) + assert.equal(service.index.add.callCount, 1) assert(service.filecoin.offer.called) assert.equal(service.filecoin.offer.callCount, 1) assert(service.upload.add.called) diff --git a/packages/upload-client/test/sharding.test.js b/packages/upload-client/test/sharding.test.js index a40f22f9e..79a3ef2ab 100644 --- a/packages/upload-client/test/sharding.test.js +++ b/packages/upload-client/test/sharding.test.js @@ -1,5 +1,7 @@ import assert from 'assert' import { CID } from 'multiformats' +import { equals } from 'multiformats/bytes' +import { sha256 } from 'multiformats/hashes/sha2' import { createFileEncoderStream } from '../src/unixfs.js' import { ShardingStream } from '../src/sharding.js' import { randomBlock, randomBytes } from './helpers/random.js' @@ -85,15 +87,15 @@ describe('ShardingStream', () => { controller.enqueue(block) }, }) - // shard with no roots = encoded block (166) + CAR header (17) = 183 - // shard with no roots = encoded block (102) + CAR header (17) = 119 - // shard with 1 root = encoded block (70) + CAR header (17) = 87 + // shard with no roots = encoded block (166) + CAR header (18) = 183 + // shard with no roots = encoded block (102) + CAR header (18) = 120 + // shard with 1 root = encoded block (70) + CAR header (18) = 88 // shard with 1 root = encoded block (70) + CAR header (59) = 155 - // i.e. shard size of 206 (119 + 87) should allow us 1 shard with 0 roots + // i.e. shard size of 208 (120 + 88) should allow us 1 shard with 0 roots // and then 1 shard with 2 blocks that, when encoded as a CAR with 1 root // will actually exceed the shard size. It must then be refactored into // 2 shards. - .pipeThrough(new ShardingStream({ shardSize: 206 })) + .pipeThrough(new ShardingStream({ shardSize: 208 })) .pipeTo( new WritableStream({ write: (s) => { @@ -119,11 +121,11 @@ describe('ShardingStream', () => { controller.enqueue(block) }, }) - // shard with no roots = encoded block (166) + CAR header (17) = 183 + // shard with no roots = encoded block (166) + CAR header (18) = 184 // shard with 1 root = encoded block (166) + CAR header (59) = 225 // i.e. shard size of 183 should allow us 1 shard with no roots and then // we'll fail to create a shard with 1 root. - .pipeThrough(new ShardingStream({ shardSize: 183 })) + .pipeThrough(new ShardingStream({ shardSize: 184 })) .pipeTo(new WritableStream()) ) }, /block will cause CAR to exceed shard size/) @@ -146,4 +148,32 @@ describe('ShardingStream', () => { ) assert.equal(shards, 0) }) + + it('indexes blocks in shards', async () => { + const file = new Blob([await randomBytes(1024 * 1024 * 10)]) + const shardSize = 1024 * 1024 * 3 + + /** @type {import('../src/types.js').IndexedCARFile[]} */ + const shards = [] + + await createFileEncoderStream(file) + .pipeThrough(new ShardingStream({ shardSize })) + .pipeTo( + new WritableStream({ + write: (s) => { + shards.push(s) + }, + }) + ) + + assert(shards.length > 1) + + for (const car of shards) { + const bytes = new Uint8Array(await car.arrayBuffer()) + for (const [expected, [offset, length]] of car.slices.entries()) { + const actual = await sha256.digest(bytes.slice(offset, offset + length)) + assert(equals(expected.bytes, actual.bytes)) + } + } + }) }) diff --git a/packages/upload-client/tsconfig.json b/packages/upload-client/tsconfig.json index 2f4f83572..82c3b2e36 100644 --- a/packages/upload-client/tsconfig.json +++ b/packages/upload-client/tsconfig.json @@ -8,6 +8,7 @@ "exclude": ["**/node_modules/**"], "references": [ { "path": "../access-client" }, + { "path": "../blob-index" }, { "path": "../filecoin-client" }, { "path": "../capabilities" } ], diff --git a/packages/w3up-client/README.md b/packages/w3up-client/README.md index 292ae96b2..9e14d3a56 100644 --- a/packages/w3up-client/README.md +++ b/packages/w3up-client/README.md @@ -68,13 +68,13 @@ UCAN-based APIs are centered around _capabilities_, which are comprised of an _a When you upload data to w3up, your uploads are linked to a unique _Space_ that acts as a "namespace" for the data you upload. Each Space corresponds to a _DID_, or [Decentralized Identity Document](https://www.w3.org/TR/did-core/). In web3.storage's implementation of w3up, these Space DIDs generally use the key DID method, of the form `did:key:publicKey` with a corresponding private signing key. -When creating a Space using `w3up-client`, it generates this private key and `did:key` for you locally. To use web3.storage, you then register a Space by associating it with your email address. From there, when invoking storage capabilities with web3.storage, the Space `did:key` is the "resource" portion of the capability, while the ability is an action like `store/add` or `store/remove`. (A Space registered with web3.storage is imperfectly analogous to an "account" with web3.storage.) +When creating a Space using `w3up-client`, it generates this private key and `did:key` for you locally. To use web3.storage, you then register a Space by associating it with your email address. From there, when invoking storage capabilities with web3.storage, the Space `did:key` is the "resource" portion of the capability, while the ability is an action like `blob/add` or `blob/remove`. (A Space registered with web3.storage is imperfectly analogous to an "account" with web3.storage.) Under the hood in the email registration process, your Space delegates the capabilities needed to use w3up to your email address, and this delegation is stored by web3.storage. If you need access to your Space in the future from any device, web3.storage allows you to reclaim those capabilities the same way you would reset a password in other services - using an email verification process. This means you don't need to store or manage Space private keys to use w3up - just create a new space, register it with w3up and use it from as many devices as you like. More on this "sign in" process is detailed in the next section on Agents. #### Agent -To invoke a capability like `store/add` on a Space using `w3up-client`, the client must have an _Agent_. Like a Space, an Agent corresponds to a `did:key` whose private key is generated locally. An Agent is useful once `w3up-client` has a UCAN delegation where a registered Space(s) delegates the Agent its capabilities. (An imperfect analogy is Agent with login session.) +To invoke a capability like `blob/add` on a Space using `w3up-client`, the client must have an _Agent_. Like a Space, an Agent corresponds to a `did:key` whose private key is generated locally. An Agent is useful once `w3up-client` has a UCAN delegation where a registered Space(s) delegates the Agent its capabilities. (An imperfect analogy is Agent with login session.) The first time `w3up-client` is instantiated on a device, it creates an Agent automatically. Alternatively, if you have your own Agent corresponding to a specific private key locally available, you can pass it to the client. @@ -208,7 +208,7 @@ async function main () { // from "bring your own Agent" example in `Creating a client object" section` // used command line to generate KEY and PROOF (stored in env variables) // KEY: `npx ucan-key ed --json` in command line, which returns private key and DID for Agent (the private key is stored in KEY) - // PROOF: w3cli used to run `w3 delegation create --can 'store/add' --can 'upload/add' | base64`, which returns the delegation from Space to the Agent we're using (stored in PROOF) + // PROOF: w3cli used to run `w3 delegation create --can 'blob/add' --can 'index/add' --can 'filecoin/offer' --can 'upload/add' | base64`, which returns the delegation from Space to the Agent we're using (stored in PROOF) const principal = Signer.parse(process.env.KEY) const store = new StoreMemory() const client = await Client.create({ principal, store }) @@ -309,7 +309,7 @@ sequenceDiagram - Your user does not need a registered Space - just an Agent with a delegation from your Space - `w3up-client` in the end user environment should have a unique Agent for each user, which should happen by default (since when `w3up-client` is instantiated it creates a new Agent anyway, or uses the one in local Store) - From there, when your end user is ready to upload, they should request from your backend a delegation from your developer-owned Space to their Agent (which can be derived via [`client.agent`](docs-Client#agent)) - - In your backend, you can call [`client.createDelegation()`](docs-Client#createDelegation) passing in the Agent object from `client.agent()` in your end user's instance, and passing through `options?` params to limit the scope of the delegation (e.g., `store/add`, `upload/add`, expiration time) + - In your backend, you can call [`client.createDelegation()`](docs-Client#createDelegation) passing in the Agent object from `client.agent()` in your end user's instance, and passing through `options?` params to limit the scope of the delegation (e.g., `blob/add`, `upload/add`, expiration time) - You can serialize this using `delegation.archive()` and send it to your user - The end user instance of the client should not need to call `client.login(email)`, as it is not claiming any delegations via email address (but rather getting the delegation directly from your backend) - Once your user receives the delegation, they can deserialize it using [`ucanto.Delegation.extract()`](https://github.com/web3-storage/ucanto/blob/c8999a59852b61549d163532a83bac62290b629d/packages/core/src/delegation.js#L399) and pass it in using `client.addSpace()`, and from there they can run any of the `upload` methods @@ -336,7 +336,7 @@ async function backend(did: string) { // Create a delegation for a specific DID const audience = DID.parse(did); - const abilities = ['store/add', 'upload/add']; + const abilities = ['blob/add', 'index/add', 'filecoin/offer', 'upload/add']; const expiration = Math.floor(Date.now() / 1000) + 60 * 60 * 24; // 24 hours from now const delegation = await client.createDelegation(audience, abilities, { expiration, @@ -425,19 +425,21 @@ We created a `esbuild-plugin` [esbuild-plugin-w3up-client-wasm-import](https://g - [`remove`](#remove) - [`capability.access.authorize`](#capabilityaccessauthorize) - [`capability.access.claim`](#capabilityaccessclaim) + - [`capability.blob.add`](#capabilityblobadd) + - [`capability.blob.list`](#capabilitybloblist) + - [`capability.blob.remove`](#capabilityblobremove) + - [`capability.index.add`](#capabilityindexadd) - [`capability.plan.get`](#capabilityplanget) - [`capability.plan.set`](#capabilityplanset) - [`capability.plan.createAdminSession`](#capabilityplancreateadminsession) - [`capability.space.info`](#capabilityspaceinfo) - - [`capability.store.add`](#capabilitystoreadd) - - [`capability.store.list`](#capabilitystorelist) - - [`capability.store.remove`](#capabilitystoreremove) - [`capability.upload.add`](#capabilityuploadadd) - [`capability.upload.list`](#capabilityuploadlist) - [`capability.upload.remove`](#capabilityuploadremove) - [`capability.filecoin.offer`](#capabilityfilecoinoffer) - [`capability.filecoin.info`](#capabilityfilecoininfo) - [Types](#types) + - [`BlobListResult`](#bloblistresult) - [`Capability`](#capability) - [`CARMetadata`](#carmetadata) - [`ClientFactoryOptions`](#clientfactoryoptions) @@ -447,7 +449,6 @@ We created a `esbuild-plugin` [esbuild-plugin-w3up-client-wasm-import](https://g - [`ServiceConf`](#serviceconf) - [`ShardStoredCallback`](#shardstoredcallback) - [`Space`](#space) - - [`StoreListResult`](#storelistresult) - [`UploadListResult`](#uploadlistresult) --- @@ -670,6 +671,53 @@ function claim (): Promise[]> Claim delegations granted to the account associated with this agent. Note: the received delegations are added to the agent's persistent store. +### `capability.blob.add` + +```ts +function add ( + blob: Blob, + options: { retries?: number; signal?: AbortSignal } = {} +): Promise +``` + +Store a blob to the service. + +### `capability.blob.list` + +```ts +function list ( + options: { retries?: number; signal?: AbortSignal } = {} +): Promise> +``` + +List blobs stored in the current space. + +More information: [`BlobListResult`](#bloblistresult), [`ListResponse`](#listresponse) + +### `capability.blob.remove` + +```ts +function remove ( + digest: MultihashDigest, + options: { retries?: number; signal?: AbortSignal } = {} +): Promise +``` + +Remove a stored blob by multihash digest. + +### `capability.index.add` + +```ts +function add( + index: CID, + options: { retries?: number; signal?: AbortSignal } = {} +): Promise +``` + +Register an "index" with the service. The `index` CID should be the CID of a CAR file, containing an index ad defined by [w3-index](https://github.com/w3s-project/specs/blob/main/w3-index.md). + +Required delegated capability proofs: `index/add` + ### `capability.plan.get` ```ts @@ -706,40 +754,6 @@ Create a billing customer portal admin session. Returns a URL that the customer can visit to administer `account`. Design and implementation driven by our Stripe integration and may not be supported by all billing providers. -### `capability.store.add` - -```ts -function add ( - car: Blob, - options: { retries?: number; signal?: AbortSignal } = {} -): Promise -``` - -Store a CAR file to the service. - -### `capability.store.list` - -```ts -function list ( - options: { retries?: number; signal?: AbortSignal } = {} -): Promise> -``` - -List CAR files stored in the current space. - -More information: [`StoreListResult`](#storelistresult), [`ListResponse`](#listresponse) - -### `capability.store.remove` - -```ts -function remove ( - link: CID, - options: { retries?: number; signal?: AbortSignal } = {} -): Promise -``` - -Remove a stored CAR file by CAR CID. - ### `capability.upload.add` ```ts @@ -798,6 +812,17 @@ Get know deals and aggregate info of a Filecoin "piece" previously offered. ## Types +### `BlobListResult` + +```ts +interface BlobListResult { + blob: { + digest: Uint8Array + size: number + } +} +``` + ### `Capability` An object describing a UCAN capability, which specifies what action the UCAN holder `can` perform `with` some resource. @@ -820,7 +845,7 @@ export type Ability = `${string}/${string}` | "*" export type Resource = `${string}:${string}` ``` -The `can` field contains a string ability identifier, e.g. `store/add` or `space/info`. +The `can` field contains a string ability identifier, e.g. `blob/add` or `space/info`. The `with` field contains a resource URI, often a `did:key` URI that identifies a Space. @@ -958,16 +983,6 @@ interface Space { } ``` -### `StoreListResult` - -```ts -interface StoreListResult { - link: CID - size: number - origin?: CID -} -``` - ### `UploadListResult` ```ts diff --git a/packages/w3up-client/package.json b/packages/w3up-client/package.json index 9edae0d6e..5c1f2ab3e 100644 --- a/packages/w3up-client/package.json +++ b/packages/w3up-client/package.json @@ -48,6 +48,14 @@ "types": "./dist/src/capability/access.d.ts", "import": "./dist/src/capability/access.js" }, + "./capability/blob": { + "types": "./dist/src/capability/blob.d.ts", + "import": "./dist/src/capability/blob.js" + }, + "./capability/index": { + "types": "./dist/src/capability/index.d.ts", + "import": "./dist/src/capability/index.js" + }, "./capability/space": { "types": "./dist/src/capability/space.d.ts", "import": "./dist/src/capability/space.js" @@ -117,6 +125,7 @@ "@ucanto/principal": "^9.0.1", "@ucanto/transport": "^9.1.1", "@web3-storage/access": "workspace:^", + "@web3-storage/blob-index": "workspace:^", "@web3-storage/capabilities": "workspace:^", "@web3-storage/did-mailto": "workspace:^", "@web3-storage/filecoin-client": "workspace:^", diff --git a/packages/w3up-client/src/capability/blob.js b/packages/w3up-client/src/capability/blob.js index f0a07b648..e2e57b03b 100644 --- a/packages/w3up-client/src/capability/blob.js +++ b/packages/w3up-client/src/capability/blob.js @@ -7,7 +7,7 @@ import { Base } from '../base.js' */ export class BlobClient extends Base { /** - * Store a Blob as a CAR file. + * Store a Blob to the resource. * * @param {Blob} blob - blob data. * @param {import('../types.js').RequestOptions} [options] @@ -19,7 +19,7 @@ export class BlobClient extends Base { } /** - * List CAR files stored to the resource. + * List blobs stored to the resource. * * @param {import('../types.js').ListRequestOptions} [options] */ @@ -30,7 +30,7 @@ export class BlobClient extends Base { } /** - * Remove a stored CAR file by digest. + * Remove a stored blob by multihash digest. * * @param {import('multiformats').MultihashDigest} digest - digest of blob to remove. * @param {import('../types.js').RequestOptions} [options] diff --git a/packages/w3up-client/src/capability/index.js b/packages/w3up-client/src/capability/index.js new file mode 100644 index 000000000..af3b6a941 --- /dev/null +++ b/packages/w3up-client/src/capability/index.js @@ -0,0 +1,20 @@ +import { Index } from '@web3-storage/upload-client' +import { Index as IndexCapabilities } from '@web3-storage/capabilities' +import { Base } from '../base.js' + +/** + * Client for interacting with the `index/*` capabilities. + */ +export class IndexClient extends Base { + /** + * Register an "index" to the resource. + * + * @param {import('../types.js').CARLink} index - CID of the CAR file that contains the index data. + * @param {import('../types.js').RequestOptions} [options] + */ + async add(index, options = {}) { + const conf = await this._invocationConfig([IndexCapabilities.add.can]) + options.connection = this._serviceConf.upload + return Index.add(conf, index, options) + } +} diff --git a/packages/w3up-client/src/client.js b/packages/w3up-client/src/client.js index 0954dd22e..7fc45ff38 100644 --- a/packages/w3up-client/src/client.js +++ b/packages/w3up-client/src/client.js @@ -5,7 +5,9 @@ import { } from '@web3-storage/upload-client' import { Blob as BlobCapabilities, + Index as IndexCapabilities, Upload as UploadCapabilities, + Filecoin as FilecoinCapabilities, } from '@web3-storage/capabilities' import { CAR } from '@ucanto/transport' import { Base } from './base.js' @@ -13,6 +15,7 @@ import * as Account from './account.js' import { Space } from './space.js' import { Delegation as AgentDelegation } from './delegation.js' import { BlobClient } from './capability/blob.js' +import { IndexClient } from './capability/index.js' import { StoreClient } from './capability/store.js' import { UploadClient } from './capability/upload.js' import { SpaceClient } from './capability/space.js' @@ -28,6 +31,7 @@ import * as Result from './result.js' export { AccessClient, FilecoinClient, + IndexClient, PlanClient, StoreClient, SpaceClient, @@ -48,6 +52,7 @@ export class Client extends Base { this.capability = { access: new AccessClient(agentData, options), filecoin: new FilecoinClient(agentData, options), + index: new IndexClient(agentData, options), plan: new PlanClient(agentData, options), space: new SpaceClient(agentData, options), blob: new BlobClient(agentData, options), @@ -109,6 +114,8 @@ export class Client extends Base { async uploadFile(file, options = {}) { const conf = await this._invocationConfig([ BlobCapabilities.add.can, + IndexCapabilities.add.can, + FilecoinCapabilities.offer.can, UploadCapabilities.add.can, ]) options.connection = this._serviceConf.upload @@ -126,6 +133,8 @@ export class Client extends Base { async uploadDirectory(files, options = {}) { const conf = await this._invocationConfig([ BlobCapabilities.add.can, + IndexCapabilities.add.can, + FilecoinCapabilities.offer.can, UploadCapabilities.add.can, ]) options.connection = this._serviceConf.upload @@ -135,9 +144,10 @@ export class Client extends Base { /** * Uploads a CAR file to the service. * - * The difference between this function and `capability.store.add` is that the - * CAR file is automatically sharded and an "upload" is registered, linking - * the individual shards (see `capability.upload.add`). + * The difference between this function and `capability.store.add` is that + * the CAR file is automatically sharded, an index is generated, uploaded and + * registered (see `capability.index.add`) and finally an an "upload" is + * registered, linking the individual shards (see `capability.upload.add`). * * Use the `onShardStored` callback to obtain the CIDs of the CAR file shards. * @@ -147,6 +157,8 @@ export class Client extends Base { async uploadCAR(car, options = {}) { const conf = await this._invocationConfig([ BlobCapabilities.add.can, + IndexCapabilities.add.can, + FilecoinCapabilities.offer.can, UploadCapabilities.add.can, ]) options.connection = this._serviceConf.upload @@ -282,7 +294,7 @@ export class Client extends Base { * the _current_ space as the resource. * * @param {import('./types.js').Principal} audience - * @param {import('./types.js').Abilities[]} abilities + * @param {import('./types.js').ServiceAbility[]} abilities * @param {Omit & { audienceMeta?: import('./types.js').AgentMeta }} [options] */ async createDelegation(audience, abilities, options = {}) { @@ -344,8 +356,12 @@ export class Client extends Base { await Promise.allSettled( upload.shards.map(async (shard) => { try { - await this.capability.store.remove(shard) + const res = await this.capability.blob.remove(shard.multihash) /* c8 ignore start */ + // if no size, the blob was not found, try delete from store + if (res.ok && res.ok.size === 0) { + await this.capability.store.remove(shard) + } } catch (/** @type {any} */ error) { // If not found, we can tolerate error as it may be a consecutive call for deletion where first failed if (error?.cause?.name !== 'StoreItemNotFound') { diff --git a/packages/w3up-client/src/types.ts b/packages/w3up-client/src/types.ts index 78cca011d..7b92f9cd7 100644 --- a/packages/w3up-client/src/types.ts +++ b/packages/w3up-client/src/types.ts @@ -78,6 +78,10 @@ export type { export type { Abilities, + ServiceAbility, + BlobAdd, + BlobList, + BlobRemove, StoreAdd, StoreList, StoreRemove, @@ -115,6 +119,14 @@ export type { } from '@web3-storage/access/types' export type { + BlobAddSuccess, + BlobAddFailure, + BlobListSuccess, + BlobListFailure, + BlobRemoveSuccess, + BlobRemoveFailure, + IndexAddSuccess, + IndexAddFailure, StoreAddSuccess, StoreGetSuccess, StoreGetFailure, diff --git a/packages/w3up-client/test/ability.test.js b/packages/w3up-client/test/ability.test.js index f75a8f148..69de45898 100644 --- a/packages/w3up-client/test/ability.test.js +++ b/packages/w3up-client/test/ability.test.js @@ -8,7 +8,7 @@ export const testAbilities = { 'should return the passed argument if all abilities are valid': async ( assert ) => { - const abilities = ['store/add', 'upload/add'] + const abilities = ['blob/add', 'upload/add'] assert.equal(asAbilities(abilities), abilities) }, diff --git a/packages/w3up-client/test/capability/index.test.js b/packages/w3up-client/test/capability/index.test.js new file mode 100644 index 000000000..bd328b677 --- /dev/null +++ b/packages/w3up-client/test/capability/index.test.js @@ -0,0 +1,40 @@ +import { ShardedDAGIndex } from '@web3-storage/blob-index' +import * as Link from 'multiformats/link' +import * as Result from '../../src/result.js' +import { randomCAR } from '../helpers/random.js' +import * as Test from '../test.js' + +export const IndexClient = Test.withContext({ + add: { + 'should register an index': async ( + assert, + { client: alice, service, provisionsStorage, uploadTable } + ) => { + const car = await randomCAR(128) + + const space = await alice.createSpace('test') + const auth = await space.createAuthorization(alice) + await alice.addSpace(auth) + await alice.setCurrentSpace(space.did()) + + // @ts-expect-error + await provisionsStorage.put({ + provider: service.did(), + customer: 'did:mailto:alice@web.mail', + consumer: space.did(), + }) + + const index = ShardedDAGIndex.create(car.cid) + const indexBytes = Result.unwrap(await index.archive()) + + const indexDigest = await alice.capability.blob.add( + new Blob([indexBytes]) + ) + assert.ok( + await alice.capability.index.add(Link.create(0x0202, indexDigest)) + ) + }, + }, +}) + +Test.test({ IndexClient }) diff --git a/packages/w3up-client/test/capability/usage.test.js b/packages/w3up-client/test/capability/usage.test.js index 4a653ef6e..aca15b60a 100644 --- a/packages/w3up-client/test/capability/usage.test.js +++ b/packages/w3up-client/test/capability/usage.test.js @@ -6,7 +6,7 @@ export const UsageClient = Test.withContext({ report: { 'should fetch usage report': async ( assert, - { connection, provisionsStorage, uploadTable } + { connection, provisionsStorage } ) => { const alice = new Client(await AgentData.create(), { // @ts-ignore diff --git a/packages/w3up-client/test/helpers/bucket-server.js b/packages/w3up-client/test/helpers/bucket-server.js index cd2540a57..72120c013 100644 --- a/packages/w3up-client/test/helpers/bucket-server.js +++ b/packages/w3up-client/test/helpers/bucket-server.js @@ -1,17 +1,47 @@ -import { createServer } from 'http' +import { createServer } from 'node:http' +import { Buffer } from 'node:buffer' const port = process.env.PORT ?? 8989 const status = process.env.STATUS ? parseInt(process.env.STATUS) : 200 -const server = createServer((req, res) => { +const data = new Map() + +const server = createServer(async (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Methods', '*') res.setHeader('Access-Control-Allow-Headers', '*') if (req.method === 'OPTIONS') return res.end() + res.statusCode = status + if (status === 200) { + const key = new URL(req.url ?? '', 'http://localhost').pathname + if (req.method === 'GET') { + const body = data.get(key) + if (!body) { + res.statusCode = 404 + } else { + res.write(body) + } + } else if (req.method === 'PUT') { + const body = Buffer.concat(await collect(req)) + data.set(key, body) + } + } res.end() }) +/** @param {import('node:stream').Readable} stream */ +const collect = (stream) => { + return /** @type {Promise} */ ( + new Promise((resolve, reject) => { + const chunks = /** @type {Buffer[]} */ ([]) + stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))) + stream.on('error', (err) => reject(err)) + stream.on('end', () => resolve(chunks)) + }) + ) +} + // eslint-disable-next-line no-console server.listen(port, () => console.log(`Listening on :${port}`)) diff --git a/packages/w3up-client/tsconfig.json b/packages/w3up-client/tsconfig.json index f0b372b74..9c0487f8f 100644 --- a/packages/w3up-client/tsconfig.json +++ b/packages/w3up-client/tsconfig.json @@ -11,6 +11,7 @@ }, "references": [ { "path": "../access-client" }, + { "path": "../blob-index" }, { "path": "../capabilities" }, { "path": "../upload-client" }, { "path": "../filecoin-client" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c37e227e0..09a5585a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,7 +23,7 @@ importers: version: 3.3.2(@docusaurus/types@3.3.2)(react@18.3.1)(typescript@5.2.2) '@docusaurus/preset-classic': specifier: ^3.0.0 - version: 3.3.2(@algolia/client-search@4.23.3)(@types/react@18.3.1)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2) + version: 3.3.2(@algolia/client-search@4.23.3)(@types/react@18.3.2)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2) docusaurus-plugin-typedoc: specifier: ^0.21.0 version: 0.21.0(typedoc-plugin-markdown@3.17.1)(typedoc@0.25.13) @@ -114,7 +114,7 @@ importers: version: 10.0.6 '@types/node': specifier: ^20.8.4 - version: 20.12.10 + version: 20.12.12 '@types/sinon': specifier: ^10.0.19 version: 10.0.20 @@ -168,7 +168,7 @@ importers: version: 13.1.0 uint8arrays: specifier: ^5.0.3 - version: 5.0.3 + version: 5.1.0 devDependencies: '@ipld/car': specifier: ^5.1.1 @@ -226,7 +226,7 @@ importers: version: 3.2.0 uint8arrays: specifier: ^5.0.3 - version: 5.0.3 + version: 5.1.0 devDependencies: '@types/assert': specifier: ^1.5.6 @@ -236,7 +236,7 @@ importers: version: 10.0.6 '@types/node': specifier: ^20.8.4 - version: 20.12.10 + version: 20.12.12 '@web3-storage/eslint-config-w3up': specifier: workspace:^ version: link:../eslint-config-w3up @@ -475,7 +475,7 @@ importers: version: 5.1.2 uint8arrays: specifier: ^5.0.3 - version: 5.0.3 + version: 5.1.0 devDependencies: '@ipld/car': specifier: ^5.1.1 @@ -540,6 +540,9 @@ importers: '@ucanto/transport': specifier: ^9.1.1 version: 9.1.1 + '@web3-storage/blob-index': + specifier: workspace:^ + version: link:../blob-index '@web3-storage/capabilities': specifier: workspace:^ version: link:../capabilities @@ -631,6 +634,9 @@ importers: '@web3-storage/access': specifier: workspace:^ version: link:../access-client + '@web3-storage/blob-index': + specifier: workspace:^ + version: link:../blob-index '@web3-storage/capabilities': specifier: workspace:^ version: link:../capabilities @@ -655,7 +661,7 @@ importers: version: 10.0.6 '@types/node': specifier: ^20.8.4 - version: 20.12.10 + version: 20.12.12 '@ucanto/server': specifier: ^10.0.0 version: 10.0.0 @@ -871,11 +877,11 @@ packages: dependencies: '@arethetypeswrong/core': 0.15.1 chalk: 4.1.2 - cli-table3: 0.6.4 + cli-table3: 0.6.5 commander: 10.0.1 marked: 9.1.6 marked-terminal: 6.2.0(marked@9.1.6) - semver: 7.6.0 + semver: 7.6.2 dev: true /@arethetypeswrong/core@0.15.1: @@ -884,7 +890,7 @@ packages: dependencies: '@andrewbranch/untar.js': 1.0.3 fflate: 0.8.2 - semver: 7.6.0 + semver: 7.6.2 ts-expose-internals-conditionally: 1.0.0-empty.0 typescript: 5.3.3 validate-npm-package-name: 5.0.1 @@ -900,7 +906,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.24.5 - picocolors: 1.0.0 + picocolors: 1.0.1 /@babel/compat-data@7.24.4: resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} @@ -1150,7 +1156,7 @@ packages: '@babel/helper-validator-identifier': 7.24.5 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.0.0 + picocolors: 1.0.1 /@babel/parser@7.24.5: resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} @@ -2103,7 +2109,7 @@ packages: babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.5) babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.5) babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.5) - core-js-compat: 3.37.0 + core-js-compat: 3.37.1 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -2157,7 +2163,7 @@ packages: resolution: {integrity: sha512-GWO0mgzNMLWaSYM4z4NVIuY0Cd1fl8cPnuetuddu5w/qGuvt5Y7oUi/kvvQGK9xgOkFJDQX2heIvTRn/OQ1XTg==} engines: {node: '>=6.9.0'} dependencies: - core-js-pure: 3.37.0 + core-js-pure: 3.37.1 regenerator-runtime: 0.14.1 dev: true @@ -2221,7 +2227,7 @@ packages: resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} dev: true - /@docsearch/react@3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.1)(react@18.3.1)(search-insights@2.13.0): + /@docsearch/react@3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.2)(react@18.3.1)(search-insights@2.13.0): resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' @@ -2241,7 +2247,7 @@ packages: '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0) '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) '@docsearch/css': 3.6.0 - '@types/react': 18.3.1 + '@types/react': 18.3.2 algoliasearch: 4.23.3 react: 18.3.1 search-insights: 2.13.0 @@ -2285,16 +2291,16 @@ packages: chalk: 4.1.2 chokidar: 3.6.0 clean-css: 5.3.3 - cli-table3: 0.6.4 + cli-table3: 0.6.5 combine-promises: 1.2.0 commander: 5.1.0 copy-webpack-plugin: 11.0.0(webpack@5.91.0) - core-js: 3.37.0 + core-js: 3.37.1 css-loader: 6.11.0(webpack@5.91.0) css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.91.0) cssnano: 6.1.2(postcss@8.4.38) del: 6.1.1 - detect-port: 1.5.1 + detect-port: 1.6.1 escape-html: 1.0.3 eta: 2.2.0 eval: 0.1.8 @@ -2319,7 +2325,7 @@ packages: react-router-config: 5.1.1(react-router@5.3.4)(react@18.3.1) react-router-dom: 5.3.4(react@18.3.1) rtl-detect: 1.1.2 - semver: 7.6.0 + semver: 7.6.2 serve-handler: 6.1.5 shelljs: 0.8.5 terser-webpack-plugin: 5.3.10(webpack@5.91.0) @@ -2429,7 +2435,7 @@ packages: dependencies: '@docusaurus/types': 3.3.2(react@18.3.1) '@types/history': 4.7.11 - '@types/react': 18.3.1 + '@types/react': 18.3.2 '@types/react-router-config': 5.0.11 '@types/react-router-dom': 5.3.3 react: 18.3.1 @@ -2768,7 +2774,7 @@ packages: - webpack-cli dev: true - /@docusaurus/preset-classic@3.3.2(@algolia/client-search@4.23.3)(@types/react@18.3.1)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2): + /@docusaurus/preset-classic@3.3.2(@algolia/client-search@4.23.3)(@types/react@18.3.2)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2): resolution: {integrity: sha512-1SDS7YIUN1Pg3BmD6TOTjhB7RSBHJRpgIRKx9TpxqyDrJ92sqtZhomDc6UYoMMLQNF2wHFZZVGFjxJhw2VpL+Q==} engines: {node: '>=18.0'} peerDependencies: @@ -2789,9 +2795,9 @@ packages: '@docusaurus/plugin-google-gtag': 3.3.2(react@18.3.1)(typescript@5.2.2) '@docusaurus/plugin-google-tag-manager': 3.3.2(react@18.3.1)(typescript@5.2.2) '@docusaurus/plugin-sitemap': 3.3.2(react@18.3.1)(typescript@5.2.2) - '@docusaurus/theme-classic': 3.3.2(@types/react@18.3.1)(react@18.3.1)(typescript@5.2.2) + '@docusaurus/theme-classic': 3.3.2(@types/react@18.3.2)(react@18.3.1)(typescript@5.2.2) '@docusaurus/theme-common': 3.3.2(@docusaurus/types@3.3.2)(react@18.3.1)(typescript@5.2.2) - '@docusaurus/theme-search-algolia': 3.3.2(@algolia/client-search@4.23.3)(@docusaurus/types@3.3.2)(@types/react@18.3.1)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2) + '@docusaurus/theme-search-algolia': 3.3.2(@algolia/client-search@4.23.3)(@docusaurus/types@3.3.2)(@types/react@18.3.2)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2) '@docusaurus/types': 3.3.2(react@18.3.1) react: 18.3.1 transitivePeerDependencies: @@ -2824,11 +2830,11 @@ packages: react: optional: true dependencies: - '@types/react': 18.3.1 + '@types/react': 18.3.2 react: 18.3.1 dev: true - /@docusaurus/theme-classic@3.3.2(@types/react@18.3.1)(react@18.3.1)(typescript@5.2.2): + /@docusaurus/theme-classic@3.3.2(@types/react@18.3.2)(react@18.3.1)(typescript@5.2.2): resolution: {integrity: sha512-gepHFcsluIkPb4Im9ukkiO4lXrai671wzS3cKQkY9BXQgdVwsdPf/KS0Vs4Xlb0F10fTz+T3gNjkxNEgSN9M0A==} engines: {node: '>=18.0'} peerDependencies: @@ -2852,7 +2858,7 @@ packages: '@docusaurus/utils': 3.3.2(@docusaurus/types@3.3.2)(typescript@5.2.2) '@docusaurus/utils-common': 3.3.2(@docusaurus/types@3.3.2) '@docusaurus/utils-validation': 3.3.2(@docusaurus/types@3.3.2)(typescript@5.2.2) - '@mdx-js/react': 3.0.1(@types/react@18.3.1)(react@18.3.1) + '@mdx-js/react': 3.0.1(@types/react@18.3.2)(react@18.3.1) clsx: 2.1.1 copy-text-to-clipboard: 3.2.0 infima: 0.2.0-alpha.43 @@ -2906,7 +2912,7 @@ packages: '@docusaurus/utils': 3.3.2(@docusaurus/types@3.3.2)(typescript@5.2.2) '@docusaurus/utils-common': 3.3.2(@docusaurus/types@3.3.2) '@types/history': 4.7.11 - '@types/react': 18.3.1 + '@types/react': 18.3.2 '@types/react-router-config': 5.0.11 clsx: 2.1.1 parse-numeric-range: 1.3.0 @@ -2934,7 +2940,7 @@ packages: - webpack-cli dev: true - /@docusaurus/theme-search-algolia@3.3.2(@algolia/client-search@4.23.3)(@docusaurus/types@3.3.2)(@types/react@18.3.1)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2): + /@docusaurus/theme-search-algolia@3.3.2(@algolia/client-search@4.23.3)(@docusaurus/types@3.3.2)(@types/react@18.3.2)(react@18.3.1)(search-insights@2.13.0)(typescript@5.2.2): resolution: {integrity: sha512-qLkfCl29VNBnF1MWiL9IyOQaHxUvicZp69hISyq/xMsNvFKHFOaOfk9xezYod2Q9xx3xxUh9t/QPigIei2tX4w==} engines: {node: '>=18.0'} peerDependencies: @@ -2946,7 +2952,7 @@ packages: react-dom: optional: true dependencies: - '@docsearch/react': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.1)(react@18.3.1)(search-insights@2.13.0) + '@docsearch/react': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.2)(react@18.3.1)(search-insights@2.13.0) '@docusaurus/core': 3.3.2(@docusaurus/types@3.3.2)(react@18.3.1)(typescript@5.2.2) '@docusaurus/logger': 3.3.2 '@docusaurus/plugin-content-docs': 3.3.2(react@18.3.1)(typescript@5.2.2) @@ -3007,7 +3013,7 @@ packages: dependencies: '@mdx-js/mdx': 3.0.1 '@types/history': 4.7.11 - '@types/react': 18.3.1 + '@types/react': 18.3.2 commander: 5.1.0 joi: 17.13.1 react: 18.3.1 @@ -3412,7 +3418,7 @@ packages: '@perma/map': 1.0.3 actor: 2.3.1 multiformats: 11.0.2 - protobufjs: 7.2.6 + protobufjs: 7.3.0 rabin-rs: 2.1.0 dev: false @@ -3435,7 +3441,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.12.10 + '@types/node': 20.12.12 '@types/yargs': 17.0.32 chalk: 4.1.2 dev: true @@ -3506,7 +3512,7 @@ packages: - supports-color dev: true - /@mdx-js/react@3.0.1(@types/react@18.3.1)(react@18.3.1): + /@mdx-js/react@3.0.1(@types/react@18.3.2)(react@18.3.1): resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==} peerDependencies: '@types/react': '>=16' @@ -3516,7 +3522,7 @@ packages: optional: true dependencies: '@types/mdx': 2.0.13 - '@types/react': 18.3.1 + '@types/react': 18.3.2 react: 18.3.1 dev: true @@ -3849,7 +3855,7 @@ packages: '@svgr/core': 8.1.0(typescript@5.2.2) cosmiconfig: 8.3.6(typescript@5.2.2) deepmerge: 4.3.1 - svgo: 3.2.0 + svgo: 3.3.2 transitivePeerDependencies: - typescript dev: true @@ -3897,26 +3903,26 @@ packages: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/bonjour@3.5.13: resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/connect-history-api-fallback@1.5.4: resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} dependencies: '@types/express-serve-static-core': 4.19.0 - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/debug@4.1.12: @@ -3952,7 +3958,7 @@ packages: /@types/express-serve-static-core@4.19.0: resolution: {integrity: sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -3996,7 +4002,7 @@ packages: /@types/http-proxy@1.17.14: resolution: {integrity: sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/inquirer@9.0.7: @@ -4025,8 +4031,8 @@ packages: /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - /@types/mdast@4.0.3: - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + /@types/mdast@4.0.4: + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} dependencies: '@types/unist': 3.0.2 dev: true @@ -4054,15 +4060,15 @@ packages: /@types/node-forge@1.3.11: resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/node@17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} dev: true - /@types/node@20.12.10: - resolution: {integrity: sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==} + /@types/node@20.12.12: + resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} dependencies: undici-types: 5.26.5 @@ -4089,7 +4095,7 @@ packages: resolution: {integrity: sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==} dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.1 + '@types/react': 18.3.2 '@types/react-router': 5.1.20 dev: true @@ -4097,7 +4103,7 @@ packages: resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.1 + '@types/react': 18.3.2 '@types/react-router': 5.1.20 dev: true @@ -4105,11 +4111,11 @@ packages: resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.1 + '@types/react': 18.3.2 dev: true - /@types/react@18.3.1: - resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} + /@types/react@18.3.2: + resolution: {integrity: sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==} dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 @@ -4126,7 +4132,7 @@ packages: /@types/sax@1.2.7: resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/semver@7.5.8: @@ -4137,7 +4143,7 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/serve-index@1.9.4: @@ -4150,7 +4156,7 @@ packages: resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} dependencies: '@types/http-errors': 2.0.4 - '@types/node': 20.12.10 + '@types/node': 20.12.12 '@types/send': 0.17.4 dev: true @@ -4167,13 +4173,13 @@ packages: /@types/sockjs@0.3.36: resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/through@0.0.33: resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/unist@2.0.10: @@ -4187,13 +4193,13 @@ packages: /@types/varint@6.0.3: resolution: {integrity: sha512-DHukoGWdJ2aYkveZJTB2rN2lp6m7APzVsoJQ7j/qy1fQxyamJTPD5xQzCMoJ2Qtgn0mE3wWeNOpbTyBFvF+dyA==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 dev: true /@types/yargs-parser@21.0.3: @@ -4228,7 +4234,7 @@ packages: graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.6.0 + semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -4304,7 +4310,7 @@ packages: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -4324,7 +4330,7 @@ packages: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.2.2) eslint: 8.57.0 - semver: 7.6.0 + semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript @@ -4914,10 +4920,10 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.23.0 - caniuse-lite: 1.0.30001616 + caniuse-lite: 1.0.30001618 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.0 + picocolors: 1.0.1 postcss: 8.4.38 postcss-value-parser: 4.2.0 dev: true @@ -4968,7 +4974,7 @@ packages: dependencies: '@babel/core': 7.24.5 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.5) - core-js-compat: 3.37.0 + core-js-compat: 3.37.1 transitivePeerDependencies: - supports-color dev: true @@ -5123,10 +5129,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001616 - electron-to-chromium: 1.4.757 + caniuse-lite: 1.0.30001618 + electron-to-chromium: 1.4.767 node-releases: 2.0.14 - update-browserslist-db: 1.0.15(browserslist@4.23.0) + update-browserslist-db: 1.0.16(browserslist@4.23.0) dev: true /buffer-from@1.1.2: @@ -5254,13 +5260,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.23.0 - caniuse-lite: 1.0.30001616 + caniuse-lite: 1.0.30001618 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite@1.0.30001616: - resolution: {integrity: sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==} + /caniuse-lite@1.0.30001618: + resolution: {integrity: sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==} dev: true /cardinal@2.1.1: @@ -5436,8 +5442,8 @@ packages: engines: {node: '>=6'} dev: true - /cli-table3@0.6.4: - resolution: {integrity: sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==} + /cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} dependencies: string-width: 4.2.3 @@ -5595,7 +5601,7 @@ packages: dot-prop: 7.2.0 env-paths: 3.0.0 json-schema-typed: 8.0.1 - semver: 7.6.0 + semver: 7.6.2 dev: false /config-chain@1.1.13: @@ -5683,19 +5689,19 @@ packages: webpack: 5.91.0 dev: true - /core-js-compat@3.37.0: - resolution: {integrity: sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==} + /core-js-compat@3.37.1: + resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} dependencies: browserslist: 4.23.0 dev: true - /core-js-pure@3.37.0: - resolution: {integrity: sha512-d3BrpyFr5eD4KcbRvQ3FTUx/KWmaDesr7+a3+1+P46IUnNoEt+oiLijPINZMEon7w9oGkIINWxrBAU9DEciwFQ==} + /core-js-pure@3.37.1: + resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==} requiresBuild: true dev: true - /core-js@3.37.0: - resolution: {integrity: sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==} + /core-js@3.37.1: + resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==} requiresBuild: true dev: true @@ -5807,7 +5813,7 @@ packages: postcss-modules-scope: 3.2.0(postcss@8.4.38) postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 - semver: 7.6.0 + semver: 7.6.2 webpack: 5.91.0 dev: true @@ -6147,7 +6153,7 @@ packages: require-package-name: 2.0.1 resolve: 1.22.8 resolve-from: 5.0.0 - semver: 7.6.0 + semver: 7.6.2 yargs: 16.2.0 transitivePeerDependencies: - supports-color @@ -6197,8 +6203,9 @@ packages: - supports-color dev: true - /detect-port@1.5.1: - resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==} + /detect-port@1.6.1: + resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==} + engines: {node: '>= 4.0.0'} hasBin: true dependencies: address: 1.2.2 @@ -6349,8 +6356,8 @@ packages: encoding: 0.1.13 dev: false - /electron-to-chromium@1.4.757: - resolution: {integrity: sha512-jftDaCknYSSt/+KKeXzH3LX5E2CvRLm75P3Hj+J/dv3CL0qUYcOt13d5FN1NiL5IJbbhzHrb3BomeG2tkSlZmw==} + /electron-to-chromium@1.4.767: + resolution: {integrity: sha512-nzzHfmQqBss7CE3apQHkHjXW77+8w3ubGCIoEijKCJebPufREaFETgGXWTkh32t259F3Kcq+R8MZdFdOJROgYw==} dev: true /emoji-regex@10.3.0: @@ -6388,8 +6395,8 @@ packages: iconv-lite: 0.6.3 dev: false - /enhanced-resolve@5.16.0: - resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==} + /enhanced-resolve@5.16.1: + resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==} engines: {node: '>=10.13.0'} dependencies: graceful-fs: 4.2.11 @@ -6596,7 +6603,7 @@ packages: eslint: 8.57.0 esquery: 1.5.0 is-builtin-module: 3.2.1 - semver: 7.6.0 + semver: 7.6.2 spdx-expression-parse: 4.0.0 transitivePeerDependencies: - supports-color @@ -6775,7 +6782,7 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 require-like: 0.1.2 dev: true @@ -7115,7 +7122,7 @@ packages: memfs: 3.5.3 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.6.0 + semver: 7.6.2 tapable: 1.1.3 typescript: 5.2.2 webpack: 5.91.0 @@ -7451,7 +7458,7 @@ packages: resolution: {integrity: sha512-nZeamxfymIWLpVcAN0CRrb7uVq3hCOGj9IcL6NMA6VVCVWqj+h9Jo/SmaWuS92AEDf1thmHsM5D5c70hM3j2Tg==} dependencies: sparse-array: 1.3.2 - uint8arrays: 5.0.3 + uint8arrays: 5.1.0 dev: true /handle-thing@2.0.1: @@ -8016,7 +8023,7 @@ packages: engines: {node: '>=16.0.0', npm: '>=7.0.0'} dependencies: err-code: 3.0.1 - protobufjs: 7.2.6 + protobufjs: 7.3.0 dev: true /ipfs-utils@9.0.14: @@ -8503,7 +8510,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.10 + '@types/node': 20.12.12 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -8514,7 +8521,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -8523,7 +8530,7 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.12.10 + '@types/node': 20.12.12 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -8654,7 +8661,7 @@ packages: /launch-editor@2.6.1: resolution: {integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==} dependencies: - picocolors: 1.0.0 + picocolors: 1.0.1 shell-quote: 1.8.1 dev: true @@ -8852,12 +8859,6 @@ packages: yallist: 3.1.1 dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - /lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -8871,7 +8872,7 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} dependencies: - semver: 7.6.0 + semver: 7.6.2 dev: true /markdown-extensions@2.0.0: @@ -8892,7 +8893,7 @@ packages: ansi-escapes: 6.2.1 cardinal: 2.1.1 chalk: 5.3.0 - cli-table3: 0.6.4 + cli-table3: 0.6.5 marked: 9.1.6 node-emoji: 2.1.3 supports-hyperlinks: 3.0.0 @@ -8919,7 +8920,7 @@ packages: /mdast-util-directive@3.0.0: resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@types/unist': 3.0.2 devlop: 1.1.0 mdast-util-from-markdown: 2.0.0 @@ -8934,7 +8935,7 @@ packages: /mdast-util-find-and-replace@3.0.1: resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 @@ -8943,7 +8944,7 @@ packages: /mdast-util-from-markdown@2.0.0: resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@types/unist': 3.0.2 decode-named-character-reference: 1.0.2 devlop: 1.1.0 @@ -8962,7 +8963,7 @@ packages: /mdast-util-frontmatter@2.0.1: resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 escape-string-regexp: 5.0.0 mdast-util-from-markdown: 2.0.0 @@ -8975,7 +8976,7 @@ packages: /mdast-util-gfm-autolink-literal@2.0.0: resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.1 @@ -8985,7 +8986,7 @@ packages: /mdast-util-gfm-footnote@2.0.0: resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.0 mdast-util-to-markdown: 2.1.0 @@ -8997,7 +8998,7 @@ packages: /mdast-util-gfm-strikethrough@2.0.0: resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.0 mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: @@ -9007,7 +9008,7 @@ packages: /mdast-util-gfm-table@2.0.0: resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.3 mdast-util-from-markdown: 2.0.0 @@ -9019,7 +9020,7 @@ packages: /mdast-util-gfm-task-list-item@2.0.0: resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.0 mdast-util-to-markdown: 2.1.0 @@ -9046,7 +9047,7 @@ packages: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.0 mdast-util-to-markdown: 2.1.0 @@ -9059,7 +9060,7 @@ packages: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@types/unist': 3.0.2 ccount: 2.0.1 devlop: 1.1.0 @@ -9091,7 +9092,7 @@ packages: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.0 mdast-util-to-markdown: 2.1.0 @@ -9102,7 +9103,7 @@ packages: /mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 unist-util-is: 6.0.0 dev: true @@ -9110,7 +9111,7 @@ packages: resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==} dependencies: '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@ungap/structured-clone': 1.2.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.0 @@ -9123,7 +9124,7 @@ packages: /mdast-util-to-markdown@2.1.0: resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@types/unist': 3.0.2 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 @@ -9136,7 +9137,7 @@ packages: /mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 dev: true /mdn-data@2.0.28: @@ -10162,7 +10163,7 @@ packages: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 - semver: 7.6.0 + semver: 7.6.2 dev: true /param-case@3.0.4: @@ -10327,8 +10328,8 @@ packages: is-reference: 3.0.2 dev: true - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -10521,7 +10522,7 @@ packages: cosmiconfig: 8.3.6(typescript@5.2.2) jiti: 1.21.0 postcss: 8.4.38 - semver: 7.6.0 + semver: 7.6.2 webpack: 5.91.0 transitivePeerDependencies: - typescript @@ -10805,7 +10806,7 @@ packages: dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 - svgo: 3.2.0 + svgo: 3.3.2 dev: true /postcss-unique-selectors@6.0.4(postcss@8.4.38): @@ -10836,7 +10837,7 @@ packages: engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 + picocolors: 1.0.1 source-map-js: 1.2.0 /prelude-ls@1.2.1: @@ -10919,8 +10920,8 @@ packages: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} dev: true - /protobufjs@7.2.6: - resolution: {integrity: sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==} + /protobufjs@7.3.0: + resolution: {integrity: sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==} engines: {node: '>=12.0.0'} requiresBuild: true dependencies: @@ -10934,7 +10935,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.12.10 + '@types/node': 20.12.12 long: 5.2.3 /proxy-addr@2.0.7: @@ -11341,7 +11342,7 @@ packages: /remark-directive@3.0.0: resolution: {integrity: sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-directive: 3.0.0 micromark-extension-directive: 3.0.0 unified: 11.0.4 @@ -11353,7 +11354,7 @@ packages: resolution: {integrity: sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 emoticon: 4.0.1 mdast-util-find-and-replace: 3.0.1 node-emoji: 2.1.3 @@ -11363,7 +11364,7 @@ packages: /remark-frontmatter@5.0.0: resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-frontmatter: 2.0.1 micromark-extension-frontmatter: 2.0.0 unified: 11.0.4 @@ -11374,7 +11375,7 @@ packages: /remark-gfm@4.0.0: resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-gfm: 3.0.0 micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 @@ -11396,7 +11397,7 @@ packages: /remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.0 micromark-util-types: 2.0.0 unified: 11.0.4 @@ -11408,7 +11409,7 @@ packages: resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} dependencies: '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-to-hast: 13.1.0 unified: 11.0.4 vfile: 6.0.1 @@ -11417,7 +11418,7 @@ packages: /remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-to-markdown: 2.1.0 unified: 11.0.4 dev: true @@ -11528,7 +11529,7 @@ packages: hasBin: true dependencies: escalade: 3.1.2 - picocolors: 1.0.0 + picocolors: 1.0.1 postcss: 8.4.38 strip-json-comments: 3.1.1 dev: true @@ -11644,7 +11645,7 @@ packages: resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} engines: {node: '>=12'} dependencies: - semver: 7.6.0 + semver: 7.6.2 dev: true /semver@5.7.2: @@ -11657,12 +11658,10 @@ packages: hasBin: true dev: true - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + /semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} hasBin: true - dependencies: - lru-cache: 6.0.0 /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -12248,8 +12247,8 @@ packages: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} dev: true - /svgo@3.2.0: - resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} + /svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -12259,7 +12258,7 @@ packages: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.0.0 + picocolors: 1.0.1 dev: true /sync-multihash-sha2@1.0.0: @@ -12553,15 +12552,15 @@ packages: /uint8arraylist@2.4.8: resolution: {integrity: sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ==} dependencies: - uint8arrays: 5.0.3 + uint8arrays: 5.1.0 /uint8arrays@4.0.10: resolution: {integrity: sha512-AnJNUGGDJAgFw/eWu/Xb9zrVKEGlwJJCaeInlf3BkecE/zcTobk5YXYIPNQJO1q5Hh1QZrQQHf0JvcHqz2hqoA==} dependencies: multiformats: 12.1.3 - /uint8arrays@5.0.3: - resolution: {integrity: sha512-6LBuKji28kHjgPJMkQ6GDaBb1lRwIhyOYq6pDGwYMoDPfImE9SkuYENVmR0yu9yGgs2clHUSY9fKDukR+AXfqQ==} + /uint8arrays@5.1.0: + resolution: {integrity: sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==} dependencies: multiformats: 13.1.0 @@ -12685,15 +12684,15 @@ packages: engines: {node: '>= 0.8'} dev: true - /update-browserslist-db@1.0.15(browserslist@4.23.0): - resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} + /update-browserslist-db@1.0.16(browserslist@4.23.0): + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: browserslist: 4.23.0 escalade: 3.1.2 - picocolors: 1.0.0 + picocolors: 1.0.1 dev: true /update-notifier@6.0.2: @@ -12711,7 +12710,7 @@ packages: is-yarn-global: 0.4.1 latest-version: 7.0.0 pupa: 3.1.0 - semver: 7.6.0 + semver: 7.6.2 semver-diff: 4.0.0 xdg-basedir: 5.1.0 dev: true @@ -12900,7 +12899,7 @@ packages: gzip-size: 6.0.0 html-escaper: 2.0.2 opener: 1.5.2 - picocolors: 1.0.0 + picocolors: 1.0.1 sirv: 2.0.4 ws: 7.5.9 transitivePeerDependencies: @@ -13006,7 +13005,7 @@ packages: acorn-import-assertions: 1.9.0(acorn@8.11.3) browserslist: 4.23.0 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.16.0 + enhanced-resolve: 5.16.1 es-module-lexer: 1.5.2 eslint-scope: 5.1.1 events: 3.3.0 @@ -13198,9 +13197,6 @@ packages: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} diff --git a/tsconfig.json b/tsconfig.json index da374df3c..655f84f09 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,6 +37,7 @@ "entryPointStrategy": "packages", "entryPoints": [ "packages/access-client", + "packages/blob-index", "packages/capabilities", "packages/upload-client", "packages/filecoin-client",