diff --git a/packages/client/package.json b/packages/client/package.json index 2222af82b3..d992d9caa8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -44,30 +44,30 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@web-std/blob": "^2.1.0", + "@ipld/car": "^3.1.20", + "@web-std/blob": "^2.1.2", "@web-std/fetch": "^2.0.3", - "@web-std/file": "^1.1.0", - "@web-std/form-data": "^2.1.0", + "@web-std/file": "^1.1.3", + "@web-std/form-data": "^2.1.1", "carbites": "^1.0.6", + "ipfs-car": "^0.5.9", "multiformats": "^9.4.10", "p-retry": "^4.6.1", "streaming-iterables": "^6.0.0" }, "devDependencies": { - "@ipld/car": "^3.1.20", "@ipld/dag-cbor": "^6.0.13", "@ipld/dag-json": "^8.0.3", "@ssttevee/multipart-parser": "0.1.9", "@types/mocha": "^9.0.0", "hundreds": "0.0.9", - "ipfs-car": "^0.5.6", - "ipfs-unixfs-importer": "9.0.6", + "ipfs-unixfs-importer": "^9.0.6", "ipld": "0.30.2", "ipld-dag-pb": "0.22.3", "ipld-garbage": "^4.0.1", "ipld-in-memory": "8.0.0", "mocha": "^9.1.0", - "multicodec": "3.2.1", + "multicodec": "^3.2.1", "multihashing-async": "^2.1.2", "npm-run-all": "^4.1.5", "nyc": "15.1.0", diff --git a/packages/client/src/bs-car-reader.js b/packages/client/src/bs-car-reader.js new file mode 100644 index 0000000000..7500dc9ecc --- /dev/null +++ b/packages/client/src/bs-car-reader.js @@ -0,0 +1,65 @@ +/** + * An implementation of the CAR reader interface that is backed by a blockstore. + * + * @typedef {import('multiformats').CID} CID + * @typedef {import('@ipld/car/api').CarReader} CarReader + * @implements {CarReader} + */ +export class BlockstoreCarReader { + /** + * @param {number} version + * @param {CID[]} roots + * @param {import('ipfs-car/blockstore').Blockstore} blockstore + */ + constructor(version, roots, blockstore) { + /** + * @private + */ + this._version = version + /** + * @private + */ + this._roots = roots + /** + * @private + */ + this._blockstore = blockstore + } + + get version() { + return this._version + } + + get blockstore() { + return this._blockstore + } + + async getRoots() { + return this._roots + } + + /** + * @param {CID} cid + */ + has(cid) { + return this._blockstore.has(cid) + } + + /** + * @param {CID} cid + */ + async get(cid) { + const bytes = await this._blockstore.get(cid) + return { cid, bytes } + } + + blocks() { + return this._blockstore.blocks() + } + + async *cids() { + for await (const b of this.blocks()) { + yield b.cid + } + } +} diff --git a/packages/client/src/lib.js b/packages/client/src/lib.js index 41eb11de4b..383656430a 100644 --- a/packages/client/src/lib.js +++ b/packages/client/src/lib.js @@ -15,27 +15,30 @@ */ import { transform } from 'streaming-iterables' -import pRetry from 'p-retry' +import pRetry, { AbortError } from 'p-retry' import { TreewalkCarSplitter } from 'carbites/treewalk' +import { pack } from 'ipfs-car/pack' +import { CID } from 'multiformats/cid' import * as Token from './token.js' -import { fetch, File, Blob, FormData } from './platform.js' +import { fetch, File, Blob, FormData, Blockstore } from './platform.js' import { toGatewayURL } from './gateway.js' +import { BlockstoreCarReader } from './bs-car-reader.js' const MAX_STORE_RETRIES = 5 const MAX_CONCURRENT_UPLOADS = 3 const MAX_CHUNK_SIZE = 1024 * 1024 * 10 // chunk to ~10MB CARs /** - * @typedef {import('multiformats/block').BlockDecoder} AnyBlockDecoder - * @typedef {import('./lib/interface').Service} Service + * @typedef {import('./lib/interface.js').Service} Service * @typedef {import('./lib/interface.js').CIDString} CIDString * @typedef {import('./lib/interface.js').Deal} Deal * @typedef {import('./lib/interface.js').Pin} Pin + * @typedef {import('./lib/interface.js').CarReader} CarReader */ /** * @template {import('./lib/interface.js').TokenInput} T - * @typedef {import('./lib/interface').Token} TokenType + * @typedef {import('./lib/interface.js').Token} TokenType */ /** @@ -87,45 +90,35 @@ class NFTStorage { if (!token) throw new Error('missing token') return { Authorization: `Bearer ${token}` } } + /** + * Stores a single file and returns it's CID. + * * @param {Service} service * @param {Blob} blob * @returns {Promise} */ - static async storeBlob({ endpoint, token }, blob) { - const url = new URL(`upload/`, endpoint) - - if (blob.size === 0) { - throw new Error('Content size is 0, make sure to provide some content') - } - - const request = await fetch(url.toString(), { - method: 'POST', - headers: NFTStorage.auth(token), - body: blob, - }) - const result = await request.json() - - if (result.ok) { - return result.value.cid - } else { - throw new Error(result.error.message) - } + static async storeBlob(service, blob) { + const { cid, car } = await NFTStorage.encodeBlob(blob) + await NFTStorage.storeCar(service, car) + return cid.toString() } + /** + * Stores a CAR file and returns it's root CID. + * * @param {Service} service - * @param {Blob|import('./lib/interface.js').CarReader} car - * @param {{ - * onStoredChunk?: (size: number) => void - * decoders?: AnyBlockDecoder[] - * }} [options] + * @param {Blob|CarReader} car + * @param {import('./lib/interface.js').CarStorerOptions} [options] * @returns {Promise} */ static async storeCar( { endpoint, token }, car, - { onStoredChunk, decoders } = {} + { onStoredChunk, maxRetries, decoders } = {} ) { + const url = new URL('upload/', endpoint) + const headers = NFTStorage.auth(token) const targetSize = MAX_CHUNK_SIZE const splitter = car instanceof Blob @@ -139,15 +132,30 @@ class NFTStorage { for await (const part of car) { carParts.push(part) } - const carFile = new Blob(carParts, { - type: 'application/car', - }) - const res = await pRetry( - () => NFTStorage.storeBlob({ endpoint, token }, carFile), - { retries: MAX_STORE_RETRIES } + const carFile = new Blob(carParts, { type: 'application/car' }) + const cid = await pRetry( + async () => { + const response = await fetch(url.toString(), { + method: 'POST', + headers, + body: carFile, + }) + const result = await response.json() + if (!result.ok) { + // do not retry if unauthorized - will not succeed + if (response.status === 401) { + throw new AbortError(result.error.message) + } + throw new Error(result.error.message) + } + return result.value.cid + }, + { + retries: maxRetries == null ? MAX_STORE_RETRIES : maxRetries, + } ) onStoredChunk && onStoredChunk(carFile.size) - return res + return cid } ) @@ -158,70 +166,55 @@ class NFTStorage { return /** @type {CIDString} */ (root) } + /** + * Stores a directory of files and returns a CID. Provided files **MUST** + * be within the same directory, otherwise error is raised e.g. `foo/bar.png`, + * `foo/bla/baz.json` is ok but `foo/bar.png`, `bla/baz.json` is not. + * * @param {Service} service * @param {Iterable} files * @returns {Promise} */ - static async storeDirectory({ endpoint, token }, files) { - const url = new URL(`upload/`, endpoint) - const body = new FormData() - let size = 0 - for (const file of files) { - body.append('file', file, file.name) - size += file.size - } - - if (size === 0) { - throw new Error( - 'Total size of files should exceed 0, make sure to provide some content' - ) - } - - const response = await fetch(url.toString(), { - method: 'POST', - headers: NFTStorage.auth(token), - body, - }) - const result = await response.json() - - if (result.ok) { - return result.value.cid - } else { - throw new Error(result.error.message) - } + static async storeDirectory(service, files) { + const { cid, car } = await NFTStorage.encodeDirectory(files) + await NFTStorage.storeCar(service, car) + return cid.toString() } /** + * Stores the given token and all resources it references (in the form of a + * File or a Blob) along with a metadata JSON as specificed in ERC-1155. The + * `token.image` must be either a `File` or a `Blob` instance, which will be + * stored and the corresponding content address URL will be saved in the + * metadata JSON file under `image` field. + * + * If `token.properties` contains properties with `File` or `Blob` values, + * those also get stored and their URLs will be saved in the metadata JSON + * file in their place. + * + * Note: URLs for `File` objects will retain file names e.g. in case of + * `new File([bytes], 'cat.png', { type: 'image/png' })` will be transformed + * into a URL that looks like `ipfs://bafy...hash/image/cat.png`. For `Blob` + * objects, the URL will not have a file name name or mime type, instead it + * will be transformed into a URL that looks like + * `ipfs://bafy...hash/image/blob`. + * * @template {import('./lib/interface.js').TokenInput} T * @param {Service} service * @param {T} metadata * @returns {Promise>} */ - static async store({ endpoint, token }, metadata) { - validateERC1155(metadata) - - const url = new URL(`store/`, endpoint) - const body = Token.encode(metadata) - const paths = new Set(body.keys()) - - const response = await fetch(url.toString(), { - method: 'POST', - headers: NFTStorage.auth(token), - body, - }) - - /** @type {import('./lib/interface.js').StoreResponse} */ - const result = await response.json() - - if (result.ok === true) { - const { value } = result - return Token.decode(value, paths) - } else { - throw new Error(result.error.message) - } + static async store(service, metadata) { + const { token, car } = await NFTStorage.encodeNFT(metadata) + await NFTStorage.storeCar(service, car) + return token } + /** + * Returns current status of the stored NFT by its CID. Note the NFT must + * have previously been stored by this account. + * * @param {Service} service * @param {string} cid * @returns {Promise} @@ -248,6 +241,8 @@ class NFTStorage { } /** + * Check if a CID of an NFT is being stored by NFT.Storage. + * * @param {import('./lib/interface.js').PublicService} service * @param {string} cid * @returns {Promise} @@ -269,6 +264,10 @@ class NFTStorage { } /** + * Removes stored content by its CID from this account. Please note that + * even if content is removed from the service other nodes that have + * replicated it might still continue providing it. + * * @param {Service} service * @param {string} cid * @returns {Promise} @@ -285,6 +284,119 @@ class NFTStorage { } } + /** + * Encodes the given token and all resources it references (in the form of a + * File or a Blob) along with a metadata JSON as specificed in ERC-1155 to a + * CAR file. The `token.image` must be either a `File` or a `Blob` instance, + * which will be stored and the corresponding content address URL will be + * saved in the metadata JSON file under `image` field. + * + * If `token.properties` contains properties with `File` or `Blob` values, + * those also get stored and their URLs will be saved in the metadata JSON + * file in their place. + * + * Note: URLs for `File` objects will retain file names e.g. in case of + * `new File([bytes], 'cat.png', { type: 'image/png' })` will be transformed + * into a URL that looks like `ipfs://bafy...hash/image/cat.png`. For `Blob` + * objects, the URL will not have a file name name or mime type, instead it + * will be transformed into a URL that looks like + * `ipfs://bafy...hash/image/blob`. + * + * @example + * ```js + * const { token, car } = await NFTStorage.encodeNFT({ + * name: 'nft.storage store test', + * description: 'Test ERC-1155 compatible metadata.', + * image: new File([''], 'pinpie.jpg', { type: 'image/jpg' }), + * properties: { + * custom: 'Custom data can appear here, files are auto uploaded.', + * file: new File([''], 'README.md', { type: 'text/plain' }), + * } + * }) + * + * console.log('IPFS URL for the metadata:', token.url) + * console.log('metadata.json contents:\n', token.data) + * console.log('metadata.json with IPFS gateway URLs:\n', token.embed()) + * + * // Now store the CAR file on NFT.Storage + * await client.storeCar(car) + * ``` + * + * @template {import('./lib/interface.js').TokenInput} T + * @param {T} input + * @returns {Promise<{ cid: CID, token: TokenType, car: CarReader }>} + */ + static async encodeNFT(input) { + validateERC1155(input) + return Token.Token.encode(input) + } + + /** + * Encodes a single file to a CAR file and also returns it's root CID. + * + * @example + * ```js + * const content = new Blob(['hello world']) + * const { cid, car } = await NFTStorage.encodeBlob(content) + * + * // Root CID of the file + * console.log(cid.toString()) + * + * // Now store the CAR file on NFT.Storage + * await client.storeCar(car) + * ``` + * + * @param {Blob} blob + * @returns {Promise<{ cid: CID, car: CarReader }>} + */ + static async encodeBlob(blob) { + if (blob.size === 0) { + throw new Error('Content size is 0, make sure to provide some content') + } + + return packCar([{ path: 'blob', content: blob.stream() }], false) + } + + /** + * Encodes a directory of files to a CAR file and also returns the root CID. + * Provided files **MUST** be within the same directory, otherwise error is + * raised e.g. `foo/bar.png`, `foo/bla/baz.json` is ok but `foo/bar.png`, + * `bla/baz.json` is not. + * + * @example + * ```js + * const { cid, car } = await NFTStorage.encodeDirectory([ + * new File(['hello world'], 'hello.txt'), + * new File([JSON.stringify({'from': 'incognito'}, null, 2)], 'metadata.json') + * ]) + * + * // Root CID of the directory + * console.log(cid.toString()) + * + * // Now store the CAR file on NFT.Storage + * await client.storeCar(car) + * ``` + * + * @param {Iterable} files + * @returns {Promise<{ cid: CID, car: CarReader }>} + */ + static async encodeDirectory(files) { + const input = [] + let size = 0 + for (const file of files) { + input.push({ path: file.name, content: file.stream() }) + size += file.size + } + + if (size === 0) { + throw new Error( + 'Total size of files should exceed 0, make sure to provide some content' + ) + } + + return packCar(input, true) + } + // Just a sugar so you don't have to pass around endpoint and token around. /** @@ -305,6 +417,7 @@ class NFTStorage { storeBlob(blob) { return NFTStorage.storeBlob(this, blob) } + /** * Stores files encoded as a single [Content Addressed Archive * (CAR)](https://github.com/ipld/specs/blob/master/block-layer/content-addressable-archives.md). @@ -339,19 +452,13 @@ class NFTStorage { * const cid = await client.storeCar(car) * console.assert(cid === expectedCid) * ``` - * @param {Blob|import('./lib/interface.js').CarReader} car - * @param {object} [options] - * @param {(size: number) => void} [options.onStoredChunk] Callback called - * after each chunk of data has been uploaded. By default, data is split into - * chunks of around 10MB. It is passed the actual chunk size in bytes. - * @param {AnyBlockDecoder[]} [options.decoders] Additional IPLD block - * decoders. Used to interpret the data in the CAR file and split it into - * multiple chunks. Note these are only required if the CAR file was not - * encoded using the default encoders: `dag-pb`, `dag-cbor` and `raw`. + * @param {Blob|CarReader} car + * @param {import('./lib/interface.js').CarStorerOptions} [options] */ storeCar(car, options) { return NFTStorage.storeCar(this, car, options) } + /** * Stores a directory of files and returns a CID for the directory. * @@ -372,6 +479,7 @@ class NFTStorage { storeDirectory(files) { return NFTStorage.storeDirectory(this, files) } + /** * Returns current status of the stored NFT by its CID. Note the NFT must * have previously been stored by this account. @@ -386,6 +494,7 @@ class NFTStorage { status(cid) { return NFTStorage.status(this, cid) } + /** * Removes stored content by its CID from the service. * @@ -402,6 +511,7 @@ class NFTStorage { delete(cid) { return NFTStorage.delete(this, cid) } + /** * Check if a CID of an NFT is being stored by nft.storage. Throws if the NFT * was not found. @@ -416,6 +526,7 @@ class NFTStorage { check(cid) { return NFTStorage.check(this, cid) } + /** * Stores the given token and all resources it references (in the form of a * File or a Blob) along with a metadata JSON as specificed in @@ -454,7 +565,6 @@ class NFTStorage { * * @template {import('./lib/interface.js').TokenInput} T * @param {T} token - * @returns {Promise>} */ store(token) { return NFTStorage.store(this, token) @@ -462,7 +572,8 @@ class NFTStorage { } /** - * @param {import('./lib/interface.js').TokenInput} metadata + * @template {import('./lib/interface.js').TokenInput} T + * @param {T} metadata */ const validateERC1155 = ({ name, description, image, decimals }) => { // Just validate that expected fields are present @@ -491,6 +602,17 @@ For more context please see ERC-721 specification https://eips.ethereum.org/EIPS } } +/** + * @param {Array<{ path: string, content: import('./platform.js').ReadableStream }>} input + * @param {boolean} wrapWithDirectory + */ +const packCar = async (input, wrapWithDirectory) => { + const blockstore = new Blockstore() + const { root: cid } = await pack({ input, blockstore, wrapWithDirectory }) + const car = new BlockstoreCarReader(1, [cid], blockstore) + return { cid, car } +} + /** * @param {Deal[]} deals * @returns {Deal[]} diff --git a/packages/client/src/lib/interface.ts b/packages/client/src/lib/interface.ts index b2998a6711..dc737deb4e 100644 --- a/packages/client/src/lib/interface.ts +++ b/packages/client/src/lib/interface.ts @@ -27,6 +27,41 @@ export interface PublicService { export type CIDString = Tagged export interface API { + /** + * Encodes the given token and all resources it references (in the form of a + * File or a Blob) along with a metadata JSON as specificed in ERC-1155 to a + * CAR file. The `token.image` must be either a `File` or a `Blob` instance, + * which will be stored and the corresponding content address URL will be + * saved in the metadata JSON file under `image` field. + * + * If `token.properties` contains properties with `File` or `Blob` values, + * those also get stored and their URLs will be saved in the metadata JSON + * file in their place. + * + * Note: URLs for `File` objects will retain file names e.g. in case of + * `new File([bytes], 'cat.png', { type: 'image/png' })` will be transformed + * into a URL that looks like `ipfs://bafy...hash/image/cat.png`. For `Blob` + * objects, the URL will not have a file name name or mime type, instead it + * will be transformed into a URL that looks like + * `ipfs://bafy...hash/image/blob`. + */ + encodeNFT( + input: T + ): Promise<{ token: Token; car: CarReader }> + /** + * Encodes a single file to a CAR file and also returns it's root CID. + */ + encodeBlob( + service: Service, + content: Blob | File + ): Promise<{ cid: CID; car: CarReader }> + /** + * Encodes a directory of files to a CAR file and also returns the root CID. + * Provided files **MUST** be within the same directory, otherwise error is + * raised e.g. `foo/bar.png`, `foo/bla/baz.json` is ok but `foo/bar.png`, + * `bla/baz.json` is not. + */ + encodeDirectory(files: Iterable): Promise<{ cid: CID; car: CarReader }> /** * Stores the given token and all resources it references (in the form of a * File or a Blob) along with a metadata JSON as specificed in ERC-1155. The @@ -46,36 +81,21 @@ export interface API { * `ipfs://bafy...hash/image/blob`. */ store(service: Service, token: T): Promise> - /** - * Stores a single file and returns a corresponding CID. + * Stores a single file and returns it's CID. */ storeBlob(service: Service, content: Blob | File): Promise /** - * Stores CAR file and returns a corresponding CID. + * Stores a CAR file and returns it's root CID. */ storeCar( service: Service, content: Blob | CarReader, - options?: { - /** - * Callback called after each chunk of data has been uploaded. By default, - * data is split into chunks of around 10MB. It is passed the actual chunk - * size in bytes. - */ - onStoredChunk?: (size: number) => void - /** - * Additional IPLD block decoders. Used to interpret the data in the CAR - * file and split it into multiple chunks. Note these are only required if - * the CAR file was not encoded using the default encoders: `dag-pb`, - * `dag-cbor` and `raw`. - */ - decoders?: BlockDecoder[] - } + options?: CarStorerOptions ): Promise /** * Stores a directory of files and returns a CID. Provided files **MUST** - * be within a same directory, otherwise error is raised. E.g. `foo/bar.png`, + * be within the same directory, otherwise error is raised e.g. `foo/bar.png`, * `foo/bla/baz.json` is ok but `foo/bar.png`, `bla/baz.json` is not. */ storeDirectory(service: Service, files: Iterable): Promise @@ -85,17 +105,37 @@ export interface API { */ status(service: Service, cid: string): Promise /** - * Removes stored content by its CID from the service. Please note that + * Removes stored content by its CID from this account. Please note that * even if content is removed from the service other nodes that have * replicated it might still continue providing it. */ delete(service: Service, cid: string): Promise /** - * Check if a CID of an NFT is being stored by nft.storage. + * Check if a CID of an NFT is being stored by NFT.Storage. */ check(service: PublicService, cid: string): Promise } +export interface CarStorerOptions { + /** + * Callback called after each chunk of data has been uploaded. By default, + * data is split into chunks of around 10MB. It is passed the actual chunk + * size in bytes. + */ + onStoredChunk?: (size: number) => void + /** + * Maximum times to retry a failed upload. Default: 5 + */ + maxRetries?: number + /** + * Additional IPLD block decoders. Used to interpret the data in the CAR + * file and split it into multiple chunks. Note these are only required if + * the CAR file was not encoded using the default encoders: `dag-pb`, + * `dag-cbor` and `raw`. + */ + decoders?: BlockDecoder[] +} + export interface CheckResult { cid: string pin: { status: PinStatus } diff --git a/packages/client/src/platform.js b/packages/client/src/platform.js index adb58e5bef..12a79f4de3 100644 --- a/packages/client/src/platform.js +++ b/packages/client/src/platform.js @@ -2,6 +2,7 @@ import fetch, { Request, Response, Headers } from '@web-std/fetch' import { FormData } from '@web-std/form-data' import { ReadableStream } from '@web-std/blob' import { File, Blob } from '@web-std/file' +import { FsBlockStore as Blockstore } from 'ipfs-car/blockstore/fs' export { fetch, @@ -12,4 +13,5 @@ export { FormData, File, ReadableStream, + Blockstore, } diff --git a/packages/client/src/platform.ts b/packages/client/src/platform.ts index 27d97850f1..63f4d0a15e 100644 --- a/packages/client/src/platform.ts +++ b/packages/client/src/platform.ts @@ -1,3 +1,5 @@ +import { MemoryBlockStore } from 'ipfs-car/blockstore/memory' + export const fetch = globalThis.fetch export const FormData = globalThis.FormData export const Headers = globalThis.Headers @@ -6,3 +8,4 @@ export const Response = globalThis.Response export const Blob = globalThis.Blob export const File = globalThis.File export const ReadableStream = globalThis.ReadableStream +export const Blockstore = MemoryBlockStore diff --git a/packages/client/src/platform.web.js b/packages/client/src/platform.web.js index 27d97850f1..63f4d0a15e 100644 --- a/packages/client/src/platform.web.js +++ b/packages/client/src/platform.web.js @@ -1,3 +1,5 @@ +import { MemoryBlockStore } from 'ipfs-car/blockstore/memory' + export const fetch = globalThis.fetch export const FormData = globalThis.FormData export const Headers = globalThis.Headers @@ -6,3 +8,4 @@ export const Response = globalThis.Response export const Blob = globalThis.Blob export const File = globalThis.File export const ReadableStream = globalThis.ReadableStream +export const Blockstore = MemoryBlockStore diff --git a/packages/client/src/token.js b/packages/client/src/token.js index 77ae331351..62f80ad491 100644 --- a/packages/client/src/token.js +++ b/packages/client/src/token.js @@ -1,24 +1,36 @@ -import { Blob, FormData } from './platform.js' +import { pack } from 'ipfs-car/pack' +import { CID } from 'multiformats/cid' +import * as Block from 'multiformats/block' +import { sha256 } from 'multiformats/hashes/sha2' +import * as dagCbor from '@ipld/dag-cbor' +import { Blob, FormData, Blockstore } from './platform.js' import { toGatewayURL, GATEWAY } from './gateway.js' +import { BlockstoreCarReader } from './bs-car-reader.js' /** * @typedef {import('./gateway.js').GatewayURLOptions} EmbedOptions * @typedef {import('./lib/interface.js').TokenInput} TokenInput + * @typedef {import('ipfs-car/blockstore').Blockstore} Blockstore */ /** * @template T - * @typedef {import('./lib/interface').Encoded} EncodedBlobUrl + * @typedef {import('./lib/interface.js').Encoded} EncodedBlobUrl */ /** * @template G - * @typedef {import('./lib/interface').Encoded} EncodedBlobBlob + * @typedef {import('./lib/interface.js').Encoded} EncodedBlobBlob */ /** * @template {import('./lib/interface.js').TokenInput} T - * @implements {Token} + * @typedef {import('./lib/interface.js').Token} TokenType + */ + +/** + * @template {TokenInput} T + * @implements {TokenType} */ export class Token { /** @@ -55,6 +67,85 @@ export class Token { static embed({ data }) { return embed(data, { gateway: GATEWAY }) } + + /** + * Takes token input, encodes it as a DAG, wraps it in a CAR and creates a new + * Token instance from it. Where values are discovered `Blob` (or `File`) + * objects in the given input, they are replaced with IPFS URLs (an `ipfs://` + * prefixed CID with an optional path). + * + * @example + * ```js + * const cat = new File(['...'], 'cat.png') + * const kitty = new File(['...'], 'kitty.png') + * const { token, car } = await Token.encode({ + * name: 'hello' + * image: cat + * properties: { + * extra: { + * image: kitty + * } + * } + * }) + * ``` + * + * @template {TokenInput} T + * @param {T} input + * @returns {Promise<{ cid: CID, token: TokenType, car: import('./lib/interface.js').CarReader }>} + */ + static async encode(input) { + const blockstore = new Blockstore() + const [blobs, meta] = mapTokenInputBlobs(input) + /** @type {EncodedBlobUrl} */ + const data = JSON.parse(JSON.stringify(meta)) + /** @type {import('./lib/interface.js').Encoded} */ + const dag = JSON.parse(JSON.stringify(meta)) + + for (const [dotPath, blob] of blobs.entries()) { + /** @type {string|undefined} */ + // @ts-ignore blob may be a File! + const name = blob.name || 'blob' + /** @type {import('./platform.js').ReadableStream} */ + const content = blob.stream() + const { root: cid } = await pack({ + input: [{ path: name, content }], + blockstore, + wrapWithDirectory: true, + }) + + const href = new URL(`ipfs://${cid}/${name}`) + const path = dotPath.split('.') + setIn(data, path, href) + setIn(dag, path, cid) + } + + const { root: metadataJsonCid } = await pack({ + input: [{ path: 'metadata.json', content: JSON.stringify(data) }], + blockstore, + wrapWithDirectory: false, + }) + + const block = await Block.encode({ + value: { + ...dag, + 'metadata.json': metadataJsonCid, + type: 'nft', + }, + codec: dagCbor, + hasher: sha256, + }) + await blockstore.put(block.cid, block.bytes) + + return { + cid: block.cid, + token: new Token( + block.cid.toString(), + `ipfs://${block.cid}/metadata.json`, + data + ), + car: new BlockstoreCarReader(1, [block.cid], blockstore), + } + } } /** @@ -69,7 +160,7 @@ export const embed = (input, options) => /** * @template {TokenInput} T * @param {import('./lib/interface.js').EncodedToken} value - * @param {Set} paths - Paths were to expcet EncodedURLs + * @param {Set} paths - Paths were to expect EncodedURLs * @returns {Token} */ export const decode = ({ ipnft, url, data }, paths) => @@ -148,22 +239,20 @@ const isEncodedURL = (value, assetPaths, path) => * @returns {FormData} */ export const encode = (input) => { - const [form, meta] = mapValueWith( - input, - isBlob, - encodeBlob, - new FormData(), - [] - ) + const [map, meta] = mapValueWith(input, isBlob, encodeBlob, new Map(), []) + const form = new FormData() + for (const [k, v] of map.entries()) { + form.set(k, v) + } form.set('meta', JSON.stringify(meta)) return form } /** - * @param {FormData} data + * @param {Map} data * @param {Blob} blob * @param {PropertyKey[]} path - * @returns {[FormData, void]} + * @returns {[Map, void]} */ const encodeBlob = (data, blob, path) => { data.set(path.join('.'), blob) @@ -176,6 +265,14 @@ const encodeBlob = (data, blob, path) => { */ const isBlob = (value) => value instanceof Blob +/** + * @template {TokenInput} T + * @param {EncodedBlobBlob} input + */ +const mapTokenInputBlobs = (input) => { + return mapValueWith(input, isBlob, encodeBlob, new Map(), []) +} + /** * Substitues values in the given `input` that match `p(value) == true` with * `f(value, context, path)` where `context` is whatever you pass (usually @@ -269,3 +366,30 @@ const mapArrayWith = (input, p, f, init, path) => { /** @type {import('./lib/interface.js').Encoded} */ (output), ] } + +/** + * Sets a given `value` at the given `path` on a passed `object`. + * + * @example + * ```js + * const obj = { a: { b: { c: 1 }}} + * setIn(obj, ['a', 'b', 'c'], 5) + * obj.a.b.c //> 5 + * ``` + * + * @template V + * @param {any} object + * @param {string[]} path + * @param {V} value + */ +const setIn = (object, path, value) => { + const n = path.length - 1 + let target = object + for (let [index, key] of path.entries()) { + if (index === n) { + target[key] = value + } else { + target = target[key] + } + } +} diff --git a/packages/client/test/bs-car-reader.spec.js b/packages/client/test/bs-car-reader.spec.js new file mode 100644 index 0000000000..5b11d30340 --- /dev/null +++ b/packages/client/test/bs-car-reader.spec.js @@ -0,0 +1,80 @@ +import * as assert from 'uvu/assert' +import { MemoryBlockStore } from 'ipfs-car/blockstore/memory' +import { CID } from 'multiformats' +import { BlockstoreCarReader } from '../src/bs-car-reader.js' +import { randomBlock } from './helpers.js' + +describe('Blockstore CAR Reader', () => { + it('should expose CAR version', () => { + const version = 1 + const roots = [ + CID.parse('bafyreib75ot3oyo43f7rhdk6xlv7c4mmjwhbjjnugrw3yqjvarpvtzxkoi'), + ] + const bs = new MemoryBlockStore() + const car = new BlockstoreCarReader(version, roots, bs) + assert.equal(car.version, version) + }) + + it('should determine existence of CID', async () => { + const block = await randomBlock() + const version = 1 + const roots = [block.cid] + const bs = new MemoryBlockStore() + await bs.put(block.cid, block.bytes) + const car = new BlockstoreCarReader(version, roots, bs) + assert.ok(await car.has(block.cid)) + const externalCid = CID.parse( + 'bafyreib75ot3oyo43f7rhdk6xlv7c4mmjwhbjjnugrw3yqjvarpvtzxkoi' + ) + assert.not.ok(await car.has(externalCid)) + }) + + it('should iterate blocks', async () => { + const rootBlock = await randomBlock() + const block = await randomBlock() + const version = 1 + const roots = [rootBlock.cid] + const bs = new MemoryBlockStore() + await bs.put(rootBlock.cid, rootBlock.bytes) + await bs.put(block.cid, block.bytes) + const car = new BlockstoreCarReader(version, roots, bs) + + const blocks = [] + for await (const b of car.blocks()) { + blocks.push(b) + } + + assert.equal(blocks.length, 2) + assert.ok(blocks[0] && blocks[0].cid.equals(rootBlock.cid)) + assert.ok(blocks[1] && blocks[1].cid.equals(block.cid)) + }) + + it('should iterate CIDs', async () => { + const rootBlock = await randomBlock() + const block = await randomBlock() + const version = 1 + const roots = [rootBlock.cid] + const bs = new MemoryBlockStore() + await bs.put(rootBlock.cid, rootBlock.bytes) + await bs.put(block.cid, block.bytes) + const car = new BlockstoreCarReader(version, roots, bs) + + const cids = [] + for await (const cid of car.cids()) { + cids.push(cid) + } + + assert.equal(cids.length, 2) + assert.ok(cids[0] && cids[0].equals(rootBlock.cid)) + assert.ok(cids[1] && cids[1].equals(block.cid)) + }) + + it('should expose blockstore', async () => { + const block = await randomBlock() + const version = 1 + const roots = [block.cid] + const bs = new MemoryBlockStore() + const car = new BlockstoreCarReader(version, roots, bs) + assert.ok(bs === car.blockstore) + }) +}) diff --git a/packages/client/test/helpers.js b/packages/client/test/helpers.js new file mode 100644 index 0000000000..d123325ccb --- /dev/null +++ b/packages/client/test/helpers.js @@ -0,0 +1,47 @@ +import { CID } from 'multiformats' +import { CarWriter } from '@ipld/car' +import * as dagCbor from '@ipld/dag-cbor' +import { garbage } from 'ipld-garbage' +import { sha256 } from 'multiformats/hashes/sha2' + +const MAX_BLOCK_SIZE = 1024 * 1024 * 4 + +function randomBlockSize() { + const max = MAX_BLOCK_SIZE + const min = max / 2 + return Math.random() * (max - min) + min +} + +export async function randomBlock() { + const bytes = dagCbor.encode( + garbage(randomBlockSize(), { weights: { CID: 0 } }) + ) + const hash = await sha256.digest(bytes) + const cid = CID.create(1, dagCbor.code, hash) + return { cid, bytes } +} + +/** + * @param {number} targetSize + * @returns {Promise>} + */ +export async function randomCar(targetSize) { + const blocks = [] + let size = 0 + const seen = new Set() + while (size < targetSize) { + const { cid, bytes } = await randomBlock() + if (seen.has(cid.toString())) continue + seen.add(cid.toString()) + blocks.push({ cid, bytes }) + size += bytes.length + } + const rootBytes = dagCbor.encode(blocks.map((b) => b.cid)) + const rootHash = await sha256.digest(rootBytes) + const rootCid = CID.create(1, dagCbor.code, rootHash) + const { writer, out } = CarWriter.create([rootCid]) + writer.put({ cid: rootCid, bytes: rootBytes }) + blocks.forEach((b) => writer.put(b)) + writer.close() + return out +} diff --git a/packages/client/test/importer.js b/packages/client/test/importer.js index bf25a40539..aac34c4bc9 100644 --- a/packages/client/test/importer.js +++ b/packages/client/test/importer.js @@ -1,86 +1,5 @@ -import pb from 'ipld-dag-pb' -import multicodec from 'multicodec' -import Multihash from 'multihashing-async' -import IPLD from 'ipld' -// @ts-ignore -import InMemory from 'ipld-in-memory' -import { importer } from 'ipfs-unixfs-importer' import { CarReader } from '@ipld/car' -const DagPB = pb.util - -/** @type {(T:typeof IPLD) => IPLD} */ -const inMemory = InMemory -const { multihash } = Multihash - -/** - * @typedef {import('ipfs-unixfs-importer').Blockstore} BlockAPI - * @implements {BlockAPI} - */ -// @ts-expect-error - must implement has, delete, putMany, getMany, ... methods. -class Block { - /** - * @param {Object} [options] - * @param {IPLD} [options.ipld] - * @param {typeof multihash} [options.mh] - */ - constructor({ ipld = inMemory(IPLD), mh = multihash } = {}) { - this.ipld = ipld - this.mh = mh - } - open() { - return Promise.resolve() - } - close() { - return Promise.resolve() - } - - /** - * @param {import('multiformats').CID} cid - * @param {Uint8Array} bytes - */ - async put(cid, bytes) { - const multihash = this.mh.decode(cid.bytes) - const node = DagPB.deserialize(bytes) - - await this.ipld.put(node, multicodec.DAG_PB, { - cidVersion: cid.version, - hashAlg: multihash.code, - }) - - // return { cid, data: bytes } - } - /** - * @param {import('multiformats').CID} cid - * @param {any} options - */ - async get(cid, options) { - // @ts-expect-error - CID is incompatible - const node = await this.ipld.get(cid, options) - if (node instanceof Uint8Array) { - return node - } else { - return DagPB.serialize(node) - } - } -} - -/** - * @param {Uint8Array} content - */ -export const importBlob = async (content) => { - // @ts-expect-error - 'Block' instance is not a valid 'Blockstore' - const results = importer([{ content }], new Block(), { - onlyHash: true, - cidVersion: 1, - rawLeaves: true, - }) - for await (const result of results) { - return result - } - throw new Error(`Import failed`) -} - /** * @param {Uint8Array} content */ @@ -90,35 +9,12 @@ export const importCar = async (content) => { if (!cid) { throw new Error(`Import failed`) } - return { cid } -} - -/** - * @param {File[]} files - */ -export const importDirectory = async (files) => { - const entries = files.map((file) => ({ - path: file.webkitRelativePath || file.name, - // file.stream() isn't typed as AsyncIterable. - content: /** @type {AsyncIterable} */ (file.stream()), - })) - - // @ts-expect-error - 'Block' instance is not a valid 'Blockstore' - const results = importer(entries, new Block(), { - onlyHash: true, - wrapWithDirectory: true, - rawLeaves: true, - cidVersion: 1, - }) - - let last = null - for await (const result of results) { - last = result + const rootBlock = await car.get(cid) + if (!rootBlock) { + throw new Error('missing root block') } - - if (last != null) { - return last - } else { - throw new Error(`Import failed`) + if (new TextDecoder().decode(rootBlock.bytes) === 'throw an error') { + throw new Error('throwing an error for tests') } + return { cid } } diff --git a/packages/client/test/lib.spec.js b/packages/client/test/lib.spec.js index 038d1e53a6..fb565b8e89 100644 --- a/packages/client/test/lib.spec.js +++ b/packages/client/test/lib.spec.js @@ -1,14 +1,14 @@ import { CarReader } from '@ipld/car' import * as assert from 'uvu/assert' import { NFTStorage, Blob, File, Token } from 'nft.storage' -import { CID } from 'multiformats' +import { CID } from 'multiformats/cid' +import { sha256 } from 'multiformats/hashes/sha2' +import * as raw from 'multiformats/codecs/raw' +import { encode } from 'multiformats/block' import { pack } from 'ipfs-car/pack' import { CarWriter } from '@ipld/car' -import * as dagCbor from '@ipld/dag-cbor' import * as dagJson from '@ipld/dag-json' -import { garbage } from 'ipld-garbage' -import { encode } from 'multiformats/block' -import { sha256 } from 'multiformats/hashes/sha2' +import { randomCar } from './helpers.js' const DWEB_LINK = 'dweb.link' @@ -35,7 +35,6 @@ describe('client', () => { it('upload blob', async () => { const client = new NFTStorage({ token, endpoint }) const cid = await client.storeBlob(new Blob(['hello world'])) - console.log(cid.toString()) assert.equal( cid, 'bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e' @@ -61,7 +60,7 @@ describe('client', () => { try { await client.storeBlob(blob) - assert.unreachable('sholud have failed') + assert.unreachable('should have failed') } catch (err) { const error = /** @type {Error} */ (err) assert.ok(error instanceof Error) @@ -174,6 +173,23 @@ describe('client', () => { const cid = await client.storeCar(reader, { decoders: [dagJson] }) assert.equal(cid, block.cid.toString(), 'returned cid matches the CAR') }) + + it('handles server error', async () => { + const client = new NFTStorage({ token, endpoint }) + const bytes = new TextEncoder().encode('throw an error') + const hash = await sha256.digest(bytes) + const cid = CID.create(1, raw.code, hash) + const carReader = new CarReader(1, [cid], [{ cid, bytes }]) + + try { + await client.storeCar(carReader, { maxRetries: 0 }) + assert.unreachable('should have thrown') + } catch (err) { + const error = /** @type {Error} */ (err) + assert.ok(error instanceof Error) + assert.is(error.message, 'throwing an error for tests') + } + }) }) describe('upload dir', () => { @@ -240,6 +256,7 @@ describe('client', () => { assert.match(error.message, /provide some content/i) } }) + it('errors without token', async () => { // @ts-expect-error - expects token option const client = new NFTStorage({ endpoint }) @@ -254,10 +271,9 @@ describe('client', () => { it('errors with invalid token', async () => { const client = new NFTStorage({ token: 'wrong', endpoint }) - try { await client.storeDirectory([new File(['wrong token'], 'foo.txt')]) - assert.unreachable('sholud have failed') + assert.unreachable('should have failed') } catch (err) { const error = /** @type {Error} */ (err) assert.ok(error instanceof Error) @@ -288,7 +304,7 @@ describe('client', () => { try { // @ts-expect-error await client.store({ name: 'name' }) - assert.unreachable('sholud have failed') + assert.unreachable('should have failed') } catch (err) { const error = /** @type {Error} */ (err) assert.ok(error instanceof TypeError) @@ -304,7 +320,7 @@ describe('client', () => { try { // @ts-expect-error await client.store({ name: 'name', description: 'stuff' }) - assert.unreachable('sholud have failed') + assert.unreachable('should have failed') } catch (err) { const error = /** @type {Error} */ (err) assert.ok(error instanceof TypeError) @@ -708,40 +724,3 @@ describe('client', () => { }) }) }) - -const MAX_BLOCK_SIZE = 1024 * 1024 * 4 - -function randomBlockSize() { - const max = MAX_BLOCK_SIZE - const min = max / 2 - return Math.random() * (max - min) + min -} - -/** - * @param {number} targetSize - * @returns {Promise>} - */ -async function randomCar(targetSize) { - const blocks = [] - let size = 0 - const seen = new Set() - while (size < targetSize) { - const bytes = dagCbor.encode( - garbage(randomBlockSize(), { weights: { CID: 0 } }) - ) - const hash = await sha256.digest(bytes) - const cid = CID.create(1, dagCbor.code, hash) - if (seen.has(cid.toString())) continue - seen.add(cid.toString()) - blocks.push({ cid, bytes }) - size += bytes.length - } - const rootBytes = dagCbor.encode(blocks.map((b) => b.cid)) - const rootHash = await sha256.digest(rootBytes) - const rootCid = CID.create(1, dagCbor.code, rootHash) - const { writer, out } = CarWriter.create([rootCid]) - writer.put({ cid: rootCid, bytes: rootBytes }) - blocks.forEach((b) => writer.put(b)) - writer.close() - return out -} diff --git a/packages/client/test/service.js b/packages/client/test/service.js index ffdd334afb..c5e4a59d05 100644 --- a/packages/client/test/service.js +++ b/packages/client/test/service.js @@ -1,35 +1,6 @@ import { CID } from 'multiformats' -import { sha256 } from 'multiformats/hashes/sha2' -import { importCar, importBlob, importDirectory } from './importer.js' +import { importCar } from './importer.js' import { Response, Request } from './mock-server.js' -import * as CBOR from '@ipld/dag-cbor' - -/** - * Sets a given `value` at the given `path` on a passed `object`. - * - * @example - * ```js - * const obj = { a: { b: { c: 1 }}} - * setIn(obj, ['a', 'b', 'c'], 5) - * obj.a.b.c //> 5 - * ``` - * - * @template V - * @param {any} object - * @param {string[]} path - * @param {V} value - */ -export const setIn = (object, path, value) => { - const n = path.length - 1 - let target = object - for (let [index, key] of path.entries()) { - if (index === n) { - target[key] = value - } else { - target = target[key] - } - } -} /** * @param {Request} request @@ -49,78 +20,11 @@ const headers = ({ headers }) => ({ */ const importUpload = async (request) => { const contentType = request.headers.get('content-type') || '' - if (contentType.includes('multipart/form-data')) { - const data = await request.formData() - const files = /** @type {File[]} */ (data.getAll('file')) - if (files.length === 0) { - throw Error('No files were provided') - } - return await importDirectory(files) - } else if (contentType.includes('application/car')) { - const content = await request.arrayBuffer() - return await importCar(new Uint8Array(content)) - } else { - const content = await request.arrayBuffer() - return await importBlob(new Uint8Array(content)) - } -} - -/** - * @param {File} file - * @returns {Promise} - */ -const importAsset = async (file) => { - const { cid } = await importDirectory([file]) - return CID.parse(cid.toString()) -} - -/** - * @param {Request} request - */ -const importToken = async (request) => { - const contentType = request.headers.get('content-type') || '' - if (contentType.includes('multipart/form-data')) { - const form = await request.formData() - - const data = JSON.parse(/** @type {string} */ (form.get('meta'))) - const dag = JSON.parse(JSON.stringify(data)) - - for (const [name, content] of form.entries()) { - if (name !== 'meta') { - const file = /** @type {File} */ (content) - const cid = await importAsset(file) - const href = `ipfs://${cid}/${file.name}` - const path = name.split('.') - setIn(data, path, href) - setIn(dag, path, cid) - } - } - - const metadata = await importBlob( - new TextEncoder().encode(JSON.stringify(data)) - ) - - const bytes = CBOR.encode({ - ...dag, - 'metadata.json': metadata.cid, - type: 'nft', - }) - const hash = await sha256.digest(bytes) - const ipnft = CID.create(1, CBOR.code, hash) - - const result = { - ok: true, - value: { - ipnft: ipnft.toString(), - url: `ipfs://${ipnft}/metadata.json`, - data, - }, - } - - return result - } else { - throw Error('/store expects multipart/form-data') + if (!contentType.includes('application/car')) { + throw new Error(`unexpected content type: ${contentType}`) } + const content = await request.arrayBuffer() + return await importCar(new Uint8Array(content)) } /** @@ -186,14 +90,6 @@ export const handle = async (request, { store, AUTH_TOKEN }) => { headers: headers(request), }) } - case 'POST /store/': - case 'POST /store': { - authorize() - const result = await importToken(request) - return new Response(JSON.stringify(result), { - headers: headers(request), - }) - } case `GET /check/${pathParts[1]}/`: case `GET /check/${pathParts[1]}`: { const cid = CID.parse(pathParts[1] || '') @@ -261,7 +157,7 @@ export const handle = async (request, { store, AUTH_TOKEN }) => { return new Response( JSON.stringify({ ok: false, - error: { message: error.message }, + error: { message: error.message || 'failed to handle request' }, }), { status: error.status || 500, diff --git a/packages/client/test/token.spec.js b/packages/client/test/token.spec.js new file mode 100644 index 0000000000..fcbc9d4905 --- /dev/null +++ b/packages/client/test/token.spec.js @@ -0,0 +1,61 @@ +import * as assert from 'uvu/assert' +import { Blob, FormData } from 'nft.storage' +import * as Token from '../src/token.js' + +describe('token', () => { + it('should encode to FormData from token input object', () => { + const input = { + name: 'name', + description: 'stuff', + } + const inputWithImage = { + ...input, + image: new Blob(['fake image'], { type: 'image/png' }), + } + const form = Token.encode(inputWithImage) + assert.ok(form instanceof FormData) + assert.ok(form.has('image')) + assert.equal(form.get('meta'), JSON.stringify(input)) + }) + + it('should decode from /store repsonse object', () => { + const token = Token.decode( + { + ipnft: 'bafyreib75ot3oyo43f7rhdk6xlv7c4mmjwhbjjnugrw3yqjvarpvtzxkoi', + url: 'ipfs://bafyreib75ot3oyo43f7rhdk6xlv7c4mmjwhbjjnugrw3yqjvarpvtzxkoi', + data: { + name: 'name', + description: 'stuff', + image: + 'ipfs://bafybeierifjwnazodizfrpyfrnr6qept7dlppv6fjas24w2wcri2osmrre/blob', + }, + }, + new Set(['image']) + ) + assert.equal( + token.ipnft, + 'bafyreib75ot3oyo43f7rhdk6xlv7c4mmjwhbjjnugrw3yqjvarpvtzxkoi' + ) + assert.equal( + token.data.image.toString(), + 'ipfs://bafybeierifjwnazodizfrpyfrnr6qept7dlppv6fjas24w2wcri2osmrre/blob' + ) + }) + + it('should create a new token from token input object', async () => { + const input = { + name: 'name', + description: 'stuff', + image: new Blob(['fake image'], { type: 'image/png' }), + } + const { token } = await Token.Token.encode(input) + assert.equal( + token.ipnft, + 'bafyreib75ot3oyo43f7rhdk6xlv7c4mmjwhbjjnugrw3yqjvarpvtzxkoi' + ) + assert.equal( + token.data.image.toString(), + 'ipfs://bafybeierifjwnazodizfrpyfrnr6qept7dlppv6fjas24w2wcri2osmrre/blob' + ) + }) +}) diff --git a/yarn.lock b/yarn.lock index bbb4c71901..a93d0d6697 100644 --- a/yarn.lock +++ b/yarn.lock @@ -518,9 +518,9 @@ "@magic-sdk/types" "^5.2.0" "@magic-sdk/admin@^1.3.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@magic-sdk/admin/-/admin-1.3.1.tgz#7cad0d9a1f9db8224cbcbc5a77526941d2b1a83f" - integrity sha512-g1B8MHSBpJoDfFL/y7gd1YtgrwYbS7m7iRijOgiXrkMnIoid0bHgUsRS9LQKy6mImH/JleJSZcaofm2mIvs6tQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/@magic-sdk/admin/-/admin-1.3.2.tgz#ad66b691ca97a3693b278ef17aace37bd0fa392a" + integrity sha512-fHHzui+3ra3vr1uOYlBZPyANMBobXK22RatAQkCSXzs2Add3Wqga71f/l3ooVr1H1xjGizzBfnDkHahKDqcmPQ== dependencies: eth-sig-util "2.1.2" ethereumjs-util "^6.2.0" @@ -729,17 +729,17 @@ dependencies: any-observable "^0.3.0" -"@sentry/browser@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.14.3.tgz#4e3b67a48b12a70c381cab326d053ee5dfc087d6" - integrity sha512-qp4K+XNYNWQxO1U6gvf6VgOMmI0JKCsvx1pKu7X4ZK7sGHmMgfwj7lukpxsqXZvDop8RxUI8/1KJ0azUsHlpAQ== +"@sentry/browser@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.15.0.tgz#7a1d316dd31cedee446e359a21774bf93d1e553d" + integrity sha512-ZiqfHK5DMVgDsgMTuSwxilWIqEnZzy4yuJ9Sr6Iap1yZddPSiKHYjbBieSHn57UsWHViRB3ojbwu44LfvXKJdQ== dependencies: - "@sentry/core" "6.14.3" - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/core" "6.15.0" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" tslib "^1.9.3" -"@sentry/cli@^1.68.0", "@sentry/cli@^1.70.1", "@sentry/cli@^1.71.0": +"@sentry/cli@^1.70.1", "@sentry/cli@^1.71.0": version "1.71.0" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.71.0.tgz#1e33e05d7651b68f501764ab24dce3d5932b195d" integrity sha512-Z8TzH7PkiRfjWSzjXOfPWWp6wxjr+n39Jdrt26OcInVQZM1sx/gZULrDiQZ1L2dy9Fe9AR4SF4nt2/7h2GmLQQ== @@ -762,15 +762,15 @@ "@sentry/utils" "6.11.0" tslib "^1.9.3" -"@sentry/core@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.14.3.tgz#42d255c1a8838e8f9d122b823ba5ff5c27803537" - integrity sha512-3yHmYZzkXlOqPi/CGlNhb2RzXFvYAryBhrMJV26KJ9ULJF8r4OJ7TcWlupDooGk6Knmq8GQML58OApUvYi8IKg== +"@sentry/core@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.15.0.tgz#5e877042fe18452f2273247126b32e139d5f907c" + integrity sha512-mCbKyqvD1G3Re6gv6N8tRkBz84gvVWDfLtC6d1WBArIopzter6ktEbvq0cMT6EOvGI2OLXuJ6mtHA93/Q0gGpw== dependencies: - "@sentry/hub" "6.14.3" - "@sentry/minimal" "6.14.3" - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/hub" "6.15.0" + "@sentry/minimal" "6.15.0" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" tslib "^1.9.3" "@sentry/hub@6.11.0": @@ -782,22 +782,22 @@ "@sentry/utils" "6.11.0" tslib "^1.9.3" -"@sentry/hub@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.14.3.tgz#f6e84e561a4aff1a4447927356fea541465364c1" - integrity sha512-ZRWLHcAcv4oZAbpSwvCkXlaa1rVFDxcb9lxo5/5v5n6qJq2IG5Z+bXuT2DZlIHQmuCuqRnFSwuBjmBXY7OTHaw== +"@sentry/hub@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.15.0.tgz#fb8a91d12fdd2726a884374ea7242f6bbd081d69" + integrity sha512-cUbHPeG6kKpGBaEMgbTWeU03Y1Up5T3urGF+cgtrn80PmPYYSUPvVvWlZQWPb8CJZ1yQ0gySWo5RUTatBFrEHA== dependencies: - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" tslib "^1.9.3" -"@sentry/integrations@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.14.3.tgz#4b32ce1e52d0abff94befe5e52d3ee4327211322" - integrity sha512-TGdf0l6GzPCEUxkIgU3bgG9DfFni3L97r/BPzwhc0izvkZ+O+3UWu6nCVHp1yWEZrskIxLszDhRz2N5D1zldPQ== +"@sentry/integrations@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.15.0.tgz#3c83617c301b5f77a57514ad7812aedc03613a31" + integrity sha512-fSBAipas6zwyYo4U91pyQOnaTcRCfwvH6ZFwu0Fqmkm64nU9rYpbTNiKkwOWbbiXXT6+LEF6RzBpsyhm4QvgmQ== dependencies: - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" localforage "^1.8.1" tslib "^1.9.3" @@ -810,66 +810,66 @@ "@sentry/types" "6.11.0" tslib "^1.9.3" -"@sentry/minimal@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.14.3.tgz#f3a5b062bdc578000689fd0b31abbb994e6b81f3" - integrity sha512-2KNOJuhBpMICoOgdxX56UcO9vGdxCw5mNGYdWvJdKrMwRQr7mC+Fc9lTuTbrYTj6zkfklj2lbdDc3j44Rg787A== +"@sentry/minimal@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.15.0.tgz#fcc083ba901cfe57d25303d0b5fa8cd13e164466" + integrity sha512-7RJIvZsjBa1qFUfMrAzQsWdfZT6Gm4t6ZTYfkpsXPBA35hkzglKbBrhhsUvkxGIhUGw/PiCUqxBUjcmzQP0vfg== dependencies: - "@sentry/hub" "6.14.3" - "@sentry/types" "6.14.3" + "@sentry/hub" "6.15.0" + "@sentry/types" "6.15.0" tslib "^1.9.3" "@sentry/nextjs@^6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-6.14.3.tgz#353139b0b29680aa59379473e5660110e54f4df6" - integrity sha512-h8OHZWDI4EspWVgdaMU8wUzGFQLMiO766RM21i4A2oEjj1weO/zPjM7hSUsF4W0AiTMMRoDcUwUkLJns6PRzWg== - dependencies: - "@sentry/core" "6.14.3" - "@sentry/hub" "6.14.3" - "@sentry/integrations" "6.14.3" - "@sentry/node" "6.14.3" - "@sentry/react" "6.14.3" - "@sentry/tracing" "6.14.3" - "@sentry/utils" "6.14.3" - "@sentry/webpack-plugin" "1.18.1" + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-6.15.0.tgz#e5515f60e1d84b114c445b0ddcecca2993105ec8" + integrity sha512-6QpU9YFW+flO/iOepFBGliaPeUOGaQn4SmKwRnB5P0iuQc+jEm8ZB6ORi4QYqGahrXqN++sOSYNzT/KaectlAA== + dependencies: + "@sentry/core" "6.15.0" + "@sentry/hub" "6.15.0" + "@sentry/integrations" "6.15.0" + "@sentry/node" "6.15.0" + "@sentry/react" "6.15.0" + "@sentry/tracing" "6.15.0" + "@sentry/utils" "6.15.0" + "@sentry/webpack-plugin" "1.18.3" tslib "^1.9.3" -"@sentry/node@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.14.3.tgz#f19f22f6b73242c0dbda204f8da2e72e38067b65" - integrity sha512-b7NjMdqpDOTxV0hiR90jlK52i9cTdAJgGjQykGFyBDf7rTGDohyEYsERgJ5+/VC3Inan/P3m12PctWA/TMwZCw== +"@sentry/node@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.15.0.tgz#d7b911e5667a3459a807a2ae0464558e872504d4" + integrity sha512-V1GeupWi9ClmoMy5eBWdVTv3k+Yx/JpddT4zCzzYY9QfjYtEvQI7R3SWFtlgXuaQQaZNU0WUoE2UgJV2N/vS8g== dependencies: - "@sentry/core" "6.14.3" - "@sentry/hub" "6.14.3" - "@sentry/tracing" "6.14.3" - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/core" "6.15.0" + "@sentry/hub" "6.15.0" + "@sentry/tracing" "6.15.0" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" cookie "^0.4.1" https-proxy-agent "^5.0.0" lru_map "^0.3.3" tslib "^1.9.3" -"@sentry/react@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.14.3.tgz#b0fec4266d851d703fc21e79c1290bd77892d356" - integrity sha512-kHadqr7o2CmqYWByXWNlPZRn30K0HzlkODvML21ztRz4QPZVq/6jvTbFhfdTz6rKa2J/bBgcIE1101Ie5ZErOg== +"@sentry/react@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.15.0.tgz#4a1a3f39f61c03a675b90a114ff79a9163bcbe3b" + integrity sha512-vrrFF/KtPJQ41tmDCWpaR+bN+/TqPwqncsGLfbClE2irY3x3eCJjT2qPstlB7NQ6rTYtScyekbB0fOoNkq9FFg== dependencies: - "@sentry/browser" "6.14.3" - "@sentry/minimal" "6.14.3" - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/browser" "6.15.0" + "@sentry/minimal" "6.15.0" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" hoist-non-react-statics "^3.3.2" tslib "^1.9.3" -"@sentry/tracing@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.14.3.tgz#0223d365ea0c7d3f7c90cb17ea84c4874bc9ef52" - integrity sha512-laFayAxpO/dQL3K3ZcSjtaqJkSf70DH1hHJ8Oiiic0c/xBxh38WSx8yu3TMrbfka5MVIuMNlkq1Gi+SC+moe4w== +"@sentry/tracing@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.15.0.tgz#5a5f08ee6b9cc1189227536fca053cd23488600d" + integrity sha512-V5unvX8qNEfdawX+m2n0jKgmH/YR2ItWZLH+3UevBTptO+xyfvRtpgGXYWUCo3iGvFgWb1C+iIC7LViR9rTvBg== dependencies: - "@sentry/hub" "6.14.3" - "@sentry/minimal" "6.14.3" - "@sentry/types" "6.14.3" - "@sentry/utils" "6.14.3" + "@sentry/hub" "6.15.0" + "@sentry/minimal" "6.15.0" + "@sentry/types" "6.15.0" + "@sentry/utils" "6.15.0" tslib "^1.9.3" "@sentry/types@6.11.0": @@ -877,10 +877,10 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.11.0.tgz#5122685478d32ddacd3a891cbcf550012df85f7c" integrity sha512-gm5H9eZhL6bsIy/h3T+/Fzzz2vINhHhqd92CjHle3w7uXdTdFV98i2pDpErBGNTSNzbntqOMifYEB5ENtZAvcg== -"@sentry/types@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.14.3.tgz#4af799df7ddfa2702a46bffabc3f1b6eb195de23" - integrity sha512-GuyqvjQ/N0hIgAjGD1Rn0aQ8kpLBBsImk+Aoh7YFhnvXRhCNkp9N8BuXTfC/uMdMshcWa1OFik/udyjdQM3EJA== +"@sentry/types@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.15.0.tgz#a2917f8aed91471bdfd6651384ffcd47b95c43ad" + integrity sha512-zBw5gPUsofXUSpS3ZAXqRNedLRBvirl3sqkj2Lez7X2EkKRgn5D8m9fQIrig/X3TsKcXUpijDW5Buk5zeCVzJA== "@sentry/utils@6.11.0": version "6.11.0" @@ -890,22 +890,15 @@ "@sentry/types" "6.11.0" tslib "^1.9.3" -"@sentry/utils@6.14.3": - version "6.14.3" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.14.3.tgz#4ae907054152882fbd376906695ac326934669d1" - integrity sha512-jsCnclEsR2sV9aHMuaLA5gvxSa0xV4Sc6IJCJ81NTTdb/A5fFbteFBbhuISGF9YoFW1pwbpjuTA6+efXwvLwNQ== +"@sentry/utils@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.15.0.tgz#0c247cb092b1796d39c3d16d8e6977b9cdab9ca2" + integrity sha512-gnhKKyFtnNmKWjDizo7VKD0/Vx8cgW1lCusM6WI7jy2jlO3bQA0+Dzgmr4mIReZ74mq4VpOd2Vfrx7ZldW1DMw== dependencies: - "@sentry/types" "6.14.3" + "@sentry/types" "6.15.0" tslib "^1.9.3" -"@sentry/webpack-plugin@1.18.1": - version "1.18.1" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.1.tgz#0fa24297043305057111d85a7154d4b8b24d43a6" - integrity sha512-maEnHC0nxRnVgAz0qvKvhTGy+SxneR8MFjpgNMvh9CyAB6GEM9VQI1hzxTcAd7Qk90qGW8W4eUmB+ZX8nMrM1w== - dependencies: - "@sentry/cli" "^1.68.0" - -"@sentry/webpack-plugin@^1.16.0": +"@sentry/webpack-plugin@1.18.3", "@sentry/webpack-plugin@^1.16.0": version "1.18.3" resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.3.tgz#1cd3401f84f561b4a451dac5f42465ee5102f5d6" integrity sha512-Qk3Jevislc5DZK0X/WwRVcOtO7iatnWARsEgTV/TuXvDN+fUDDpD/2MytAWAbpLaLy3xEB/cXGeLsbv6d1XNkQ== @@ -1078,9 +1071,9 @@ "@types/node" "*" "@types/lodash@^4.14.149": - version "4.14.176" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0" - integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ== + version "4.14.177" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578" + integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw== "@types/long@^4.0.1": version "4.0.1" @@ -1172,9 +1165,9 @@ integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== "@types/react@^17.0.34": - version "17.0.34" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" - integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== + version "17.0.35" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.35.tgz#217164cf830267d56cd1aec09dcf25a541eedd4c" + integrity sha512-r3C8/TJuri/SLZiiwwxQoLAoavaczARfT9up9b4Jr65+ErAUX3MIkU0oMOQnrpfgHme8zIqZLX7O5nnjm5Wayw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -1339,7 +1332,7 @@ rabin-wasm "^0.1.4" uint8arrays "^2.1.5" -"@web-std/blob@^2.1.0", "@web-std/blob@^2.1.1": +"@web-std/blob@^2.1.0", "@web-std/blob@^2.1.1", "@web-std/blob@^2.1.2": version "2.1.3" resolved "https://registry.yarnpkg.com/@web-std/blob/-/blob-2.1.3.tgz#31c11be71579a015dc35f582acb7f6e82c81538f" integrity sha512-K94rkZpa8yDEylkniNmK0aCYpkZe7wWn8GHNpyM+ckBQuRqhRmX0NG9d1b1f4pX3FKdLcfp7vTj6FjfdcjO3rQ== @@ -1363,14 +1356,14 @@ resolved "https://registry.yarnpkg.com/@web-std/file-url/-/file-url-1.0.1.tgz#41209ec581ee7c97b19222b5daf47f2992f6fdd8" integrity sha512-EUwv2YteIegzUWmTDUGo9PVh04qxPx26/4vzy50OhBjsuUldf6h2CpdY27Lrdk4LbpZGjbn7Ryw+amMLRoHUCQ== -"@web-std/file@^1.1.0": +"@web-std/file@^1.1.0", "@web-std/file@^1.1.3": version "1.1.4" resolved "https://registry.yarnpkg.com/@web-std/file/-/file-1.1.4.tgz#4d9f382627fc2399435136e0239c8929b3d3ac74" integrity sha512-oQ/qgKpuJn8DaPl4kfhItD1hflKGwQ27I21Cq0Rf0ENfirxV10ipyiixn392W3z6WsDJ5d6CDLAFoWUCCCu2BQ== dependencies: "@web-std/blob" "^2.1.0" -"@web-std/form-data@^2.1.0": +"@web-std/form-data@^2.1.0", "@web-std/form-data@^2.1.1": version "2.1.2" resolved "https://registry.yarnpkg.com/@web-std/form-data/-/form-data-2.1.2.tgz#8b737d23846ba3d9cfc70c48cc833d53720a468a" integrity sha512-YN8L2xDU3258+9cB4DG2CfC+FvOmEL5cnO/RjB4hsPPffnehbj39SP1UVn9AI3Ep/ERJwT1ec9TS4jTH4xAAPQ== @@ -1476,9 +1469,9 @@ ajv@^6.10.0, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.7.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.7.1.tgz#52be6f1736b076074798124293618f132ad07a7e" - integrity sha512-gPpOObTO1QjbnN1sVMjJcp1TF9nggMfO4MBR5uQl6ZVTOaEPq5i4oq/6R9q2alMMPB3eg53wFv1RuJBLuxf3Hw== + version "8.8.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.0.tgz#c501f10df72914bb77a458919e79fc73e4a2f9ef" + integrity sha512-L+cJ/+pkdICMueKR6wIx3VP2fjIx3yAhuvadUv/osv9yFD7OVZy442xFF+Oeu3ZvmhBGQzoF6mTSt+LUWBmGQg== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2022,12 +2015,12 @@ browserslist@4.16.6: node-releases "^1.1.71" browserslist@^4.17.5: - version "4.17.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d" - integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw== + version "4.18.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" + integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== dependencies: - caniuse-lite "^1.0.30001274" - electron-to-chromium "^1.3.886" + caniuse-lite "^1.0.30001280" + electron-to-chromium "^1.3.896" escalade "^3.1.1" node-releases "^2.0.1" picocolors "^1.0.0" @@ -2131,11 +2124,16 @@ busboy@^0.2.11: dicer "0.2.5" readable-stream "1.1.x" -bytes@3.1.0, bytes@^3.1.0: +bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" + integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== + c8@^7.2.0, c8@^7.7.1: version "7.10.0" resolved "https://registry.yarnpkg.com/c8/-/c8-7.10.0.tgz#c539ebb15d246b03b0c887165982c49293958a73" @@ -2210,11 +2208,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0, camelcase@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + version "6.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" + integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== -caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001274: +caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001280: version "1.0.30001280" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz#066a506046ba4be34cde5f74a08db7a396718fb7" integrity sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA== @@ -2753,9 +2751,9 @@ cssnano-simple@3.0.0: cssnano-preset-simple "^3.0.0" csstype@^3.0.2: - version "3.0.9" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" - integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== + version "3.0.10" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" + integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== d@1, d@^1.0.1: version "1.0.1" @@ -3118,10 +3116,10 @@ electron-fetch@^1.7.2: dependencies: encoding "^0.1.13" -electron-to-chromium@^1.3.723, electron-to-chromium@^1.3.886: - version "1.3.895" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.895.tgz#9b0f8f2e32d8283bbb200156fd5d8dfd775f31ed" - integrity sha512-9Ww3fB8CWctjqHwkOt7DQbMZMpal2x2reod+/lU4b9axO1XJEDUpPMBxs7YnjLhhqpKXIIB5SRYN/B4K0QpvyQ== +electron-to-chromium@^1.3.723, electron-to-chromium@^1.3.896: + version "1.3.899" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.899.tgz#4d7d040e73def3d5f5bd6b8a21049025dce6fce0" + integrity sha512-w16Dtd2zl7VZ4N4Db+FIa7n36sgPGCKjrKvUUmp5ialsikvcQLjcJR9RWnlYNxIyEHLdHaoIZEqKsPxU9MdyBg== elegant-spinner@^1.0.1: version "1.0.1" @@ -3321,87 +3319,172 @@ esbuild-android-arm64@0.13.13: resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.13.tgz#da07b5fb2daf7d83dcd725f7cf58a6758e6e702a" integrity sha512-T02aneWWguJrF082jZworjU6vm8f4UQ+IH2K3HREtlqoY9voiJUwHLRL6khRlsNLzVglqgqb7a3HfGx7hAADCQ== +esbuild-android-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.14.tgz#c85083ece26be3d67e6c720e088968a98409e023" + integrity sha512-Q+Xhfp827r+ma8/DJgpMRUbDZfefsk13oePFEXEIJ4gxFbNv5+vyiYXYuKm43/+++EJXpnaYmEnu4hAKbAWYbA== + esbuild-darwin-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.13.tgz#e94e9fd3b4b5455a2e675cd084a19a71b6904bbf" integrity sha512-wkaiGAsN/09X9kDlkxFfbbIgR78SNjMOfUhoel3CqKBDsi9uZhw7HBNHNxTzYUK8X8LAKFpbODgcRB3b/I8gHA== +esbuild-darwin-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.14.tgz#8e4e237ad847cc54a1d3a5caee26a746b9f0b81f" + integrity sha512-YmOhRns6QBNSjpVdTahi/yZ8dscx9ai7a6OY6z5ACgOuQuaQ2Qk2qgJ0/siZ6LgD0gJFMV8UINFV5oky5TFNQQ== + esbuild-darwin-arm64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.13.tgz#8c320eafbb3ba2c70d8062128c5b71503e342471" integrity sha512-b02/nNKGSV85Gw9pUCI5B48AYjk0vFggDeom0S6QMP/cEDtjSh1WVfoIFNAaLA0MHWfue8KBwoGVsN7rBshs4g== +esbuild-darwin-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.14.tgz#b3b5ebd40b2cb06ee0f6fb342dd4bdcca54ad273" + integrity sha512-Lp00VTli2jqZghSa68fx3fEFCPsO1hK59RMo1PRap5RUjhf55OmaZTZYnCDI0FVlCtt+gBwX5qwFt4lc6tI1xg== + esbuild-freebsd-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.13.tgz#ce0ca5b8c4c274cfebc9326f9b316834bd9dd151" integrity sha512-ALgXYNYDzk9YPVk80A+G4vz2D22Gv4j4y25exDBGgqTcwrVQP8rf/rjwUjHoh9apP76oLbUZTmUmvCMuTI1V9A== +esbuild-freebsd-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.14.tgz#175ecb2fa8141428cf70ea2d5f4c27534bad53e0" + integrity sha512-BKosI3jtvTfnmsCW37B1TyxMUjkRWKqopR0CE9AF2ratdpkxdR24Vpe3gLKNyWiZ7BE96/SO5/YfhbPUzY8wKw== + esbuild-freebsd-arm64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.13.tgz#463da17562fdcfdf03b3b94b28497d8d8dcc8f62" integrity sha512-uFvkCpsZ1yqWQuonw5T1WZ4j59xP/PCvtu6I4pbLejhNo4nwjW6YalqnBvBSORq5/Ifo9S/wsIlVHzkzEwdtlw== +esbuild-freebsd-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.14.tgz#a7d64e41d1fa581f8db7775e5200f18e67d70c4d" + integrity sha512-yd2uh0yf+fWv5114+SYTl4/1oDWtr4nN5Op+PGxAkMqHfYfLjFKpcxwCo/QOS/0NWqPVE8O41IYZlFhbEN2B8Q== + esbuild-linux-32@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.13.tgz#2035793160da2c4be48a929e5bafb14a31789acc" integrity sha512-yxR9BBwEPs9acVEwTrEE2JJNHYVuPQC9YGjRfbNqtyfK/vVBQYuw8JaeRFAvFs3pVJdQD0C2BNP4q9d62SCP4w== +esbuild-linux-32@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.14.tgz#14bdd4f6b6cfd35c65c835894651ba335c2117da" + integrity sha512-a8rOnS1oWSfkkYWXoD2yXNV4BdbDKA7PNVQ1klqkY9SoSApL7io66w5H44mTLsfyw7G6Z2vLlaLI2nz9MMAowA== + esbuild-linux-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.13.tgz#fbe4802a8168c6d339d0749f977b099449b56f22" integrity sha512-kzhjlrlJ+6ESRB/n12WTGll94+y+HFeyoWsOrLo/Si0s0f+Vip4b8vlnG0GSiS6JTsWYAtGHReGczFOaETlKIw== +esbuild-linux-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.14.tgz#7fd56851b2982fdd0cd8447ee9858c2c5711708a" + integrity sha512-yPZSoMs9W2MC3Dw+6kflKt5FfQm6Dicex9dGIr1OlHRsn3Hm7yGMUTctlkW53KknnZdOdcdd5upxvbxqymczVQ== + esbuild-linux-arm64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.13.tgz#f08d98df28d436ed4aad1529615822bb74d4d978" integrity sha512-KMrEfnVbmmJxT3vfTnPv/AiXpBFbbyExH13BsUGy1HZRPFMi5Gev5gk8kJIZCQSRfNR17aqq8sO5Crm2KpZkng== +esbuild-linux-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.14.tgz#a55634d70679ba509adeafd68eebb9fd1ec5af6c" + integrity sha512-Lvo391ln9PzC334e+jJ2S0Rt0cxP47eoH5gFyv/E8HhOnEJTvm7A+RRnMjjHnejELacTTfYgFGQYPjLsi/jObQ== + esbuild-linux-arm@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.13.tgz#6f968c3a98b64e30c80b212384192d0cfcb32e7f" integrity sha512-hXub4pcEds+U1TfvLp1maJ+GHRw7oizvzbGRdUvVDwtITtjq8qpHV5Q5hWNNn6Q+b3b2UxF03JcgnpzCw96nUQ== +esbuild-linux-arm@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.14.tgz#bb96a99677e608b31ff61f37564326d38e846ca2" + integrity sha512-8chZE4pkKRvJ/M/iwsNQ1KqsRg2RyU5eT/x2flNt/f8F2TVrDreR7I0HEeCR50wLla3B1C3wTIOzQBmjuc6uWg== + esbuild-linux-mips64le@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.13.tgz#690c78dc4725efe7d06a1431287966fbf7774c7f" integrity sha512-cJT9O1LYljqnnqlHaS0hdG73t7hHzF3zcN0BPsjvBq+5Ad47VJun+/IG4inPhk8ta0aEDK6LdP+F9299xa483w== +esbuild-linux-mips64le@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.14.tgz#6a55362a8fd1e593dea2ecc41877beed8b8184b9" + integrity sha512-MZhgxbmrWbpY3TOE029O6l5tokG9+Yoj2hW7vdit/d/VnmneqeGrSHADuDL6qXM8L5jaCiaivb4VhsyVCpdAbQ== + esbuild-linux-ppc64le@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.13.tgz#7ec9048502de46754567e734aae7aebd2df6df02" integrity sha512-+rghW8st6/7O6QJqAjVK3eXzKkZqYAw6LgHv7yTMiJ6ASnNvghSeOcIvXFep3W2oaJc35SgSPf21Ugh0o777qQ== +esbuild-linux-ppc64le@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.14.tgz#9e0048587ece0a7f184ab147f20d077098045e7f" + integrity sha512-un7KMwS7fX1Un6BjfSZxTT8L5cV/8Uf4SAhM7WYy2XF8o8TI+uRxxD03svZnRNIPsN2J5cl6qV4n7Iwz+yhhVw== + esbuild-netbsd-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.13.tgz#439bdaefffa03a8fa84324f5d83d636f548a2de3" integrity sha512-A/B7rwmzPdzF8c3mht5TukbnNwY5qMJqes09ou0RSzA5/jm7Jwl/8z853ofujTFOLhkNHUf002EAgokzSgEMpQ== +esbuild-netbsd-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.14.tgz#dcab16a4bbcfa16e2e8535dadc5f64fdc891c63b" + integrity sha512-5ekKx/YbOmmlTeNxBjh38Uh5TGn5C4uyqN17i67k18pS3J+U2hTVD7rCxcFcRS1AjNWumkVL3jWqYXadFwMS0Q== + esbuild-openbsd-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.13.tgz#c9958e5291a00a3090c1ec482d6bcdf2d5b5d107" integrity sha512-szwtuRA4rXKT3BbwoGpsff6G7nGxdKgUbW9LQo6nm0TVCCjDNDC/LXxT994duIW8Tyq04xZzzZSW7x7ttDiw1w== +esbuild-openbsd-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.14.tgz#3c7453b155ebb68dc34d5aec3bd6505337bdda08" + integrity sha512-9bzvwewHjct2Cv5XcVoE1yW5YTW12Sk838EYfA46abgnhxGoFSD1mFcaztp5HHC43AsF+hQxbSFG/RilONARUA== + esbuild-sunos-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.13.tgz#ac9ead8287379cd2f6d00bd38c5997fda9c1179e" integrity sha512-ihyds9O48tVOYF48iaHYUK/boU5zRaLOXFS+OOL3ceD39AyHo46HVmsJLc7A2ez0AxNZCxuhu+P9OxfPfycTYQ== +esbuild-sunos-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.14.tgz#85addf5fef6b5db154a955d4f2e88953359d75ce" + integrity sha512-mjMrZB76M6FmoiTvj/RGWilrioR7gVwtFBRVugr9qLarXMIU1W/pQx+ieEOtflrW61xo8w1fcxyHsVVGRvoQ0w== + esbuild-windows-32@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.13.tgz#a3820fc86631ca594cb7b348514b5cc3f058cfd6" integrity sha512-h2RTYwpG4ldGVJlbmORObmilzL8EECy8BFiF8trWE1ZPHLpECE9//J3Bi+W3eDUuv/TqUbiNpGrq4t/odbayUw== +esbuild-windows-32@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.14.tgz#f77f98f30a5c636c44db2428ecdf9bcbbaedb1a7" + integrity sha512-GZa6mrx2rgfbH/5uHg0Rdw50TuOKbdoKCpEBitzmG5tsXBdce+cOL+iFO5joZc6fDVCLW3Y6tjxmSXRk/v20Hg== + esbuild-windows-64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.13.tgz#1da748441f228d75dff474ddb7d584b81887323c" integrity sha512-oMrgjP4CjONvDHe7IZXHrMk3wX5Lof/IwFEIbwbhgbXGBaN2dke9PkViTiXC3zGJSGpMvATXVplEhlInJ0drHA== +esbuild-windows-64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.14.tgz#bc778674c40d65150d12385e0f23eb3a0badbd0d" + integrity sha512-Lsgqah24bT7ClHjLp/Pj3A9wxjhIAJyWQcrOV4jqXAFikmrp2CspA8IkJgw7HFjx6QrJuhpcKVbCAe/xw0i2yw== + esbuild-windows-arm64@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.13.tgz#06dfa52a6b178a5932a9a6e2fdb240c09e6da30c" integrity sha512-6fsDfTuTvltYB5k+QPah/x7LrI2+OLAJLE3bWLDiZI6E8wXMQU+wLqtEO/U/RvJgVY1loPs5eMpUBpVajczh1A== -esbuild@0.13.13, esbuild@^0.13.13: +esbuild-windows-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.14.tgz#91a8dad35ab2c4dd27cd83860742955b25a354d7" + integrity sha512-KP8FHVlWGhM7nzYtURsGnskXb/cBCPTfj0gOKfjKq2tHtYnhDZywsUG57nk7TKhhK0fL11LcejHG3LRW9RF/9A== + +esbuild@0.13.13: version "0.13.13" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.13.tgz#0b5399c20f219f663c8c1048436fb0f59ab17a41" integrity sha512-Z17A/R6D0b4s3MousytQ/5i7mTCbaF+Ua/yPfoe71vdTv4KBvVAvQ/6ytMngM2DwGJosl8WxaD75NOQl2QF26Q== @@ -3424,6 +3507,29 @@ esbuild@0.13.13, esbuild@^0.13.13: esbuild-windows-64 "0.13.13" esbuild-windows-arm64 "0.13.13" +esbuild@^0.13.13: + version "0.13.14" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.14.tgz#98a3f7f42809abdc2b57c84565d0f713382dc1a5" + integrity sha512-xu4D+1ji9x53ocuomcY+KOrwAnWzhBu/wTEjpdgZ8I1c8i5vboYIeigMdzgY1UowYBKa2vZgVgUB32bu7gkxeg== + optionalDependencies: + esbuild-android-arm64 "0.13.14" + esbuild-darwin-64 "0.13.14" + esbuild-darwin-arm64 "0.13.14" + esbuild-freebsd-64 "0.13.14" + esbuild-freebsd-arm64 "0.13.14" + esbuild-linux-32 "0.13.14" + esbuild-linux-64 "0.13.14" + esbuild-linux-arm "0.13.14" + esbuild-linux-arm64 "0.13.14" + esbuild-linux-mips64le "0.13.14" + esbuild-linux-ppc64le "0.13.14" + esbuild-netbsd-64 "0.13.14" + esbuild-openbsd-64 "0.13.14" + esbuild-sunos-64 "0.13.14" + esbuild-windows-32 "0.13.14" + esbuild-windows-64 "0.13.14" + esbuild-windows-arm64 "0.13.14" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5030,7 +5136,7 @@ ipfs-car@0.4.3: streaming-iterables "^5.0.4" uint8arrays "^2.1.5" -ipfs-car@^0.5.6, ipfs-car@^0.5.8: +ipfs-car@^0.5.8, ipfs-car@^0.5.9: version "0.5.9" resolved "https://registry.yarnpkg.com/ipfs-car/-/ipfs-car-0.5.9.tgz#ffe5e0d7c1e0a14aaa1e193628d9e21d7746d383" integrity sha512-81jKWdkFj1XjP23Qu2ts5K4VOZsLySD4V4UH8fQeb1T6ETf5btdm24mhRRuY7hvM/Vq7AMtDc+bDXocO+WouJQ== @@ -5186,7 +5292,7 @@ ipfs-unixfs-exporter@^7.0.4: multiformats "^9.4.2" uint8arrays "^3.0.0" -ipfs-unixfs-importer@9.0.6, ipfs-unixfs-importer@^9.0.3, ipfs-unixfs-importer@^9.0.4: +ipfs-unixfs-importer@9.0.6, ipfs-unixfs-importer@^9.0.3, ipfs-unixfs-importer@^9.0.4, ipfs-unixfs-importer@^9.0.6: version "9.0.6" resolved "https://registry.yarnpkg.com/ipfs-unixfs-importer/-/ipfs-unixfs-importer-9.0.6.tgz#9d920388e4555f3249136c90a146387e8c88dd8d" integrity sha512-FgzODqg4pvToEMZ88mFkHcU0s25CljmnqX2VX7K/VQDckiZIxhIiUTQRqQg/C7Em4uCzVp8YCxKUvl++w6kvNg== @@ -6908,7 +7014,7 @@ multibase@^4.0.1, multibase@^4.0.2: dependencies: "@multiformats/base-x" "^4.0.1" -multicodec@3.2.1, multicodec@^3.0.1, multicodec@^3.1.0: +multicodec@^3.0.1, multicodec@^3.1.0, multicodec@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-3.2.1.tgz#82de3254a0fb163a107c1aab324f2a91ef51efb2" integrity sha512-+expTPftro8VAW8kfvcuNNNBgb9gPeNYV9dn+z1kJRWF2vih+/S79f2RVeIwmrJBUJ6NT9IUPWnZDQvegEh5pw== @@ -7961,7 +8067,7 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@^1.22.0, prismjs@~1.25.0: +prismjs@^1.25.0, prismjs@~1.25.0: version "1.25.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== @@ -8290,9 +8396,9 @@ react-native-fetch-api@^2.0.0: p-defer "^3.0.0" react-query@^3.13.9: - version "3.32.2" - resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.32.2.tgz#66da144c4b2862f06a679aea3790ccb17f099a4e" - integrity sha512-g72+faJAwvYywXNrAbP+Iq5YJJzp94ADWHZMSJPi9I3DZdRDQ76gYi/YLzrA9fE9VZkoKz+OOH321eCsC2GPLQ== + version "3.32.3" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.32.3.tgz#a15035d0bcd632fffc7044839469e685c2dbcfc9" + integrity sha512-RB/EOSdJkmuypDIARG/mVi2T41WZKz3tUh72HAUu1OA3VoFr2kG2OFZoO3ylMKjUYI4A9bSaD7wtYg4Nwb71gQ== dependencies: "@babel/runtime" "^7.5.5" broadcast-channel "^3.4.1" @@ -8316,14 +8422,14 @@ react-refresh@0.8.3: integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== react-syntax-highlighter@^15.4.4: - version "15.4.4" - resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz#dc9043f19e7bd063ff3ea78986d22a6eaa943b2a" - integrity sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw== + version "15.4.5" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.4.5.tgz#db900d411d32a65c8e90c39cd64555bf463e712e" + integrity sha512-RC90KQTxZ/b7+9iE6s9nmiFLFjWswUcfULi4GwVzdFVKVMQySkJWBuOmJFfjwjMVCo0IUUuJrWebNKyviKpwLQ== dependencies: "@babel/runtime" "^7.3.1" highlight.js "^10.4.1" lowlight "^1.17.0" - prismjs "^1.22.0" + prismjs "^1.25.0" refractor "^3.2.0" react@17.0.2: @@ -8530,9 +8636,9 @@ requires-port@^1.0.0: integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= reselect@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.2.tgz#7bf642992d143d4f3b0f2dca8aa52018808a1d51" - integrity sha512-wg60ebcPOtxcptIUfrr7Jt3h4BR86cCW3R7y4qt65lnNb4yz4QgrXcbSioVsIOYguyz42+XTHIyJ5TEruzkFgQ== + version "4.1.3" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.3.tgz#6307935d4ec661e77ede662be16fc9b39aa8f7ab" + integrity sha512-TVpMknnmdSRNhLPgTDSCQKw32zt1ZIJtEcSxfL/ihtDqShEMUs2X2UY/g96YAVynUXxqLWSXObLGIcqKHQObHw== resolve-alpn@^1.0.0: version "1.2.1" @@ -9079,9 +9185,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.10" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" - integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== split2@^3.1.1: version "3.2.2" @@ -9285,7 +9391,7 @@ string.prototype.padend@^3.0.0: define-properties "^1.1.3" es-abstract "^1.19.1" -string.prototype.trim@^1.2.4: +string.prototype.trim@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" integrity sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg== @@ -9558,9 +9664,9 @@ tachyons@^4.12.0: integrity sha512-2nA2IrYFy3raCM9fxJ2KODRGHVSZNTW3BR0YnlGsLUf1DA3pk3YfWZ/DdfbnZK6zLZS+jUenlUGJsKcA5fUiZg== tape@^5.2.2: - version "5.3.1" - resolved "https://registry.yarnpkg.com/tape/-/tape-5.3.1.tgz#f0b0a0da35973fdb8767238ee94e943edd1ba0d5" - integrity sha512-Mj3h+/dgfI2xct4kTpzqZaRxhhglXcMg//xGTbB0AQisfiOYa6ZBNQIgv46xi1MqbgthuNLSS1SAySDZsb7MMA== + version "5.3.2" + resolved "https://registry.yarnpkg.com/tape/-/tape-5.3.2.tgz#d7e2e68b7b7ea853681743d2760e9fffc7a0adcf" + integrity sha512-F+UjjvUJsEq/D0NwTKtuekTJsN7vpvYT/dvkZuBw7Y+2whC2JIU8syHIlNNkqstmtBu/mVoqhDr1QJGFUl+caA== dependencies: call-bind "^1.0.2" deep-equal "^2.0.5" @@ -9568,7 +9674,7 @@ tape@^5.2.2: dotignore "^0.1.2" for-each "^0.3.3" get-package-type "^0.1.0" - glob "^7.1.7" + glob "^7.2.0" has "^1.0.3" has-dynamic-import "^2.0.0" inherits "^2.0.4" @@ -9579,7 +9685,7 @@ tape@^5.2.2: object.assign "^4.1.2" resolve "^2.0.0-next.3" resumer "^0.0.0" - string.prototype.trim "^1.2.4" + string.prototype.trim "^1.2.5" through "^2.3.8" temp-dir@^2.0.0: